Forum: Mikrocontroller und Digitale Elektronik LCD-Display funktioniert nur nach Übertragung des Programms


von ThomasC (Gast)


Angehängte Dateien:

Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Manfred S. (mandinice)


Lesenswert?

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

von ThomasC (Gast)


Lesenswert?

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

von ThomasC (Gast)


Lesenswert?

@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 :-)

von ThomasC (Gast)


Lesenswert?

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

von Holger W. (holgerw)


Lesenswert?

Hast du mal getestet die Ini des LCD 2x hintereinander auszuführen ?
Holger

von spess53 (Gast)


Lesenswert?

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

von ThomasC (Gast)


Lesenswert?

@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 !

von snowfly (Gast)


Lesenswert?

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.

von ThomasC (Gast)


Lesenswert?

@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

von ThomasC (Gast)


Angehängte Dateien:

Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Anja (Gast)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Jobst M. (jobstens-de)


Angehängte Dateien:

Lesenswert?

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

von Thomas C. (thomasc)


Lesenswert?

@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

von Jobst M. (jobstens-de)


Lesenswert?

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

von Thomas C. (thomasc)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

> 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
Noch kein Account? Hier anmelden.