Hallo Leute, nachdem ich lange Zeit stiller Mitleser bin (und weiß wie verhasst LCD-Display Threads sind !) hab ich jetzt ein Problem wo ich einfach nicht mehr weiter weiß. Das angehängte Programm gibt 'Hello World' auf einem LCD-Display aus. Das LCD-Display ist dabei von Gleichmann (GE-C1602B), Controller-Chip müsste ST7066U sein. Er hält sich aber an die Vorgaben des HD44780 und lässt sich so auch ansteuern (über die Arduino LiquidCrystal Library habe ich das getestet). Benutzes Board ist also ein Arduino UNO, Atmel Chip ist AtMega328P. Sollte tatsächlich jemand irgendein Timing oder so überprüfen wollen, das Datenblatt des Displays gibt es hier: http://www.msc-ge.com/download/displays/dabla_allg/ge-c1602b-tmi-ct-r.pdf (Die Timing-Sachen sind aber wohl wahrscheinlich aus dem Datenblatt des Controller Chips 1zu1 übernommen, dieses gibt es hier: http://www.crystalfontz.com/controllers/ST7066U.pdf) Nun aber zu meinem Problem: Es funktioniert. Nur leider nur wenn ich das Programm gerade mit AVRDude über den ISP übertrage. Dann läuft es und das LCD wird richtig initialisiert. Ich kann dann auch resetten (auf dem Arduino ist ein Reset-Knop eingebaut) so oft ich will, es funktioniert stabil und immer wieder. Das initialisieren schlägt aber fehl wenn ich die Stromversorung abhänge und neu anstecke. Egal ob das Board dann nun über USB oder den ISP versorgt wird, es initialisiert sich nichts. Auch nicht wenn ich resette. Ich hab es auch schon mit zwei verschiedenen Displays getestet. Beide mucken (ich habe also beim Löten der Buchsenleiste nichts gebraten, hoffe ich). Ich kann mir nur vorstellen dass die Versorgungsspannung für das LCD noch nicht ausreicht damit es seinen internen Reset durchgeführt hat wenn ich meine Befehle sende. Was nur dagegen spricht: a) Ich warte doch wirklich lang, und habe auch schon Wartezeiten im Sekundenbereich ausprobiert. b) Warum geht es nach dem Reset nicht ? Nur wenn ich davor gerade mit AVRDude das Programm gesendet habe ? c) Das gleiche Verhalten trat auch bei meinen Tests mit der Arduino LiquidCrystal Library auf (bilde ich mir ein, ich kann es jetzt gerade nicht testen) Ich bin mittlerweile ziemlich am verzweifeln und wäre um ein paar Stupser in die richtige Richtung echt dankbar ! Mfg, Thomas
Hi Dürfte an einer zu kleinen Wartezeit vor der Initialisierung des LCDs liegen. Du überfährst das LCD bevor es aus dem Power-Up kommt. MfG Spess
Hallöchen Sorry. Aber den Assemblercode zu analysieren ist mir zuviel aufwand aber : Initialisiert Du die im Programm verwendeten Variablen und Register korrekt ? Beim Programmeinspielen werden normalerweise diese auf den Wert 0 gesetzt, was bei einem PowerUp nicht zuverlässig gemacht wird. bG Mandi
Hi spess53, Ich hab in der Unterroutine 'preinitlcd' die folgende Warteschleife:
1 | // Vor dem ersten Befehl, WARTEN, und zwar ca. 50 Millisekunden, 50000000 Nanosekunden
|
2 | // 50000000 Nanosekunden = 800000 Takte (3 x 326404 Takte => 979212)
|
3 | ldi r16, 0x10 |
4 | call longwait |
Sollte alles stimmen so warte die Funktion '326404 x 0x10' Zyklen, also 5222464 Zyklen, bei 62.5 Nanosekunden pro Zyklus (16 Mhz) sind das immerhin 326404000 Nanosekunden, also beinnahe 0,33 Sekunden. Das müsste doch eigentlich eine Welt für das LCDDisplay sein, vorallem weil das Datenblatt von 40 ms after 4,5 Volt spricht. (Ich hab ja immerhin 326 ms). Aber auch wenn ich den Wert exorbitant in die Höhe schraube (~16 Sekunden) ändert es nix... Sitzt der wait an der evtl. an der falschen Stelle ??? Thomas
@Mandi: hust Ini---was ??? Ich werd mal ein paar Initialisierungen einbauen, wäre toll wenns das wäre... Thomas PS: Der Assemblercode ist ja nur als Referenz da, damit ihr eventuell was nachschauen könnt, ich erwarte nicht dass den jemand komplett studiert :-)
Also auch das initialisieren aller Register führt zu keinem Ergebnis. Variablen nutze ich garkeine (der Atmel hat ja wahrlich genug Register). Die Port-Register habe ich mal auf LOW geschalten vor dem ersten Befehl, mal nicht -> Scheint auch nichts an dem Verhalten zu ändern. Thomas
Hast du mal getestet die Ini des LCD 2x hintereinander auszuführen ? Holger
HI Noch etwas: Du benutzt für die Initialisierung dein UP 'prepcommand', das das Busy-Flag abfragt. Das Flag ist aber erst, je nach Interface erst nach dem zweiten bzw. dritten 'Function Set' verfügbar. Kommentare sollen erläutern, warum man etwas macht. Aber nicht in Romanform. Ramadressen kann man mit Labels versehen:
1 | .dseg ; RAM |
2 | |
3 | xyz: .Byte 1 ; reserviert 1 Byte an der Speicherstelle |
4 | ; mit dem Namen xyz |
5 | |
6 | .cseg ; Programmspeicher |
7 | |
8 | .... |
MfG Spess
@Holger: zweimal, dreimal, viermal... es macht keinen Unterschied -> Nach Übertragen des Programmes geht es, nach abstecken und anstecken geht es nicht. @spess53: Prepcommand setzt die Datenleitungen entsprechend der Befehle - es mag nicht schön aussehen, aber es funktioniert ja (nach dem Übertragen). Das Busy-Flag frage ich nicht ab. Was die Roman-Kommentare betrifft: Es ist halt nunmal eines der anfänglichen Assembler-Programme, da wollte ich noch alles so gut wie möglich kommentieren, sonst bin ich nicht so. Ich glaube ja dass der Programmcode nicht sonderlich schön, kompakt oder schnell ist - das würde ich alles mal verbessern (und auch die Kommentare stehen auf meiner Liste !) wenn er denn stabil funktionieren würde. Ich kann mir eben nicht erklären warum er jedesmal nachdem ich ihn mit AVRDude übertrage funktioniert, und nach dem abstecken nicht mehr. Thomas PS: Danke für den Tipp mit den Labels für die Ramadressen !
Hast du mal überprüft ob nur das LCD nicht will oder das ganze Programm dann nicht tut? Ich hab hier nen Programmer rumliegen den ich jedesmal neu flashen muss wenn ich mal vergessen hab ihn abzustecken und er dann rückwärts versorgt wird.
@snowfly: Das scheint es auch nicht zu sein, anhand des Timings kann ich sehen dass die LED wieder genauso nach X Sekunden angeht, wie bei den Versuchen wo gerade programmiert wurde. Außerdem habe ich ein zweites LCD Display und ein zweites Arduino Board getestet. Thomas
Guten Morgen, hier mal der aktuelle Programmcode. Lasse nun die Register auf 0 initialisieren, außerdem werden die Pins jetzt zuerst auf Output geschalten, und dann nochmal extra mit LOW initialisiert. Noch dazu ist mir aufgefallen dass ein Interrupt-Vektor ja zwei Instruction Words lang ist, deshalb noch die 'nop's vor dem 'reti' in der Interrupt-Vektoren Tabelle. Problem bleibt dabei aber bestehen wie eh und je. Was ist denn anders zwischen: a) Einem ganz neu starten des Atmel über externe Spannungsversorunge, und b) Einem neu starten des Atmel nach Übertragung des Programmcodes. Lustigerweise kann ich nach dem übertragen des Programmcodes den Reset auf LOW halten solange ich will (und während des Resets sogar die Spannungsversorgung des LCDs abstecken) und sobald ich den Reset wieder auf HIGH gebe geht alles. Ich kapiers einfach nicht.... Thomas
Hi
>Ich kapiers einfach nicht....
Und wer soll dein 'Programm' kapieren? Niemand hat Lust an Hand des
Datenblattes zu kontrollieren, ob die IO-Adressen (Ports usw.) auf die
du zugreifst, richtig sind. Zumal noch nicht einmal klar ist, für
welchen Controller das Programm geschrieben ist. Für jeden Controller
gib es eine Include-Datei in der symbolische Adressen vereinbart sind,
mit denen ein Programm wesentlich lesbarer wird.
Du hast auch noch Fehler in deinem Code. Z.B. lädst du mit
lds r16, 0x0B
r16 mit dem Inhalt von r11. Gewollt?
MfG Spess
ThomasC schrieb: > 40 ms after 4,5 Volt spricht. (Ich hab ja immerhin 326 > ms). Und wie ist die Versorgungsspannung am LCD tatsächlich? Wie ist der Verlauf bei Verwendung einer Batterie? Wird die Mindestanstiegsgeschwindigkeit im Datenblatt des LCD erreicht? (Seite 31 ST7066U-Datenblatt) Ist im ATMEGA die Fuse für Brown-Out aktiviert oder ab welcher Spannung läuft der Prozessor bereits los? Ist die Versorgungsspannung im ausgeschalteten Zustand < 1V? Ich hatte mal das Problem daß ich einen FTDI mit TTL-Schnittstelle am ATMEGA dran hatte (über 2*1K Serienwiderstand). Der Strom aus dem FTDI am RXD-Eingang über die Eingangsschutzdiode auf die Versorgung hat gereicht um den Reset des LCD zu verhindern. Gruß Anja
Hi >> Leider können wir unser Register SPH und SPL nicht direkt adressieren >WAS? =-O >Versuch mal out sph, ... Wenn das .include "m328pdef.inc" fehlt, kann man natürlich SPH, SPL... nicht benutzen. MfG Spess
Das ist ja nicht lesbar ...! > Leider können wir unser Register SPH und SPL nicht direkt adressieren WAS? =-O Versuch mal out sph, ... Verhält sich angehängtes File anders? Edit: Update, hatte noch einen kleinen bug inne .. spess53 schrieb: > Zumal noch nicht einmal klar ist, für > welchen Controller das Programm geschrieben ist. Doch, steht oben: ThomasC schrieb: > Benutzes Board ist also ein Arduino UNO, Atmel Chip ist AtMega328P. Gruß Jobst
@Spess: Ich will ja garnicht dass jemand das ganze Programm durchsieht. (wie gesagt, es ist nur der Vollständigkeit halber dabei) Da es nach dem übertragen funktioniert gehe ich davon aus dass es ja formal richtig sein muss. Der Fehler den du entdeckt hast - Hut ab, der ist mir entgangen - ist tatsächlich ein Fehler, er wirkt sich aber nicht aus, da damit allerhöchstens andere Port-Bits verstellt werden, und da ich noch nichts anderes angeschlossen habe ist das wohl egal. Was die Include-Datei und die Benahmung der Ports betrifft, die habe ich absichtlich weggelassen. Ich will den Chip verstehen und deswegen so Hardwarenah wie möglich bleiben. Bei meinen ersten Programmen habe ich die Opcodes und die Sprung-Offsets noch manuell im Hexeditor getippt. Auch das geht :-) @Anja: Als externe Spannungsversorgung nimmt der Arduino die USB-Schnittstelle. Das mit den Fuses und Brown-Out werde ich mal checken, ich habe etwas von 2,7 Volt gelesen. Eigentlich dachte ich aber dass mein 'ich warte mal 4 Sekunden und schicke dann erst Befehle an das LCD-Display' ausreichen sollte - egal was Brown-Out macht, bzw. wieviel Volt ich beim Start des Programmes habe. Dass die Spannung eventuell nicht steil genug ansteigt könnte sein. Eventuell könnte ich testen die Versorgungsspannung für das LCD direkt über einen I/O-Pin anzuschalten. Was die Versorgungsspannung im ausgeschaltenen Zustand betrifft so ziehe ich das USB-Kabel ab. So müsste es definitiv unter 1 Volt sein :-) Übrigens habe ich auch schon mal über ein Labornetzgerät direkt 5 Volt an den Atmel gegeben. Das hat auch nicht geholfen. Vielen Dank, das sind mal ein paar Tipps und Dinge die ich probieren könnte... Thomas
spess53 schrieb: > Wenn das .include "m328pdef.inc" fehlt, kann man natürlich SPH, SPL... > nicht benutzen. Stimmt natürlich ... ;-) Haben wir hier jetzt ein bischen Kuddelmuddel gemacht? :-D Gruß Jobst
Hallo Leute, ich hab den Fehler endlich gefunden. Es ist mir peinlich das zuzugeben, aber es ist ein stinknormaler Programmierfehler. Hier erstmal die Fehler, die beide in der Unterfunktion 'prepcommand' waren: 1. In Zeile 413 lade ich PORTD nach r16, hier habe ich die falsche Adresse genommen, es hätte nicht 0x0B sondern 0x2B sein sollten. Den Fehler hat ja spess53 gefunden. Wow ! 2. In Zeile 415 möchte ich die Bits im PORTD Register auf 0 setzen welche meinen Datenleitungen entsprechen. Leider mach ich aber ein AND mit '00001111' und nicht mit '11000011'. Somit habe ich die falschen Bits geleert. Warum ist mir das nicht aufgefallen ? Da Fehler 2 keine Auswirkungen hat solange Fehler 1 existiert. Wie ich drauf gekommen bin ? Dank der Hilfe hier, dem gefundenen Fehler von spess53 (sollten wir uns jemals im RL treffen so ist dir ein Bier oder gleich ein ganzer Schweinebraten sicher !) und einem Logik Analysator der gezeigt hat dass ein paar Lines auf HIGH bleiben. Und warum bin ich nicht früher drauf gekommen ? Weil ich mich viel zu sehr darauf versteift hatte dass es irgendwas damit zu tun hätte das es nach dem frischen Übertragen geht und später nicht mehr. Jetzt hätte ich aber noch eine kleine Frage die wirklich nicht mehr weh tut... Ich nehme an nach dem übertragen ging alles gut, da das Register r11 immer 0 ist und somit Fehler 2 keine Auswirkungen hat. Wenn man aber über eine externe Stromquelle den Atmel neustartet ist das Register r11 scheinbar nicht mit 0 initialisiert. Korrekt ? Bringt mich jedenfalls zur Frage: Auf was stehen die Register direkt nach dem einschalten ? Grundrauschen ? Unbestimmt ? ... Weiß das jemand ? Jedenfalls ist das ganze sehr ärgerlich. Ich hab das Programm X-mal debuggt und konnte da den Fehler nicht finden weil im AVRStduio Simulator natürlich alle Register mit 0 initialisiert werden. Tut mir also leid den nächsten Idioten-LCD-Thread gestartet zu haben. Ich gelobe Besserung ! Mfg, Thomas
Hi >Bringt mich jedenfalls zur Frage: Auf was stehen die Register direkt >nach dem einschalten ? Grundrauschen ? Unbestimmt ? ... Weiß das jemand >? Der Zustand der Register ist unbestimmt. Haben also, genau wie der RAM, nach dem Einschalten zufällige Werte. MfG Spess
> Was die Include-Datei und die Benahmung der Ports betrifft, > die habe ich absichtlich weggelassen. > Ich will den Chip verstehen und deswegen so Hardwarenah wie > möglich bleiben. Was für ein Unsinn. Dann führ dir wenigstens ein paar Namen für die Bezeichner ein. Und wenn du das getan hast, kommst du drauf, dass das include File genau das gleiche macht. Mit genau demselben Argument könntest du auch die Memnonics weglassen und alles per .db machen. -> es gibt Dinge, die sind in einem realen Projekt nicht sinnvoll! Und es gibt Dinge, die bringen dir keinen Erkentnisgewinn. In der Programmierung geht es nicht darum, ob der CPU Befehl Add jetzt das Bitmuster 010010001 hat oder nicht. Das ist Detailswissen das man sich einmal ansieht wenn man sich dafür interessiert, aber dann gleich wieder vergisst und dem Assembler überlässt solche Dinge zu wissen. Ob du
1 | // Unsere bits (DB7+DB6+DB5+DB4) auf 0 setzen durch ein AND mit 00001111 (0x0F) |
2 | andi r16, 0x0F |
3 | // Und PORTD zurückschreiben |
4 | sts 0x2B, r16 |
schreibst, oder
1 | andi r16, (1<<DB7) | (1<<DB6) | (1<<DB5) | (1<<DB4) |
2 | sts PORTB, r16 |
schenkt sich nichts. Die eine Schreibweise hilft dir beim Verstehen was da passiert nicht mehr oder weniger als die andere. Aber: Die 2.te ist weniger fehleranfällig! Von leichter zu handhaben bei Hardwaränderungen reden wir erst mal gar nicht. Du musst dir eines merken: Wenn du dich genötigt siehst, solche Dinge in einem Kommentar zu verewigen, dann machst du etwas falsch und musst dich fragen: Wie kann ich die Möglichkeiten meines Programmiersystems nutzen, so dass ich genau diesen Kommentar gar nicht brauche, sondern den Inhalt des Kommentars im Code selber ausdrücken kann. Denn: Kommentare auf dieser Ebene sind Schall und Rauch. Die sind öfter falsch als einem lieb ist. Deine Analyse ist falsch > Warum ist mir das nicht aufgefallen ? Weil deine Schreibweisen beschissen sind > Wie ich drauf gekommen bin ? Weil du ein paar 'Dumme' gefunden haben, die die Arbeit übernommen haben, deinen Code auf Dinge zu kontrollieren, die bei vernünftiger SChreibweise gar nicht passiert wären > Und warum bin ich nicht früher drauf gekommen ? Weil du unsinnigerweise auf bescheuerter SChreibweise bestehst Und der wichtigste Punkt in deiner Analyse fehlt: **** Was hab ich für die Zukunft daraus gelernt? **** --> Das Schreibweise und die Möglichkeiten des Assemblers nicht einfach nur optische Gimmicks sind, sondern ihren Sinn haben, gewisse dumme Fehler gar nicht erst zu machen! Dein Fehler in diesem Programm fängt nicht bei irgendwelchen technischen Details an, sondern ist viel banaler: Du hast ihn dir selbst eingebrockt, weil du nicht verinnerlicht hast, dass man sich seine Arbeit so organisieren muss, das man sich selbst vor Fehlern schützt. Deine Annahme 'Ich mache keine Fehler, also muss ich mir auch keine Hilfskonstrukte machen' ist falsch! PS: Deine 'Technik', jede Zeile am linken Rand anfangen zu lassen, fällt in genau die gleiche Kategorie.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.