Hallo,
ich benutze einen Atmega2560 zum Ansteuern eines grafischen LCD
Displays. Das Display benutzt den folgenden Controller ST7565R.
Warum zeigt das Display die Null vernüftig an, wenn ich die Daten ohne
for- Schleife nacheinander wie folgt schicke?
1
const unsigned char* Zeiger;
2
Zeiger = font1D;
3
lcdSendData(*(Zeiger+240+0));
4
lcdSendData(*(Zeiger+240+1));
5
lcdSendData(*(Zeiger+240+2));
6
lcdSendData(*(Zeiger+240+3));
7
lcdSendData(*(Zeiger+240+4));
Benutze ich eine for-Schleife wird die Null nicht richtig dargestellt:
1
for (uint8_t i = 0; i<=5;i++)
2
{
3
lcdSendData(*(i+Zeiger+240));
4
5
}
Die LCD Ausgabe ist auf dem Foto im Anhang gezeigt.
Vielen Dank für eure Hilfe! :)
c-hater schrieb:> Reinhard O. schrieb:>>> lcdSendData(*(Zeiger+240+4))> ^!!!>>> for (uint8_t i = 0; i<=5;i++)> ^^!!!>> Alles klar?
Vielen Dank für deine Antwort. Habe es gerade korrigiert.
1
for (uint8_t i = 0; i<5;i++)
2
{
3
lcdSendData(*(i+Zeiger+240));
4
5
}
Leider immernoch die selbe Ausgabe wie auf dem Foto obwohl es ein
offensichtlicher Fehler war.
Reinhard O. schrieb:> Leider immernoch die selbe Ausgabe wie auf dem Foto obwohl es ein> offensichtlicher Fehler war.
Dann gibt es wohl noch andere Fehler. In dem Teil des Codes, den du
nicht gepostet hast...
Wer hätte das ahnen können...
Es muss wohl so sein. Ich kann es mir aber nicht erklären, weil die
for-Schleife doch eigentlich genau das macht, was ich oben drüber
machen. Dann dürfte das ohne for-Schleife ja auch nicht funktionieren.
Oder sehe ich das falsch?
Reinhard O. schrieb:> Oder sehe ich das falsch?
Ja. Diese Logik funktioniert leider nur, solange aller anderer Code
fehlerfrei ist.
Wer clever ist, zieht den naheliegenden Umkehrschluss: wenn's nicht
funktioniert, obwohl es nach den Gesetzen der Logik funktionieren
müsste, MUSS der Fehler irgendwo anders stecken...
Alles andere wäre Voodoo...
c-hater schrieb:> Reinhard O. schrieb:>>> Oder sehe ich das falsch?>> Ja. Diese Logik funktioniert leider nur, solange aller anderer Code> fehlerfrei ist.>> Wer clever ist, zieht den naheliegenden Umkehrschluss: wenn's nicht> funktioniert, obwohl es nach den Gesetzen der Logik funktionieren> müsste, MUSS der Fehler irgendwo anders stecken...>> Alles andere wäre Voodoo...
Da stimme ich dir zu.
Ich habe noch über ein timing Problem nachgedacht. Bei der for-Schleife
liegt ja zwischen den einzelnen lcdSendData aufrufen mindestens 1
Prozessortakt.
Im Gegensatz zu den lcdSendData Befehlen direkt untereinander.
Könnte das ein Ansatz sein? Welchen Code soll ich posten? Von der
Funktion lcdSendData?
Danke dir und den zukünftigen Tippgebern!
Reinhard O. schrieb:> Ich habe noch über ein timing Problem nachgedacht. Bei der for-Schleife> liegt ja zwischen den einzelnen lcdSendData aufrufen mindestens 1> Prozessortakt.> Im Gegensatz zu den lcdSendData Befehlen direkt untereinander.
Wenn das wirklich ein Problem sein sollte (was nicht vollkommen
auszuschliessen ist), dann taugt der Code nix, den du verwendest.
Ich tippe aber eher auf Fehler, die DU SELBER gemacht hast. Wenn ich als
c-hater mit Querlesen schon in ganz wenigen Zeilen von dir auf Anhieb
einen Fehler finde, läßt das nämlich nix Gutes für die Qualität deines
restlichen Codes ahnen, das sagt mir meine Erfahrung...
> Welchen Code soll ich posten? Von der> Funktion lcdSendData?
Den ganzen natürlich, abgerüstet auf das Minimum, bei dem der Fehler
noch auftritt. In compilierbarer Form. Sonst sitzen wir womöglich noch
bis Sylvester 2030 an deinem Problem...
Reinhard O. schrieb:> [code]
Ja, das dürfte ein Timingproblem sein. Der Code nimmt auf zwei Ebenen
nur unvollständig Rücksicht auf das Timing des Controllers.
Der Buscycle ist mindestens wacklig (hängt vom Takt des AVR ab, bei
16MHz ist es grenzwertig).
Aber auch bei den Kommandos wird oft keine Rücksicht auf die
Ausführungszeiten genommen. lcd_Init ist sehr wacklig, lcdSetPos ist bei
16Mhz definitiv schon kaputt.
Wetten: wenn du das Teil mit 1MHz betreibst, läuft das Programm, genau
wie du es dir vorstellst? Auch mit der Schleife...
Fazit: wenn du selber LowLevel programmieren willst (was ich nur
begrüßen kann), musst du lernen, Datenblätter zu lesen und deren
Restriktionen im Code umzusetzen. Es geht nicht anders.
c-hater schrieb:> Fazit: wenn du selber LowLevel programmieren willst (was ich nur> begrüßen kann), musst du lernen, Datenblätter zu lesen und deren> Restriktionen im Code umzusetzen. Es geht nicht anders.
Gut, dann weiß ich jetzt woran es liegt! Vielen Dank!
Dann werde ich die fuse bits mal so setzen, dass der atmega2560
langsamer läuft und schauen ob das Problem dann weg ist.
Wie mache ich sowas im Code? Spontan fällt mir ein flag ein, dass im
1Mhz takt gesetzt wird.
Reinhard O. schrieb:> Wie mache ich sowas im Code?
Das kommt auf die Zeiten an, um die es jeweils geht. Von taktabhängig
implantierter Zahl von nop() über delay_us() bis hin zu asynchronem
Warten kann alles nötig werden/sinnvoll sein.
Man muss halt einfach nur zwei Sachen wissen: wie lange muss die
Verzügerung mindestens sein und wie lange braucht mein eigener Code bei
gegebenem Takt. Die Differenz (sofern positiv) muß man halt möglichst
effizent verwarten.
Ich vermute, die automatische "pos"-Erhöhung im Controller klappt nicht
(aus was für Gründen auch immer, falsche Initialisierung, evtl wegen
Timingfehler, s.o.) - die zweiten 5 Bytes werden falsch dargestellt.
Kannst ja mal Schleife und Einzelstatements vertauschen.
> Sollte diess nicht> lcdSendData(*(Zeiger+240+i));> sein?
Das ist egal, macht das gleiche. Da das so häufig vorkommt, haben die
C-Erfinder dem sogar eine extra Syntax gegönnt:
lcdSendData(Zeiger[240+i]);
;-)
c-hater schrieb:> Man muss halt einfach nur zwei Sachen wissen: wie lange muss die> Verzügerung mindestens sein und wie lange braucht mein eigener Code bei> gegebenem Takt. Die Differenz (sofern positiv) muß man halt möglichst> effizent verwarten.
Um die Laufzeit des Codes zu bestimmen, würde ich einen Ausgang am
Anfang auf high setzen und am Ende low und die Pulsbreite mit dem
Oszilloskop messen oder? Dann muss ich das mal mit zur Arbeit nehmen.
Besitze privat leider noch keins.
foobar schrieb:> Kannst ja mal Schleife und Einzelstatements vertauschen.
Könntest du ein Code Beispiel posten?
P.S. Ich hatte schonmal ein ähnliches Problem als ich Daten über SPI in
einer for-Schleife übertragen wollte. Da wurden falsche Bytes vom
Controller ausgegeben sobald ich die untereinander ohne Schleife
geschickt habe, hat es geklappt. Die Ursache habe ich damals nicht
ausmachen können.
Reinhard O. schrieb:> Um die Laufzeit des Codes zu bestimmen, würde ich einen Ausgang am> Anfang auf high setzen und am Ende low und die Pulsbreite mit dem> Oszilloskop messen oder?
Kann man machen.
Ich selber programmiere Assembler und bin auf sowas nicht angewiesen,
weil ich direkt Takte zählen kann. Dementsprechend habe ich auch einen
Satz Makros für die Warterei, mit dem halbautomatisch Busywaits
effizient in den Code eingebaut werden können.
Das ist aber natürlich nicht der Weisheit letzter Schluss. Die
Entscheidung, ob Busywait oder asynchrones Warten sinnvoller ist, nimmt
mir diese Halbautomatik nicht ab.
Beim 44780 (und kompatiblen) als Ziel und AVR8 als Quelle steht diese
Entscheidung leider relativ häufig an, weil in dem weiten Taktbereich,
in die AVR operieren können, viele der HD44780-Wartezeiten je nach
Anwendung mal in diese Kiste gehören, mal in die andere...
Nur die Buszyklen sind immer mit Busywaits hinreichend versorgt, da
lohnt asynchrones Warten nie.
c-hater schrieb:> Ich selber programmiere Assembler und bin auf sowas nicht angewiesen,> weil ich direkt Takte zählen kann.
Willkommen im 20. Jahrhundert. Moment, C wurde (und andere Hochsprachen)
wurden ja bereits vor 1975 erfunden.
Also: Willkommen im 21. Jahrhundert, wo wirlich niemand mehr - auch kein
Profi - ganze Projekte in Assembler programmiert.
Wie kann man so in der Vergangenheit leben? Ich kann mir kaum vorstellen
- solltest du im professionellen Umfeld agieren - dass du an Aufträge
kommst.
@c-hater: ich kann verstehen, daß du in Assembler programmierst, denn
nur so hat man einen Controller wirklich verstanden. Würde ich auch
gerne können, aber ich nur mal vor 32 Jahren in der Ausbildung
8085-Assembler programmiert und das schnell wieder vergessen. Schade
drum.
C-Lover schrieb:> Ich kann mir kaum vorstellen> - solltest du im professionellen Umfeld agieren - dass du an Aufträge> kommst.
Mein Vorteil ist wohl: ich kann nicht NUR C, aber ich kann auch C und
noch einige Sprachen mehr...
Es hatte schon immer gewisse Vorteile, kein Schmalspur-Vollidiot zu
sein...
foobar schrieb:>>> Kannst ja mal Schleife und Einzelstatements vertauschen.
Dann sind die 0 und die fehlerhafte Anzeige vertauscht (s. Foto).
foobar schrieb:> Und wenn du das ausprobiert hast, kannst du mal dein wait fixen:
Habe die wait Funktion durch deine ersetzt. Die Anzeige ist immer noch
fehlerhaft.
Leider stehen im Datenblatt keine weiteren Erklärungen zum Timing nur
Diagramme.
Warum wird sowas nicht als Flußdiagramm dargestellt in dem dann auch die
Wartezeiten zwischen den einzelnen Befehlen angegeben werden?
Reinhard O. schrieb:> Warum wird sowas nicht als Flußdiagramm dargestellt in dem dann auch die> Wartezeiten zwischen den einzelnen Befehlen angegeben werden?
Üblicherweise gibt es entweder bei jedem einzelnen Befehl eine Anmerkung
zur benötigten Mindestdauer oder eine Tabelle, die das für alle Befehle
in einer übersichtlichen Form zusammenfasst.
Man muss halt nur die Kompetenz haben, diese Informationen im DB zu
finden...
c-hater schrieb:> Üblicherweise gibt es entweder bei jedem einzelnen Befehl eine Anmerkung> zur benötigten Mindestdauer oder eine Tabelle, die das für alle Befehle> in einer übersichtlichen Form zusammenfasst.
Könntest du mal in das Datenblatt schauen und mir sagen an welcher
Stelle ich gucken muss?
c-hater schrieb:> Man muss halt nur die Kompetenz haben, diese Informationen im DB zu> finden...
Genau aus dem Grund bin ich hier. In der Hoffnung diese Kompetenz mit
der Hilfe aus dem Forum aufzubauen.
Der TO sollte erstmal die Schaltung offen legen, wie er
das Interface mit dem µC beschaltet hat.
Da gibts nämlich mehrere Möglichkeiten.
Die Block-Diagramme für die passenden Routinen sind
im Datasheet beschrieben.
Also meine spontane Vermutung liegt hier:
> const unsigned char zero[] PROGMEM = {> 0x7C, 0x8A, 0x92, 0xA2, 0x7C,> };
Das Array liegt also in PROGMEM.
> const unsigned char* Zeiger;> Zeiger = zero;> lcdSendData(*(Zeiger+0));> ...
Da kannst du nicht einfach einen normalen Zeiger drauf basteln und
erwarten, dass du die richtigen Daten bekommst.
Für PROGMEM brauchst du die pgm_read_zeug-Funktionen, sonst bekommst du
falsche Werte. Alles, was du mit PROGMEM markierst, liegt im Flash und
damit im Flash-Adressraum, aber Zeiger zeigen grundsätzlich ins RAM und
damit den RAM-Adressraum. Auf einem AVR sind das getrennte Welten.
> Für PROGMEM brauchst du die pgm_read_zeug-Funktionen, sonst bekommst du> falsche Werte.
Stimmt. Und wie ich gerade sehe, kommt noch obendrauf, dass es ein AVR
mit mehr als 64k Flash ist - viel Spaß mit diesen Missgeburten ...
*** schrieb:> Der TO sollte erstmal die Schaltung offen legen, wie er> das Interface mit dem µC beschaltet hat. Da gibts nämlich mehrere> Möglichkeiten.
Hallo,
Ich steuere das LCD über die 6800 Schnittstelle an.
Wenn das also Info nicht reicht, poste ich später den Schaltplan.
S. R. schrieb:> Daten bekommst.>> Für PROGMEM brauchst du die pgm_read_zeug-Funktionen
Ich teste es später mal mit der Funktion.
Vielen Dank für den Hinweis!
S. R. schrieb:> Also meine spontane Vermutung liegt hier:>>> const unsigned char zero[] PROGMEM = {>> 0x7C, 0x8A, 0x92, 0xA2, 0x7C,>> };>> Das Array liegt also in PROGMEM.>>> const unsigned char* Zeiger;>> Zeiger = zero;>> lcdSendData(*(Zeiger+0));>> ...>> Da kannst du nicht einfach einen normalen Zeiger drauf basteln und> erwarten, dass du die richtigen Daten bekommst.
Ich habe einfach mal "PROGMEM" weggenommen und jetzt funktioniert es.
Super! Vielen Dank!
Im nächsten Schritt werde ich PROGMEM wieder hinzufügen und gucken ob
ich es mit den Fubktionen pgm_read_*** zum Laufen bringe.
Reinhard O. schrieb:> Im nächsten Schritt werde ich PROGMEM wieder hinzufügen und gucken ob> ich es mit den Fubktionen pgm_read_*** zum Laufen bringe.
Wenn du in C (nicht C++) unterwegs bist, dann kannst du das Array auch
einfach mal mit "__flash" markieren. Das hat den gleichen Effekt wie
PROGMEM, braucht aber keine umständlichen Zugriffsfunktionen.