Drehgeber mit 30 Rastungen und X Impulse per Timer oder ext INT

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Drehgeber mit 30 Rastungen und X Impulse per Timer oder ext INT

      Ich habe eine Optischen Drehgeber der hat 30 Rastungen und X ? Impulse
      Worauf beziehen sich Impulse? Auf ein 0-1-0 an einen Kanal ?
      Bei meinem müssen alle Wechsel geprüft werden, das bedeutet jede Wechsel egal ob bei A oder B ist eine Rastung
      Das Folgende Programm Funktionier korrekt.
      Wahlweise per Timer oder Externem Interrupt .
      Lässt sich die ISR noch verbessern?
      Bei Timer ISR gehen halt sehr schnelle Drehungen verloren deshalb mag ich Timer nicht so …
      Die extern Interrupt Version braucht halt zwei INTx Eingänge
      Danke für Anregung …
      Ich weiß es gibt schon viele Versionen für Drehgeber ( Encoder , Drehimpulsgeber Inkrementalgeber )
      Aber mit keiner hat meiner zu meiner Zufriedenheit funktioniert.
      Somit ist das Programm ein Mix aus vielem hier im Forum
      Danke an alle die „Ihre Teile“ davon erkennen…..

      Gruß HansHans

      BASCOM Source Code: Drehgeber_R30_Ix_v1.bas

      1. '
      2. ' Drehimpulsgeber , Encoder , Drehgeber
      3. ' jeder Wechsel wird ausgewertet
      4. ' Date:31.12.2020
      5. ' Email: hans-hans@gmx.de
      6. ' Copyright (c) https://bascomforum.de
      7. '
      8. $regfile = "m2560def.dat"
      9. $crystal = 16000000
      10. $hwstack = 80
      11. $swstack = 90
      12. $framesize = 100
      13. $baud = 9600
      14. '*******************************************************************************
      15. ' Per Timer oder INT ?
      16. Const ISR_typ = 2 '1= Timer 2 = ext INT
      17. '**************************************************************************************
      18. 'Drehgeber :
      19. Config PortG.5 = input 'Taster Drehgeber :4
      20. PORTG.5 = 1 'Pullup on
      21. Config PortE.5 = input 'B Drehgeber INT5 :3
      22. Config PortE.4 = input 'A Drehgeber INT4 :2
      23. PORTE.5 = 1 'Pullup on
      24. PORTE.4 = 1 'Pullup on
      25. A Alias PinE.4
      26. B Alias PinE.5
      27. C_taster Alias PinG.5
      28. Dim Zustandswechsel as byte
      29. Dim Drehgeber_Step as Byte
      30. Dim Drehgeber as word
      31. '**************************************************************************************
      32. #if ISR_typ = 1 ' Timer ISR
      33. Const Timer1_Preload = 40536 '3036 =1sec / 40536 =100ms / 45536 =10ms 49536 = 1ms
      34. Config Timer1 = Timer , Prescale =64 '256 = 1Sec /64 =100ms / 8 =10ms / 1=1ms
      35. Ocr1ah = High(Timer1_Preload)
      36. Ocr1al = Low(Timer1_Preload)
      37. Tccr1a = 0
      38. Set Tccr1b.3
      39. On Compare1a Unterbrechungsroutine
      40. Enable Compare1a
      41. Print "Mit Timer ISR"
      42. #ELSEIF ISR_typ = 2 'ext INT ISR
      43. Config Int4 = CHANGE
      44. On Int4 Unterbrechungsroutine
      45. Enable Int4
      46. Config Int5 = CHANGE
      47. On Int5 Unterbrechungsroutine
      48. Enable Int5
      49. Print "Mit ext Int ISR"
      50. #else
      51. Print "Keine Auswahl "
      52. #endif
      53. Enable Interrupts
      54. '**************************************************************************************
      55. Do
      56. Print Drehgeber
      57. wait 1
      58. Loop
      59. '**************************************************************************************
      60. Unterbrechungsroutine:
      61. Zustandswechsel.0 = A
      62. Zustandswechsel.1 = B
      63. If C_taster = 1 then Drehgeber_Step =1 Else Drehgeber_Step =10 'Schnelllauf bei Taste drücken
      64. Select Case Zustandswechsel
      65. Case &H02 : drehgeber = drehgeber + Drehgeber_Step
      66. Case &H23 : drehgeber = drehgeber + Drehgeber_Step '
      67. Case &H31 : drehgeber = drehgeber + Drehgeber_Step
      68. Case &H10 : drehgeber = drehgeber + Drehgeber_Step
      69. Case &H01 : drehgeber = drehgeber - Drehgeber_Step
      70. Case &H13 : drehgeber = drehgeber - Drehgeber_Step
      71. Case &H32 : drehgeber = drehgeber - Drehgeber_Step
      72. Case &H20 : drehgeber = drehgeber - Drehgeber_Step
      73. End Select
      74. Zustandswechsel.4 = Zustandswechsel.0
      75. Zustandswechsel.5 = Zustandswechsel.1
      76. Return
      Display All
    • Hallo HansHans,
      deine Timer Konfiguration ist etwas irreführend.
      Du verwendest eigentlich den CTC Modus, setzt den Timer aber erst "normal" auf um dann noch ein Bit in TCCR1B zu ändern. Das kannst du auch einfacher haben
      Config Timer1=Timer, Prescale = 64, Clear_Timer=1
      Für den CTC Modus ist dann deine Berechnung falsch. Mit Prescale 64 und OCR1A=40536 wird der Timer nur etwa 6mal pro Sekunde aufgerufen, also wesentlich weniger als du wahrscheinlich impulse erzeugst. Da du doppelte Wechsel ignorierst, werden dabei natürlich schnell Impulse verpasst. Vielleicht versuchst du mal OCR1A=2500, was 10ms entspricht.

      Dein Select Case sollte auch einfacher gehen.
      Diff = Zustandswechsel.4 XOR B 'alter A-Wert XOR neuer B-Wert
      If Diff = 0 Then drehgeber + Drehgeber_Step else drehgeber - Drehgeber_Step

      Sollte das gleiche Ergebnis liefern.
    • HansHans wrote:

      Ocr1ah = High(Timer1_Preload)
      Ocr1al = Low(Timer1_Preload)
      In Zeile 40 und 41 konfigurierst du die OC1a-Register.
      Du kannst das High- und Low-Byte auf einmal schreiben mit:

      Compare1A=Timer1_Preload


      HansHans wrote:

      Die extern Interrupt Version braucht halt zwei INTx Eingänge
      Du kannst auch PCINT anstelle von INT nehmen. Dann kannst du jeden beliebigen Pin verwenden.
      In Deinem Fall bietet es sich an, die PCINT-Pins von einem einzigen Port zu nehmen.


      HansHans wrote:

      Bei Timer ISR gehen halt sehr schnelle Drehungen verloren deshalb mag ich Timer nicht so …
      Die extern Interrupt Version braucht halt zwei INTx Eingänge
      Man kann das auch mit nur einem Interrupt (Terminal-A) machen, und der andere (Terminal-B) ist ein normalen Pin.

      Ich habe hier mal eine Routine, die mit 500 Hz per Timer aufgerufen wird, die bei mir gut funktioniert.
      Von der eigentlichen ISR wird ein Call zu dieser Routine gemacht.

      BASCOM Source Code

      1. ' --------------------------------------------------------
      2. ' Indirekte Interrupt-Routine zur Drehencoder-Abfrage
      3. ' Wird von OC2A-Interrupt-Routine aufgerufen (Abtastung 500Hz)
      4. ' --------------------------------------------------------
      5. Sub IsrEncoder()
      6. ' Aktuelle Werte Terminal-A und B lesen
      7. _EncoderState2.0 = pinEncA ' Terminal-A
      8. _EncoderState2.1 = pinEncB ' Terminal-B
      9. ' Wenn aktuelle Werte den vorherigen entsprechen, dann raus
      10. If _EncoderState1 = _EncoderState2 then Goto _IsrEncoderExit
      11. ' Encoder-Phasen prüfen
      12. Select Case _EncoderState1
      13. Case 3
      14. If _EncoderState2 = 1 then
      15. _EncoderState1 = _EncoderState2
      16. Decr _EncoderValue
      17. ElseIf _EncoderState2 = 2 then
      18. _EncoderState1 = _EncoderState2
      19. Incr _EncoderValue
      20. End If
      21. Case 1
      22. If _EncoderState2 = 0 then
      23. _EncoderState1 = _EncoderState2
      24. ElseIf _EncoderState2 = 3 then
      25. _EncoderState1 = _EncoderState2
      26. End If
      27. Case 0
      28. If _EncoderState2 = 1 then
      29. _EncoderState1 = _EncoderState2
      30. Incr _EncoderValue ' Encoder wurde rechts gedreht
      31. ElseIf _EncoderState2 = 2 then
      32. _EncoderState1 = _EncoderState2
      33. Decr _EncoderValue ' Encoder wurde links gedreht
      34. End If
      35. Case 2
      36. If _EncoderState2 = 0 then
      37. _EncoderState1 = _EncoderState2
      38. ElseIf _EncoderState2 = 3 then
      39. _EncoderState1 = _EncoderState2
      40. End If
      41. End Select
      42. _IsrEncoderExit:
      43. End Sub
      Display All
      Wie du siehst, ist die Unteroutine der ISR als Statemachine realisiert.

      Man kann die Routine natürlich auch von einer INT- oder einer PCINT- ISR aus aufrufen.
      Man muss dann nur Zeile 11 entfernen. Die wird benötigt, wenn der Encoder sich nicht seit letzter Abfrage gedreht hat, braucht auch nichts ausgewertet zu werden.
      Bei einem Interrupt ist aber eine Änderung definitiv da.

      Frohes Neues.
    • Mitch64 wrote:

      auch mit nur einem Interrupt (Terminal-A) machen
      Eben nicht da er bei jeder Änderung eine Rastung hat. Somit würde der Schritt übersehen wo sich nur B Ändert. Daher überhaupt der aufwändige Code mit dem Zwischenspeichern des alten Wertes.

      PS Hat Dein Chip langeweile? Milionen Aufrufe für die Katz bis dann doch mal jemand das Ding dreht? :D
    • Franz wrote:

      Du verwendest eigentlich den CTC Modus, setzt den Timer aber erst "normal" auf um dann noch ein Bit in TCCR1B zu ändern. Das kannst du auch einfacher haben
      Config Timer1=Timer, Prescale = 64, Clear_Timer=1
      Für den CTC Modus ist dann deine Berechnung falsch. Mit Prescale 64 und OCR1A=40536 wird der Timer nur etwa 6mal pro Sekunde aufgerufen, also wesentlich weniger als du wahrscheinlich impulse erzeugst. Da du doppelte Wechsel ignorierst, werden dabei natürlich schnell Impulse verpasst. Vielleicht versuchst du mal OCR1A=2500, was 10ms entspricht.
      Danke ..Aber : Das ist und soll jetzt hier nicht das Thema sein, die Konfiguration ist auch nicht von mir sondern aus Irgend einem Buch (ich glaub aus dem ersten was es überhaupt zu Bascom gab )ich kann nochmal suchen wen es wichtig ist .Die Berechnung ist vermutlich jetzt nur hier falsch da hin und her kopiert. Andere Schaltungen laufen so ohne Nennenswerte Abweichung schon Jahre …

      Pluto25 wrote:

      PS Hat Dein Chip langeweile? Milionen Aufrufe für die Katz bis dann doch mal jemand das Ding dreht?

      Das ist genau der Punkt warum ICH Interrupt will, das er nur fragt wen auch einer dreht …
      Da ist es dann auch nicht so schlimm wenn die iSR nicht soooo Optimal ist bis auf den letzten Takt.

      Pluto25 wrote:

      Eben nicht da er bei jeder Änderung eine Rastung hat. Somit würde der Schritt übersehen wo sich nur B Ändert. Daher überhaupt der aufwändige Code mit dem Zwischenspeichern des alten Wertes.
      Genau !… DANKE du hast das Problem genau richtig erkannt


      Mitch64 wrote:

      Wieviele Pulse macht denn der Encoder bei 30 Rastungen?
      Das ist ja die Frage die ich selbst nicht beantworten kann, deshalb:

      HansHans wrote:

      Worauf beziehen sich Impulse? Auf ein 0-1-0 an einen Kanal ?

      Den gab es mal bei Neuholt vor vielen Jahren, für 5 DM ich meine zumindest es waren noch DM.
      Wollte ihn später nochmals kaufen … Habe in nur bei Porsche für ca 75€ gefunden.
      Ich schau dann nochmal nach dem Type.
    • Grayhil 62AY11003

      Drehimpulsgeber_62AY11003.pdf

      Das habe ich selbst gerade erst gefunden und noch nicht gelesen ....

      neuhold-elektronik.at/datenblatt/Encoder.pdf
      --> Seite 4 bzw E4


      EDIT: Meiner hat auch 32 Rastungen ... Sorry

      EDIT:

      HansHans wrote:

      nicht von mir sondern aus Irgend einem Buch (ich glaub aus dem ersten was es überhaupt zu Bascom gab )ich kann nochmal suchen wen es wichtig ist .

      Es ist aus dem Buch:
      „Programmieren der AVR RISC Mikrocontroller mit Bascom_Avr“
      Von Claus Kühnel.
      Ist schon älter evtl. gab es damals „BASCOM Versions Gründe“ dafür und er weiß es heute auch besser …
      Werde es aber auch Übernehmen dein Vorschlag …

      The post was edited 5 times, last by HansHans ().

    • Franz wrote:

      Für den CTC Modus ist dann deine Berechnung falsch. Mit Prescale 64 und OCR1A=40536 wird der Timer nur etwa 6mal pro Sekunde aufgerufen, also wesentlich weniger als du wahrscheinlich impulse erzeugst. Da du doppelte Wechsel ignorierst, werden dabei natürlich schnell Impulse verpasst. Vielleicht versuchst du mal OCR1A=2500, was 10ms entspricht

      Ich habe nochmal nachgerechnet hast natürlich recht …. a_57_04ef5ee8
      Ändert aber nix an der Sache, das es Unnütz viele Aufrufe sind die man machen muss obwohl die meiste Zeit keiner dreht und trotzdem gehen bei 10ms noch Impulse verloren bei schnellem drehen
    • Selbst wenn du die Timer ISR jede ms nur zu diesem Zweck aufrufst, sind das nur etwa 1% der verfügbaren Takte. Wenn dein Controller so hart am Limit ist, dann hast du recht.
      Meistens dreht der aber in der Main seine Runden und wartet dabei, bis etwas zu tun ist. Ist das weniger "unnütz"?
      Wenn man dies über einen Timer macht, dann häufig so, dass man den für alle möglichen Zwecke aufruft und dann nebenbei auch noch die Eingänge des Encoders überprüft. Und du hast ja wahrscheinlich nicht einen M2560 am Start nur um den Encoder abzufragen.
      Der Nachteil mit den Int-Eingängen (egal ob INTx oder PCINTx) ist halt, dass zu viele Interrupts ausgelöst werden, wenn die Kontakte prellen. Wenn das bei deinem Encoder nicht passieren kann, dann sollte die Methode eigentlich unproblematisch sein.