Ramps 1.4 LCD 2004 Controllerboard an einem (Arduino) Mega 2560

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

    • Ramps 1.4 LCD 2004 Controllerboard an einem (Arduino) Mega 2560

      Bevor ich es für mich ganz allein entwickle und später nach und nach die Schwierigkeiten und Details vergesse, möchte ich euch hier daran teilhaben lassen:

      Ich baue derzeit an einem 4-Kanal Li-Ionen Zellen Schnelltester mit anschließendem Ausdruck der Ergebnisse auf einem Thermo-Etikettendrucker (aber darum geht es hier nicht primär. Wen es aber interessiert, der Thermodrucker-thread ist hier Thermo-Etikettendrucker seriell ansteuern).

      Da ich mich noch nicht mit Grafik-LCD Anzeigen beschäftigt habe, suchte ich zunächst nach möglichst großen LCD-Anzeigen, wobei hier vor allem die 4-zeiligen mit je 20 Zeichen in Frage kommen (gibt es eigentlich auch 4-zeilige mit mehr als 20 Zeichen?). Diese werden z.B. bei 3D-Druckern eingesetzt und haben dort meist auch gleich noch einen Drehgeber (rotary encoder), SD-Karten-Leser inkl. Pegelumsetzer, Piezo-Piepser und ein Adapterboard zum gemeinsamen ANschluß aller dieser Komponenten mit dabei. Beispiel bei Ebay Für rund 8€ kann man da wirklich nicht meckern, finde ich. Zumal sich so eine Einheit später auch gut in der Frontplatte eines Gehäuses verbauen läßt. Also gekauft. Aus den online verfügbaren Schaltplänen und Unterlagen zum Hauptboard des Ramps 1.4 kann man sich mehr schlecht als recht die tatsächlichen Verbindungen zu den Portpins des Atmel zusammenreimen. Mehr genutzt haben mir diese, von anderen Usern zusammengestellten (und von mir zT ergänzten) Anschlußbelegungen:
      LCD_2004.png

      Pinout_Ramps1.4_controller_adapter.jpg

      man verzeihe mir meinen "handaufgenommenen" Schaltplan des Controllerboards:

      Aufnahme_LCD_Verbindungen.jpg

      Daraus habe ich dann in Eagle eine Platine entworfen, die ein Anstecken eines (Arduino) Mega 2560 boards von unten an die Hauptplatine und den Anschluß des Adapterboards des "Controllers" von oben auf diese Hauptplatine ermöglicht (bzw.dies so in die vorhandene Platine des Li_ion Zelltesters eingefügt). Wer hier noch nach einem Eagle-footprint der Kontakte von Mega 2560 und LCD/SD Controllerboard sucht, schreibt mich bitte an, ich müßte das dann erst noch einmal "aufarbeiten", bevor ich es zB auf einem Format 80x100 (für die freie Eagle Version) weitergeben könnte.

      Viel Ärger hatte ich mit dem Drehgeber und der damit verbundenen Erstellung eines Menüs. Die auf den boards vorhandenen Drehgeber erzeugen unter Verwendung des Bascom Befehls
      Encoder(pinc.6 , Pinc.4 , Auswahlrunter , Auswahlhoch , 0)
      genau vier Sprünge in der Auswahl. (der Befehl ist hier gleich mit dem zum Mega2560 board passenden Portpins dargestellt.)
      Außerdem prellen die im Drehgeber vorhandenen Taster (Kontakte) ohne Ende,
      IMG_20210110_115449.jpg IMG_20210110_115656.jpg IMG_20210110_115728.jpg


      und auch in die andere Richtung (L->H Flanke)

      IMG_20210110_115357.jpg



      so daß natürlich auch das Menü später ganz wild hin und herspringt.
      Daher habe ich zunächst (üppig dimensionierte) 2x 100nF Kondensatoren (vorher auf +-5% ausgemessen) direkt an die Pins des Encoders gelötet:
      IMG_20210110_121521.jpg


      Damit wurde das Menü-Verhalten bereits etwas besser. (unten im Bild übrigens der Transistortreiber für den Piezo-Buzzer; man sieht leider auch üble Flussmittelrückstände der chinesischen Handlötung. An einigen Lötstellen waren außerdem Brücken untereinander und zur Massefläche vorhanden, so daß ich hier vorsichtshalber erst einmal suspekte Stellen nachlötete)

      Im Oszillogramm sieht das nach dem C-Einbau so aus (H->L):

      IMG_20210110_121937.jpg (kein Wunder, die 100nF werden ja auch direkt durch den Encoder-Kontakt entladen)


      und entsprechend länger dauert L->H, weil sich die 100nF erst am internen Pullup (ca. 70k ?) aufladen müssen:

      IMG_20210110_122216.jpg

      Hier jetzt auch die Zeitauflösung auf 5ms/div geändert; bei 200us oder 500us/div hätte das nicht auf den Schirm gepasst.

      Kurze Pause hier; Geht gleich weiter
    • Blieb noch das Problem mit den vier Impulsen je Rastung (und real auch unterschiedlicher "Empfindlichkeit": in der Hoch-Richtung wurden oft 2-4 Sprünge ausgeführt, in der Runter-Richtung dagegen nur 1-3.
      Den öfter mal zu findenden "Danegger-Code" wollte ich für so etwas einfaches wie einen handbetätigten Dreh-Encoder nicht verwenden und stattdessen unbedingt beim Bascom Befehl 'Encoder' bleiben.
      Ich bin daher zunächst mit Wartezeiten rangegangen:

      BASCOM Source Code

      1. Const Runterstellwartezeit = 20 'ms; die Zeit, die erst mind verstreichen muß, bis eine weitere Encoder-Drehaktion erfolgen kann bzw. eine solche registriert wird
      2. Const Hochstellwartezeit = 25
      3. Const Encoderdurchlaufwartezeit = 1 'ms; die zeit, die nach einer Encoderabfrage mindestens vergehen muß, bis der nächste Durchlauf erfolgen oder das Programm weiterlaufen kann. Verhindert zB, daß mit nur einem (prellenden) Enterdruck gleich zwei aufeinanderfolgende Werte "abgehakt" werden
      4. ---
      5. Config Portc = Input : Portc = 1
      6. ---
      7. Config Portb.7 = Output
      8. Onboardled Alias Portb.7
      9. ...
      10. Enterpressed Alias Pinc.2 'der Taster des rotary encoder (Inkrementalgeber/Drehgeber)
      11. ...
      12. 'wichtig für LCD-Anzeige! (ohne ...=Output ging es nicht!)
      13. Config Porta.1 = Output : Config Porta.3 = Output : Config Porta.5 = Output : Config Porta.7 = Output : Config Porth.0 = Output : Config Porth.1 = Output
      14. Config Lcdpin = Pin , Db4 = Porta.1 , Db5 = Porta.3 , Db6 = Porta.5 , Db7 = Porta.7 , E = Porth.0 , Rs = Porth.1 'in der nä Zeile kommen die zugehörigen Arduino Mega2560 Pinnumerierungen
      15. 'Config Lcdpin = Pin , Db4 = D23 , Db5 = D25 , Db6 = D27 , Db7 = D29 , E = D17 , Rs = D16 'befinden sich alle bis auf Rs und E auf der rechten (Außen)Seite der 2-reihigen Pfostenbuchse. (wenn ohne Ramps-Adpaterstecker)
      16. Config Lcd = 20 * 4 'Chipset = Ks077 'LCD-Initialisierung, eigtl. 8x2
      17. Cls 'ist wichtig als Reset des LCD nach der Definition
      18. ...
      19. Dim Encoderwert As Byte 'interessiert mich nicht; nur für Bascom-Funktion nötig
      20. Dim Start_year As Word : Start_year = 2021
      21. ---
      22. 'Encoder-Taster: (EC,A2(EXP1_2),D35, PortC.2)
      23. Portc.2 = 1
      24. 'Encoder: ENC1(EXP2_5,D33,PortC.4);ENC2(EXP2_3,D31,PortC.6)
      25. Portc.4 = 1 : Portc.6 = 1
      26. ...
      27. Gosub Datumseinstellung
      28. ...
      29. End
      30. Datumseinstellung:
      31. Cls : Lcd "lastdate: " ; Last_year ; "/" ; Last_month ; "/" ; Last_day ; " "
      32. Locate 2 , 1 : Lcd "please <Enter> "
      33. Locate 3 , 1 : Lcd "new date: "
      34. Do
      35. Loop Until Enterpressed = 0 'ist aktiv 0 (ohne Betät durch Pullup stets 1)
      36. Waitms Encoderdurchlaufwartezeit
      37. Do
      38. Loop Until Enterpressed = 1 'Taster muß auch wieder losgelassen sein, bevor es weitergeht!
      39. Decr Start_year 'da die Encoderroutine erfahrungsgemäß IMMER zunächst einen Rechtsdreh erkennt,
      40. 'wird vorab pauschal 1 Jahr abgezogen!
      41. Do
      42. Encoderwert = Encoder(pinc.6 , Pinc.4 , Jahrrunter , Jahrhoch , 0)
      43. ' ^--- 1 means wait for change which blocks programflow
      44. ' ^------------^---------- labels which are called
      45. Waitms Encoderdurchlaufwartezeit
      46. Toggle Onboardled 'OnboardLED blinkt. Neben der anderen roten, die dauerhaft an ist.
      47. Loop Until Enterpressed = 0
      48. Waitms Encoderdurchlaufwartezeit
      49. Do
      50. Loop Until Enterpressed = 1 'Taster muß auch wieder losgelassen sein, bevor es weitergeht!
      51. Waitms 400
      52. ...
      53. Do
      54. Loop Until Enterpressed = 1 'das ist zuletzt immer wichtig, damit nicht aus Versehen Fehleingaben erfolgen
      55. Return
      56. Jahrrunter:
      57. Waitms Runterstellwartezeit
      58. If Start_year >= Year_base Then Decr Start_year 'man kommt nur max ein Jahr unter das Basisjahr runter, das wird dann später auch so gespeichert. Alles ein Jahr unter dem Basisjahr würde auch beim späteren Wiedereinlesen als Fehler erkannt und durch die Basisangaben ersetzt werden.
      59. Locate 3 , 18 : Lcd "<E>" : Locate 3 , 11 : Lcd Start_year
      60. Return
      61. Jahrhoch:
      62. Waitms Hochstellwartezeit
      63. Incr Start_year
      64. Locate 3 , 18 : Lcd "<E>" : Locate 3 , 11 : Lcd Start_year
      65. Return
      Display All


      Das ging so schon recht gut. Bei späteren Menüpunkten mit weniger Einstellparametern nervte allerdings die fehlende "Treffgenauigkeit" bei der Einstellung; man mußte teils immer noch einmal vor/zurück drehen, bis man (hier zB auf das richtige Jahr) kam.
    • Daher dann eine Blockierzeit per Timer programmiert, die weitere "Schaltungen" des Drehgebers in dieser Zeitspanne, egal in/aus welcher Richtung, ignoriert/verhindert. Timer hat der Atmega2560 ja zum Glück en masse; ich habe daher hier den 16-Bit Timer/Counter 3 gewählt. Interrupts mochte ich noch nicht verwenden; es geht auch ohne:

      BASCOM Source Code

      1. Const Wechselblockierzeit = 25000 ' (6250 Ticks = ca. 100ms waren zu wenig!) 'Ticks á 16µs. Diese Zeitspanne wird nach einer durchlaufenen Encoder-Operation kein anderes oder weiteres Ereignis angenommen. Hintergrund: häufig Falscherkennungen der Richtung oder "zuvielzählen pro Rastung" in Menüpunkten mit viel "Zwischendurch-LCD-Schreibzeit'
      2. 'für die Blockierung weiterer Ereignisse wird ein Timer erforderlich, jedoch nicht zwangsläufig ein Interrupt
      3. 'Jeder Tick des benutzten Timer 3 ist 16µs lang. Nach 62 Ticks sind 0,992ms erreicht, nach 6250 Ticks 100ms, nach 25000 Ticks 400ms.
      4. Deflcdchar 1 , 4 , 14 , 21 , 4 , 4 , 4 , 4 , 4 'Hochpfeil
      5. Deflcdchar 2 , 4 , 4 , 4 , 4 , 4 , 21 , 14 , 4 'Runterpfeil
      6. ...
      7. Dim Parameter_use As Byte 'Möglichkeiten: 1) common - Vorgabe, 2) common - von letzter Zelle, 3) individual - Vorgabe, 4) individual von letzter Zelle
      8. ...
      9. Dim Schaltblockiert As Bit 'dieses Bit hier ist richtungsunabhängig; es blockert ALLE weiteren Vorgänge, die zu schnell hinter dem ersten erfolgen
      10. ...
      11. $regfile = "m2560def.dat"
      12. $hwstack = 100
      13. $swstack = 100
      14. $framesize = 120
      15. $crystal = 16000000 'Quarzfrequenz
      16. ...
      17. Const Timer3reload = 62500
      18. Config Timer3 = Timer , Prescale = 256 'das ist ein 16Bit Timer, wie auch der Timer 1. Jeder Tick ist 16µs lang. Nach 62 Ticks sind 0,992ms erreicht.
      19. Load Timer3 , Timer3reload
      20. 'on ovf1 Timer1_isr ' <--das hier müßte wieder rein, wenn irgendwann ein Sekundentakt automatisch per Interrupt erzeugt werden soll
      21. 'enable timer1 ' <--
      22. Start Timer3
      23. 'enable interrupts ' <--
      24. Gosub Datumseinstellung
      25. Gosub Gesammelt_oder_einzeln
      26. End
      27. ...
      28. Gesammelt_oder_einzeln:
      29. Cls : Lcd "will ..."
      30. ...
      31. Do
      32. Loop Until Enterpressed = 1 'mußte hier rein
      33. Parameter_use = 1 'das ist die Vorgabe und bleibt bestehen, wenn man hier sofort und ohne Drehen"Enter" drückt
      34. Do
      35. Encoderwert = Encoder(pinc.6 , Pinc.4 , Parameterwahl_runter , Parameterwahl_hoch , 0)
      36. 'Wartezeiten kamen hier ganz raus durch das Konzept mit der Wechselbloeckierzeit
      37. Toggle Onboardled 'OnboardLED blinkt. Neben der anderen roten, die dauerhaft an ist.
      38. Select Case Parameter_use
      39. Case 1 :
      40. ...
      41. Locate 1 , 1 : Lcd "..."
      42. Locate 2 , 1 : Lcd "..."
      43. Locate 3 , 1 : Lcd "..."
      44. Locate 4 , 1 : Lcd "choose<E1>, change" ; Chr(1) ; Chr(2) 'up und down-Peile
      45. Case 2 :
      46. ...
      47. Case 3 :
      48. ...
      49. Case 4 :
      50. ...
      51. Case 5:
      52. ...
      53. Locate 4 , 1 : Lcd "choose<E5>, change" ; Chr(1) ; Chr(2) 'up und down-Peile
      54. End Select
      55. If Timer3 >= Wechselblockierzeit Then Schaltblockiert = 0 'das muß hier noch rein, damit die auch zeitnah wieder zu Null werden!
      56. Loop Until Enterpressed = 0
      57. Waitms Encoderdurchlaufwartezeit
      58. Do
      59. Loop Until Enterpressed = 1
      60. ...
      61. Return
      62. Parameterwahl_runter:
      63. 'Waitms Runterstellwartezeit_lcd 'beim neuesten Konzept mit der Schaltblockierung sind diese Verzögerungen nicht mehr nötig!
      64. If Schaltblockiert = 1 Then
      65. If Timer3 > Wechselblockierzeit Then Schaltblockiert = 0 'ansonsten aber nichts weiter tun, also insbesondere KEINE Veränderung des "Parameter_use"!
      66. Else
      67. Schaltblockiert = 1
      68. Timer3 = 0 'nullen des Timers3, um dessen Zählerstand ggf später in anderer Routine auswerten zu können
      69. Decr Parameter_use
      70. If Parameter_use < 1 Or Parameter_use > 200 Then Parameter_use = 5
      71. End If
      72. Return
      73. Parameterwahl_hoch:
      74. 'Waitms Hochstellwartezeit_lcd
      75. If Schaltblockiert = 1 Then
      76. If Timer3 > Wechselblockierzeit Then Schaltblockiert = 0
      77. Else
      78. Schaltblockiert = 1
      79. Timer3 = 0
      80. Incr Parameter_use
      81. If Parameter_use > 5 Then Parameter_use = 1
      82. End If
      83. Return
      84. ...
      85. Timer3_isr: 'dieser Timerinterrupt wird zZt noch nicht benutzt (Timer wird ggf "manuell ausgewertet und nachgeladen", bevor er überläuft!
      86. Load Timer3 , Timer3reload
      87. 'Add your ISR code here
      88. Return
      89. 'Timer value explination
      90. ' The timer is a 16Bit timer, it overflows when the timer reaches 65536
      91. ' The AVR is running at 16000000Hz, the prescaler is 256
      92. ' Each tick is 0,016 ms - (1 / CPUSpeed in KHz ) * Prescaler
      93. ' The timer needs 62500 ticks to reach the required time ( 62500 * 0,016 = 1000ms)
      94. ' The start value for the timer must be set to 3036 so that it will overflow at 65536 after 62500 ticks
      95. ' NOTE: The load command does the inversion for you (256-value or 65536-value)
      Display All


      Damit gelang die Einstellung dann sehr schön sauber und angenehm, also so, wie man sich das allgemein wünscht und heutzutage sogar von einem Gerät erwartet. In Punkto Verstellgeschwindigkeit bei eher wenigen Anzeigeänderungen zwischendurch (zB mehrere Jahre/Werte sehr schnell durchlaufen können) ist jedoch meiner Meinung nach der erstgepostete Code mit den Wartezeiten besser. (Ist ja auch ähnlich wie im Bascom-Beispielcode für den Befehl Encode)

      Es würde mich nun natürlich interessieren, wie eure Erfahrungen mit Drehgebern/Encodern sind. Gibt es welche (und bezahlbare), die zB weniger Weiterschaltungen je Rastung erzeugen? Solche wären meiner Meinung nach besser geeignet ...