Emulator für Incrementalgeber

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

    • Darum y, das wird überraschenderweise in der Main nicht verwendet.
      Hier lagen gerade 100µf rum, aber 100n sollten auch gereicht haben?
      Total Durch dreht der mit beiden Ocr1 auf 0 - darum min = 10
      Das Display hält sich erstaunlich gut. Sie mögen so einen Dauerbeschuß eigendlich nicht so gern,
      zumal es vorher in jedem Befehl ein paar isr-Unterbrechungen mitmachen musste. Es hat wohl einen guten Tag :D
    • So sieht das schon fast gut aus leide ubernimmt er es wohl nur jedes zweite mal ;(
      Und irgendwas stört gewaltig. Ein vergessenes Push? Oder falschübernehme wenn die isr während der Berechnung aufgerufen wird ?

      Source Code

      1. $regfile = "m8adef.dat"
      2. $crystal = 8000000
      3. $hwstack = 40
      4. $swstack = 40
      5. $framesize = 40
      6. $lib "YwRobot_Lcd_i2c.lib"
      7. Config Scl = Portc.5
      8. Config Sda = Portc.4
      9. I2cinit
      10. Ddrb = $c7
      11. Config Lcd = 16x2
      12. Const Pcf8574_lcd = &H7C
      13. Dim Lcd_backlight As Byte : Set Lcd_backlight
      14. Initlcd
      15. Waitms 60
      16. Cls
      17. Config Adc = Single , Prescaler = Auto , Reference = Avcc
      18. Dim N As Word , N2 As Word , Nalt As Word
      19. Dim I As Word , T As Word , T2 As Word , A As Byte
      20. Dim T3 As Word , B As Byte
      21. Dim Tx As Word , T2x As Word
      22. Lcd "Drehz.:"
      23. Locate 2 , 1
      24. Lcd "Ocr "
      25. Config Watchdog = 2048
      26. Start Watchdog
      27. On Oc1a Oca_isr Nosave
      28. Enable Oc1a
      29. On Oc1b Ocb_isr Nosave
      30. A = $f2
      31. B = $e2 'Tccr1a
      32. Ocr1b = 15000 : T2 = 15000
      33. Ocr1a = 30000 : T = 30000
      34. Icr1 = 60000
      35. Tccr1a = $f2
      36. Tccr1b = $19
      37. Enable Interrupts
      38. Do
      39. N = Getadc(0) * 6
      40. If N < 10 Then N = 10
      41. Locate 1 , 9
      42. N2 = N / 10
      43. Tx = 24000 / N2
      44. ' If T.0 = 1 Then Incr T 'ungrade Ocr vermeiden??
      45. T2x = Tx / 2
      46. N = 24000 / Tx
      47. N = N * 10
      48. Lcd N
      49. Lcd " Upm "
      50. N2 = N / 10
      51. T = 24000 / N2
      52. T2 = T / 2
      53. I = T * 2
      54. T3 = T + T2
      55. If N <> Nalt Then ' <> Nalt Then
      56. Icr1 = I
      57. Ocr1a = T
      58. ' Ocr1b = T2
      59. Nalt = N
      60. End If
      61. Locate 2 , 5
      62. Lcd T
      63. Lcd " "
      64. Lcd T2
      65. Lcd " "
      66. Reset Watchdog
      67. Loop
      68. Oca_isr:
      69. !push r16
      70. Loadadr B , Y
      71. !sbi tifr,3
      72. !ld r16,y
      73. !out Tccr1a,r16
      74. !ld r16,-y
      75. !out Ocr1bh,r16
      76. !ld r16,-y
      77. !out Ocr1bl,r16
      78. !sbi timsk,3
      79. !pop r16
      80. Return
      81. Ocb_isr:
      82. !push R16
      83. Loadadr A , Y
      84. !ld r16,y
      85. !out Tccr1a,r16
      86. !ld r16,-y
      87. !out Ocr1bh,r16
      88. !ld r16,-y
      89. !out Ocr1bl,r16
      90. !cbi timsk,3
      91. !pop R16
      92. Return
      Display All
    • So jetzt kann ich mal ein Ergebnis liefern.

      Also das AB-Signal in Software zu generieren scheitert, da es zittert (was mich stört) und die Ausgabefrequenz nach oben sehr durch den Controller-Takt begrenzt ist. 100kHz (n=6000 mit Inkrementalgeber mit 1000 Pulsen) wird nicht erreicht.

      Rein per Hardware-PWM, also nur durch interne Hardware, zu generieren scheitert auch, weil die Phasenlage nicht zu kontrollieren ist.

      Mischlösungen, also ein Signal per PWM oder Toggeln zu erzeigen und die 2. Phase per Interrupt ist auch nicht viel besser. Da hier wieder das jittern auszugleichen ist, was wieder bedeutet, dass man die 100kHz nur mit fast 100% Controller-Auslastung erreicht.

      Also alleine der Atmega-Controller schafft es nicht.

      Also dachte ich, das mal mit externer Hardware am Controller zu versuchen.
      Also den Controller nur als AD-Wandler für die gewünschte Frequenz und als Generator für die Grundfrequenz zu verwenden, und an einem Port ein 2-Bit breites Binärzählersignal auszugeben, welches dann mit Logikschaltung in das AB-Signal zu verwandeln ist.

      Also schnell mal eine Werte-Tabelle erstellt, Binärwert als Eingang und die Signale A und B als Ausgang (was eigentlich dem 2-Bit Gey-Code entspricht). Da hat sich schnell herausgestellt, dass das Signal einfach mit einem Flip-Flop und 2 Ex-Or Gatter erstellt werden kann.

      Hier das Schaltbild dazu.
      Bildschirmfoto vom 2021-04-11 08-36-53.png

      Der Controller liefert also mit toggelndem Pin am OC1A die doppelte Frequenz, bezogen auf die gewünschte Inkrementalgeber-Frequenz. Die Anschluss Phase bestimmt, ob ob das eine Signal invertiert wird oder nicht.

      Die mögliche AB-Signal Frequenz liegt jetzt bei weit über 100kHz. Mit einem Incrementalgeber mit 10000 Pulse je Umdrehung wird problemlos ein AB-Signal mit 1MHz ausgegeben.

      Das Signal wird durch 2 Standard TTL-Bausteine erzeugt. Im Test habe ich anstelle der Ex-Or Gatter (weil nicht vorhanden) diese diskret mit 4 Nand aufgebaut. Siehe Wikipedia.
      Die CPU-Auslastung ist hierbei bei praktisch 0%. Lediglich zum AD-Wandeln und das OB1A-Register setzen werden wenige Takte benötigt.

      Es ist also noch genügend Reserve vorhanden, im Falle das jemand erweitern will mit Tasten, Display und serieller Anbindung für Remote-Steuerung etc.

      Im Anhang noch das vollständige Schaltbild als PDF.

      BASCOM Source Code: Incrementalgeber Emulator-Programm

      1. $Regfile = "m168def.dat"
      2. $HWStack = 40
      3. $SWStack = 40
      4. $FrameSize = 40
      5. $Crystal = 16000000 ' Quarz
      6. Config SubMode = New
      7. Const PIN_AIN = 0 ' ADC0 Soll-Drehzahl
      8. ' mögliche Inkrementalgeber
      9. Const I_Geber_100 = 100 ' Geber mit 100 Pulse/Ump
      10. Const I_Geber_500 = 500 ' Geber mit 500 Pulse/Ump
      11. Const I_Geber_1024 = 1024 ' Geber mit 1024 Pulse/Ump
      12. Const I_GEBER_TEST = 10000
      13. Const I_Geber = I_GEBER_1024 ' Geber setzen
      14. Dim F_Out as Long ' Ausgabefrequenz des Gebers
      15. ' ------------------------------------
      16. ' Routinen
      17. ' ------------------------------------
      18. ' Berechnung der Ausgabefrequenz des Gebers
      19. Function getFOut() as Long
      20. Local n as Integer ' Drehzahl
      21. Local f as Single ' Frequenz
      22. ' n-max * Adc 6000 * Adc
      23. ' frq = -------------- = -----------
      24. ' adc-max * 60 1000 * 60
      25. n = GetAdc(PIN_AIN) * 6 ' Soll-Drehzahl einlesen
      26. ' Drehzahl (rpm) in Frequenz umrechnen
      27. f = n ' in Single wandeln
      28. f = f / 60 ' Umrechnung Drehzahl in Frq
      29. ' Berechnung Ausgabe-Frequenz mit gewähltem Inkremental-Geber
      30. f = f * I_GEBER
      31. getFOut = f ' in Long umwandeln
      32. End Function
      33. ' Registerwerte Timer1 berechnen und setzen
      34. Sub setTimer()
      35. Local R_ICR as Long
      36. Local tmp as Word
      37. ' Register berechnen / setzen
      38. R_ICR = _xtal \ F_Out ' ICR1-Registerwert berechnen
      39. R_ICR = R_ICR \ 2 ' Da Pin getoggelt wird, brauche ich doppelte Frequenz
      40. tmp = LowW(R_ICR) ' berechneter Registerwert in Word wandeln
      41. OCR1A = tmp ' Register setzen
      42. End Sub
      43. ' ------------------------------------
      44. ' Initialisierung
      45. ' ------------------------------------
      46. Config ADC = Single , Prescaler = Auto , Reference = AVCC
      47. Config Portb.0 = Input : Set PortB.0 ' PullUp an
      48. Signal_A Alias PortB.1
      49. Signal_B Alias PortB.2
      50. Config Signal_A = Output ' OC1A
      51. Config Signal_B = Output ' OC1B
      52. Config Timer1 = Timer , Compare_A = Toggle , Prescale = 1 , CLEAR_TIMER = 1
      53. Enable Interrupts
      54. ' ------------------------------------
      55. ' Hauptschleife
      56. ' ------------------------------------
      57. Do
      58. F_Out = GetFOut() ' Ausgabe-Frequenz des Gebers berechnen
      59. Call SetTimer() ' Register für Frequenzausgabe setzen
      60. Waitms 100
      61. Loop
      Display All
      Files
      • Geber.pdf

        (20.6 kB, downloaded 15 times, last: )
    • Hi...

      Hab nen schnellen und sehr kurzen Code für 2-bit Graycodeerzeugung mit Bitmanipulation in der ISR geschrieben...

      Die Folge 00 01 11 10 00... an 2 Ausgängen kann einfach erzeugt werden,
      indem man jeweils den letzen Stand der beiden Ausgänge immer wechselnd mit 01 und 10 ver-exodert...
      Der Wechsel zwischen 01 und 10 im Register geht einfach durch fortlaufendes ver-exodern mit 11 .

      00 exor 01 = 01
      01 exor 10 = 11
      11 exor 01 = 10
      10 exor 10 = 00

      ....etc....
      Files
      • Graygen01.bas

        (893 Byte, downloaded 15 times, last: )

      The post was edited 1 time, last by Stemmy ().

    • Das Problem war nicht das Signal selbst zu erzeugen, sondern die hohe Frequenz.
      Incr.Geber mit 3000 Pulse / Ump bei 6000 Ump am Motor.
      Das sind dann 300 kHz Ausgabe-Frequenz.
      Deine ISR muss also 4x so oft aufgerufen werden.

      Welche Frequenz erreichst du denn.
      Weiterhin ist mir aufgefallen, dass du Register vor der leeren Do:Loop belegst und keine Register rettest in der ISR.
      Das ist für deinen Code OK.

      Wenn in der Hauptschleife aber mehr laufen soll als nichts, dann klappts mit deinem Code nicht mehr.
    • Die Register kann man variieren... habs absichtlich ohne (Bascom-)Code in der Loop gemacht, um mit nosave klar zu kommen...
      Soll ja auch nix anderes machen, als schnellen Graycode raushauen... verschiedene Speed-Optionen kann man in der Intialisierung einbauen...

      In der Isr sind es grad mal 4 takte... dazu der isr-Aufruf und der Rücksprung... die Summe bestimmt dann den maximalen Takt, bzw. die minimale Periodenzeit des Timers... mit Timer ist halt die Frequenz besser zu kontrollieren...

      Ginge aber auch ohne Interrupt als asm in der loop... und dann bei PLL-Takt... hab das jetzt nicht ausgerechnet vom Timing her, aber könnte schon machbar sein die 300kHz, wenn der µC nix anderes macht... werde ich mir mal anschauen bei Gelegenheit, was machbar wäre von der Laufzeit her...

      Mir gings eigentlich nur ums maximal schnelle Code-Prinzip mittels Bitmanipulation, dh. ohne Abfragen, Tests und bedingte Sprünge...
      Das Signal ist auf diese Weise auch absolut symetrisch und dürfte auch jitterfrei sein...

      The post was edited 1 time, last by Stemmy ().

    • $regfile = "attiny85.dat "
      $crystal = 8000000
      $hwstack = 48
      $swstack = 48
      $framesize = 48

      Clkpr = 128 '8MHz
      Clkpr = 0
      !nop
      !nop
      osccal=255 'osccal+1 '255 => 888 kHz
      !nop
      !nop
      ddrb = &b011000
      'Const Pullup=&b000111

      !clr r16
      !ldI r17,8 ' wenn hier 16 geladen wird, läuft es andersrum
      !ldi r22,24

      Lab1:
      !eor r16,r17
      '!ori r16,Pullup
      !out portb,r16
      !eor r17,r22
      '!nop
      !rjmp Lab1

      Dieser Code ohne Timer produziert an PB.3 und PB.4 einen 2-bit-Gray-Takt mit 888 kHz... ohne PLL, nur osccal auf 255 gesetzt...
      Mit NOPs in der Schleife und anderen Osccal-Werten kann die Frequenz nach unten empirisch angepasst werden...

      The post was edited 1 time, last by Stemmy ().