Hallo, ich übe mich gerade im Programmieren eines LCD (HD44780U). Ich habe es erfolgreich (so meine ich) initialisiert und kann Zeichen am LCD anzeigen und sogar eigene Zeichen in CGRAM schreiben und Befehle an das LCD schicken. Soweit zu meiner glorreichen Erfolgsgeschichte. Jetzt jedoch wäre ich gerne einen Schritt weiter gegangen und möchte die momentanen Wert des Address Counters auslesen. Soweit schaffe ich es auch, jedoch tritt hierbei ein merkwürdiges Phänomen auf, dass ich mir nicht eklären kann. Das DB6 bit ist IMMER HIGH, egal wo der Cursor gerade steht. Sprich, setze ich den Cursor an die Position 0x05, und lese ihn gleich wieder aus bekomme ich den wert 0b01000101 zurück. Gibt es hierfür eine logische Erklärung, habe ich etwas falsch verstanden oder übersehen. Wenn jemand Einblick in meinen Code benötigt so bin ich gerne bereit diesen zu offenbaren, einfach bescheid geben welcher Teil benötigt wird. Vorab sei erwähnt dass die Portbelegung nicht ganz optimal ist, jedoch nicht in meinem Einflussbereich. Es sei angemerkt dass ich den AC sowohl in 8bit als auch in 4bit Modus auslese und beidermal ist das DB6 high ist. //////////////////////////////////////////////////// // PORTA alias LCD_PORT1 // |-----------------------------------------------| // | PA7 | PA6 | PA5 | PA4 | PA3 | PA2 | PA1 | PA0 | // |-----------------------------------------------| // | DB4 | DB3 | DB2 | DB1 | DB0 | E | RW | RS | // |-----------------------------------------------| // // PORTB alias LCD_PORT2 // |-----------------------------------------------| // | PB7 | PB6 | PB5 | PB4 | PB3 | PB2 | PB1 | PB0 | // |-----------------------------------------------| // | - | - | - | - | - | DB7 | DB6 | DB5 | // |-----------------------------------------------| //////////////////////////////////////////////////// Hier noch der Code in 8bit der AC auslesen soll: // reading AC in 8bit mode uint8_t lcd_get_ac (void) { LCD_PORT1 = 0x00; LCD_PORT2 = 0x00; LCD_PORT1 = (1 << RW); LCD_DDR1 = 0x05; LCD_DDR2 = 0x00; LCD_PORT1 |= (1 << E); #ifndef DEBUG _delay_us(10); #endif PORTC = LCD_PIN1; uint8_t porta = LCD_PIN1; uint8_t portb = LCD_PIN2; LCD_DDR1 = 0xFF; LCD_DDR2 = 0xFF; LCD_PORT1 = 0x00; LCD_PORT2 = 0x00; // output to PORTC (leds) for debugging PORTC = (porta >> 3) | (portb << 5); return (porta >> 3) | (portb << 5); } Ich bin dankbar für jeden Hinweis, da mich das Problem nun schon mehrere Tage beschäftigt, und ich wie gesagt mir es nicht mehr logisch erklären kann.
Kommt denn das _delay_us(10) bei dir zum Zuge? Vielleicht ist das LCD ja nicht schnell genug mit der Antwort.
Abgesehen verstehe ich nicht, wozu das PORTC = LCD_PIN1 gut sein soll. PORTC wird doch gleich wieder umgenietet? Eigentlich kommt mir das alles auch etwas unübersichtlich vor.
LCD_DDR1 = 0x05 ist auch falsch: du musst doch RW setzen, das wird damit aber nicht als Ausgabe gesetzt.
Und überhaupt: wieso machst du nicht eine Funktion, um ein Kommando zu schicken? Oder willst diese ganze immer gleiche Sequenz mit allen Fehlermöglichkeiten jedesmal zur Übung neu schreiben?
Hi >Dann setzst du ja erst RW, und dann DDR. >Macht das Sinn? Genausowenig, wie die Ports auf Ausgang zu schalten, während RW High ist. MfG Spess
Das sind ja schon genug Gründe, daß es nicht gehen kann :-) Wie hast du denn bisher Kommandos geschickt?
Hui danke für die schnellen antworten, und für doch zahlreichen verbesserungsvorschläge. Möchte mich gleich mal für meinen unstrukturierten code entschuldigen. > Kommt denn das _delay_us(10) bei dir zum Zuge ? Ja das kommt zum Zuge und unabhängig wi lange ich warte -> ändert nichts >Abgesehen verstehe ich nicht, wozu das PORTC = LCD_PIN1 gut sein soll Für das erste setzen von PORTC muss ich mich entschuldigen ist noch ein Rudiment aus der Vergangenheit das ich übersehen habe. Ich weiss dass es gleich wieder überschrieben wird und wenig Sinn macht -> wird herausgenommen >Dann setzst du ja erst RW, und dann DDR. Das ist so eine Sache die mir auch komisch vorkommt aber ich hatte es zu Beginn anders und da hat es nicht funktioniert. Ich werde es jedoch umschreiben und einen neuen Versuch starten. >LCD_DDR1 = 0x05 ist auch falsch: du musst doch RW setzen, das wird damit aber nicht als Ausgabe gesetzt. Gutes Argument danke. wird sofort geändert. > wieso machst du nicht eine Funktion, um ein Kommando zu schicken welchen teil meinst du soll ich in eine Funktion auslagern ? >Genausowenig, wie die Ports auf Ausgang zu schalten, während RW High werde das jetzt gleich noch ändern.. >Wie hast du denn bisher Kommandos geschickt Dementsprechend ob es sich um Daten oder LCD Befehle handelt hab ich eine eigene Funktion dafür. also danke erst mal für die zahlreichen anregungen werde diese alle mal umsetzen und mich wieder melden mfg reini
Reinhold Taucher schrieb: >> wieso machst du nicht eine Funktion, um ein Kommando zu schicken > welchen teil meinst du soll ich in eine Funktion auslagern ? Naja, um das Kommando zur Abfrage der Adresse zu schicken, fummelst du an dieser Stelle mit den Bits und Ports des LCD rum. Für jedes andere Kommando (Adresse setzen, Cursor löschen...) müsstest du das dort jeweils wieder machen. Das ist doch ungeschickt: - an jeder dieser vielen Stellen steht streckenweise der gleiche Code - Änderst du die Aufteilung der Bits auf die Ports irgendwann, musst du an vielen Stellen die Portx, PINx und DDRx ändern - an jeder Stelle, wo das nutzst, hast du wieder die gleichen Fehlerquellen Deshalb lagert man doch den gleichbleibenden Code in Funktionen aus:
1 | // sendet ein Daten- oder Kommandobyte (RS am LCD muss vorher
|
2 | // entsprechend gesetzt sein)
|
3 | void writeByte( unsigned char data ) const |
4 | {
|
5 | // viel Bitgefummel, je nach 4-Bit- oder 8-Bit-Modus...
|
6 | |
7 | // Dem LCD Zeit geben, das Kommando auszufuehren
|
8 | _delay_us(42); |
9 | }
|
10 | |
11 | |
12 | // sendet einen Befehl an das LCD
|
13 | void writeCommand( bool isRW, unsigned char command ) const |
14 | {
|
15 | // RS auf 0 setzen...
|
16 | // RW setzen je nach Wert von isRW...
|
17 | |
18 | // Kommandobyte ausgeben
|
19 | writeByte( command ); |
20 | }
|
21 | |
22 | // sendet ein Datenbyte an das LCD
|
23 | void printChar( unsigned char data ) |
24 | {
|
25 | if( iSpalte<nSpalten ) |
26 | {
|
27 | // RS auf 1 setzen...
|
28 | // RW auf 0 setzen...
|
29 | |
30 | // Zeichen ausgeben:
|
31 | writeByte( data ); |
32 | ++iSpalte; |
33 | }
|
34 | else
|
35 | {
|
36 | LCD_State = LCD_State_ColumnNumberInvalid; |
37 | }
|
38 | }
|
(Das ist jetzt aus einer etwas anders aufgebauten C++-Lib und passt vom Inhalt der Funktionen nicht 1:1 zu deinem Programm, aber das Prinzip ist ähnlich). So kann ich jetzt mit printChar() und writeCommand() munter auf das LCD schreiben, ohne mich immer wieder mit Bitgefummel und Timinggeschichten herumzuplagen. Dort, wo ich das Kommando abschicken will (z.B. in deiner lcd_get_ac()) gehört sowas nicht rein, da würde ein schnödes writeCommand( true, LCD_COMMAND_BUSY_FLAG_ADDRESS_READ ) oder ähnliches stehen. Niederes Fummeln gehört von der Logik getrennt! NB: Falls jetzt gleich der Einwand kommt, daß das dann langsamer wäre: 1. Erstens muß man bei der LCD-Ausgabe eh dauernd warten, ein Funktionsaufruf mehr fällt da gar nicht auf 2. in C++ sowieso und in C ebenfalls beim gcc zumindest gibt es auch inline. Dann ist es wie beschrieben sauber getrennt und trotzdem genauso schnell. 3. Der Optimierer soll ja auch was zu tun haben. 4. Selbst mit echten Funktionsaufrufen ist es zwar etwas langsamer, aber dafür kompakter. Die Entscheidung, ob man Code oder Laufzeit sparen will, trifft man besser einmal an anderer Stelle, z.B. mit -Os oder -O2.
hallo, prinzipiell habe ich Methoden an denen ich Befehle und Daten senden kann, jeodoch müsst ich diese umschreiben (damit sie auch RW und Enable entgegen nehmen), was ich auch tun werde sowie ich Zeit finde (bzw. werde ich auf Basis der hier angeführten Vorschläge mein ganzes LCD Prog. refactorn). Ich habe jetzt die Methode etwas überarbeitet. ... LCD_DDR1 = 0x07; LCD_DDR2 = 0x00; LCD_PORT1 = (1 << RW) | (1<< E); _delay_us(100); uint8_t porta = LCD_PIN1; uint8_t portb = LCD_PIN2; .... Jedoch nun bekomme ich irgendwelche Wert heraus, die meines Erachtens nicht mehr im Verhältniss mit dem Coursor stehen. Ich setze den Curosr auf Position 0x09 und schreibe 1 Buchstaben somit sollte AC dann 10 sein !? Den Wert den ich auslese für die einzelnen Ports ist folgender: PINA alias LCD_PIN1: 0b01011110 (sollte 0b01010110 sein meines Erachtens) PIBB alias LCD_PIN2: 0b00000110 (sollte 0b00000000 sein meines Erachtens), habe mir diese Werte mittels den LEDs am PORTC ausgeben lassen. Es sei angemerkt dass das setzen des Cursors ohne Probleme funktioniert mfg reini
Reinhold Taucher schrieb: > LCD_DDR1 = 0x07; > ... > LCD_PORT1 = (1 << RW) | (1<< E); Damit setzst du bei den Input-Pins übrigens die Pullup-Widerstände. Räum doch erstmal deinen Quelltext auf, daß man ihn auch lesen und verstehen kann und zeige ihn dann. Vielleicht sieht man dann ja auch, was falsch ist (wenn du es bis dahin nicht selbst gemerkt hast).
Reinhold Taucher schrieb: > hallo, > prinzipiell habe ich Methoden an denen ich Befehle und Daten senden > kann, jeodoch müsst ich diese umschreiben (damit sie auch RW und Enable > entgegen nehmen), was ich auch tun werde sowie ich Zeit finde (bzw. > werde ich auf Basis der hier angeführten Vorschläge mein ganzes LCD > Prog. refactorn). Nicht: "sobald du Zeit findest" Sondern: "Jetzt ist der Zeitpunkt dafür!" Man kann nie früh genug anfangen, seinen Code vernünftig zu machen. Alles andere führt nur zu Stochern im Nebel. Anstatt da weiter zu stochern, ist es besser die notwendigen Code-Arbeiten zu machen und sich an klarem Code zu erfreuen, als weiter in der Dunkelheit rumzustolpern.
Reinhold Taucher schrieb: > Ich bin dankbar für jeden Hinweis, da mich das Problem nun schon mehrere > Tage beschäftigt ... Mit sauberem Programmieren wäre es vermutlich schneller gegangen. Es würde deine Zeit sparen, und vor allem unsere :-)
Und bevor du weitermachst, solltest du erst mal das hier bereinigen //////////////////////////////////////////////////// // PORTA alias LCD_PORT1 // |-----------------------------------------------| // | PA7 | PA6 | PA5 | PA4 | PA3 | PA2 | PA1 | PA0 | // |-----------------------------------------------| // | DB4 | DB3 | DB2 | DB1 | DB0 | E | RW | RS | // |-----------------------------------------------| // // PORTB alias LCD_PORT2 // |-----------------------------------------------| // | PB7 | PB6 | PB5 | PB4 | PB3 | PB2 | PB1 | PB0 | // |-----------------------------------------------| // | - | - | - | - | - | DB7 | DB6 | DB5 | // |-----------------------------------------------| //////////////////////////////////////////////////// Das ist doch Scheisse, die Datenbits so dermassen unsinnig auf 2 Ports zu verteilen! Wenn du eh einen Port (PORTA) vollständig benutzen kannst und anscheinend genügend Ports frei hast um den 8-Bit Modus zu benutzen, dann leg alle 8 Datenbits auf den Port A und die 3 Steuerleitungen auf den Port B. Dann brauchst du da nicht wie ein Wilder Bits rumschaufeln um ein Datenwort auszugeben. Kopfschüttel. Warum muss man sich selber Prügel zwischen die Beine werfen? Wenn du das aber so lässt, dann ist es umso wichtiger, dass du dieses 'Schaltungsdetail' möglichst in 1 oder 2 Funktionen versteckst, damit du dich auf den höheren Softwareschichten nicht mehr darum kümmern musst und ständig durcheinander kommst. Man kann schon auch im Nebel spazieren gehen. Wenn man dann aber auch noch mutwillig das Licht abschaltet, hat man es nicht anders verdient.
Man könnte auch ganz profan ne Variable nehmen und die wird in der Zeichenausgabefunktion hochgezählt. Den Pin für das Rücklesen und das ganze Codegeraffel dazu kann man sich dann sparen. Man schreibt ja nicht ständig auf das LCD, sondern macht Pausen, damit der Benutzer das LCD auch ablesen kann (max 2..5 Ausgaben/s). Peter
Ich sehe auch keinen Sinn darin, vom LCD etwas zu lesen. Aber wenn er den sportlichen Ehrgeiz hat, das einfach probieren zu wollen, wenn es lt. Datenblatt geht, ist das doch schon legitim genug. Ebenso wie der 8-Bit-Modus: zweckfrei, aber warum nicht? Um schnell voranzukommen, könnte er ja einfach etwas fertiges von hier nehmen. Das ist nur viel langweiliger... Ich will gar nicht wissen, wieviele sinnlose Dinge ich im letzten Jahr gemacht habe.
Klaus Wachtler schrieb: > Um schnell voranzukommen, könnte er ja einfach etwas fertiges von hier > nehmen. Das ist nur viel langweiliger... Find ich schon ok, sich daran zu versuchen. Wenn auch sonst nichts, so ist es doch zumindest eine Übung in Programmieren und der Arbeitsweise. Und Übung macht bekanntlich auch den Meister. Wir sollten nie vergessen, dass wir viele Dinge aus dem Ärmel geschüttelt einfach so hinschreiben, die Einsteiger und Neulinge vor Kopfkratzen stellen. Ein Gerät abfragen zu können, ist an sich nichts verwerfliches. Ob man das dann auch so machen würde, ist IMHO momentan noch nicht so wichtig als die Tatsache, dass er es kann wenn er es braucht.
Klaus Wachtler schrieb: > Damit setzst du bei den Input-Pins übrigens die Pullup-Widerstände. ich bin mir dessen bewusst, aber warum die Anmerkung ? so mach mich jetzt drauf und dran das Programm umzuschreiben. Ihr werdet sicher wieder hören von mir (seht dies als Drohung g)
Es sah mir nach einem Versehen aus. Wenn du das so willst, ist alles in Ordnung! Ich werde mir das Resultat ansehen. Das ist auch eine Drohung.
Klaus Wachtler schrieb: > Es sah mir nach einem Versehen aus. > Wenn du das so willst, ist alles in Ordnung! Du verunsicherst mich jetzt einwenig, warum soll das nicht absicht sein wenn ich AC lesen will ?
>Du verunsicherst mich jetzt einwenig, warum soll das nicht absicht sein >wenn ich AC lesen will ? Wozu willst du AC eigentlich lesen? Wenn du nicht sicher bist wo dein LCD Cursor steht dann positioniere ihn neu oder zähl selber mit.
Reinhold Taucher schrieb: > Klaus Wachtler schrieb: >> Es sah mir nach einem Versehen aus. >> Wenn du das so willst, ist alles in Ordnung! > Du verunsicherst mich jetzt einwenig, warum soll das nicht absicht sein > wenn ich AC lesen will ? Mich machte nur stutzig, daß du für die Datenbits auf dem einen Port die Pullup-Widerstände setzst, für die auf dem anderen nicht. Außerdem weiß ich nicht, ob man überhaupt welche braucht. Wenn das LCD bei der Ausgabe fest genug auf 0 oder 5V zieht, braucht man ja keine Widerstände. Die Version ohne Pullupsetzen würde sehr ähnlich aussehen (|= statt =) und deshalb fragte ich nochmal nach.
Hallo miteinander, ich habe mich jetzt mal hingesetzt und den Code der euch nicht gefallen hat :-) überarbeite bzw. habe ich um jeglichen Fehler auszuschliessen neue angefangen und versucht so gut wie möglich das ganze zu strukturieren. Jetzt habe ich eine Methode die mir die Commands setzt und und ausführt, eine die mir die Daten an das lcd schreibt und eine die mir Daten aus dem lcd ausliest bzw. auslesen soll. Schreiben funktioniert ohne Probleme. Jedoch habe ich nun das Problem dass weniger funktioniert als zuvor. Ich bekomme immer OxFF vom Display zurückgeliefert. Sogar das BF ist immer High ?. Da kann ja was nicht stimmen, nur wie soll es anders sein komm ich absolut nicht drauf und bin meines erachtens auf fremde Hilfe bzw. Denkanstösse angewiesen. Kann mir jemand weiterhelfen ungeachtet meiner PIN Belegung, die ich wie schon erwähnt nicht beeinflussen kann und ungeachtet der Sinnhaftigkeit den AC auszulesen.
Reini T. schrieb: > Da kann ja was nicht stimmen ja Reini T. schrieb: > Kann mir jemand weiterhelfen ja 0xFF deutet auf Zeile 42 hin.
asso habe vergessen zu erwähnen dass ich zuvor den Cursor auf Position 0x09 setzte und der dort auch hinspringt. ich meine DB0 - DB6 sind 1 und DB7 auch
Reini T. schrieb: > verstehe euren Humor nicht ? Stell dir vor du bist Auto-Mechaniker. Ich komm zu dir (zu Fuss) und sag: "Mein Auto geht nicht, was kann das sein?" Was tust du?
Bring ihn nicht mit so einem Beispiel durcheinander! Ein Automechaniker würde nie nach Quelltext fragen.
Heureka, ich habs geschafft die Werte RICHTIGEN Werte auszulesen. Jetzt dürft ihr mir eine anderen Phänomen erklären. Wenn ich zuerst DDR setze und dann RW und E bekomme ich flasche Werte sprich lauter 1, obwohl RW und E gesetzt sind. Wenn ich aber zuerst RW dann E setze und erst wenn diese beiden gesetzt sind die Portrichtung ändere bekomme ich die richtigen Werte heraus. Kann mir jemand erkären was dabei der Unterschied ist. >Das soll heißen, zeig endlich dein Programm trau ich mich nicht mehr .. ich glaube es würde nicht euren Ansprüchen genügen, nachdem ich jetzt wieder wild die Bits ändere. >Was tust du? Ich würde ihn höflich daraufhinweisen dass ich für weiter Analysen das Auto benötigen würde und nicht einfach irgendwas behaupten um die Person weiter zu verwirren.
Reini T. schrieb: >>Das soll heißen, zeig endlich dein Programm > trau ich mich nicht mehr .. ich glaube es würde nicht euren Ansprüchen > genügen, nachdem ich jetzt wieder wild die Bits ändere. Dann musst du in Zukunft zur Wahrsagerin gehen. >>Was tust du? > Ich würde ihn höflich daraufhinweisen dass ich für weiter Analysen das > Auto benötigen würde und nicht einfach irgendwas behaupten um die Person > weiter zu verwirren. Siehst du. Und genauso wie du ohne Auto nicht analysieren kannst, was da schief läuft, können wir ohne Quellcode auch nicht analysieren was in deinem Code schief läuft. > Jetzt dürft ihr mir eine anderen Phänomen erklären. Wenn ich zuerst > DDR setze und dann RW und E bekomme ich flasche Werte sprich lauter > 1, obwohl RW und E gesetzt sind. > Wenn ich aber zuerst RW dann E setze und erst wenn diese beiden > gesetzt sind die Portrichtung ändere bekomme ich die richtigen > Werte heraus. > Kann mir jemand erkären was dabei der Unterschied ist. Ohne Quellcode ... nein Die Profis hier haben sich schon längst abgewöhnt auch nur 1 Cent auf eine textuelle Beschreibung deines Codes zu setzen (*). Denn meistens stimmt die nicht mit dem überein, was dann tatsächlich programmiert wurde :-) (*) Edit: Das betrifft nicht nur dich speziell, sondern ist ganz allgemein zu sehen. Ohne tatsächlichen Code kann man in den seltensten Fällen helfen.
Reini T. schrieb: >>Das soll heißen, zeig endlich dein Programm > trau ich mich nicht mehr .. ich glaube es würde nicht euren Ansprüchen > genügen, nachdem ich jetzt wieder wild die Bits ändere. Dann mach es halt ordentlich. Entweder funktioniert es dann schon, oder man kann dir helfen. So musst du halt alleine vor dich hin raten. Das macht gar nichts, sondern spart allen hier Arbeit.
Okey hier der Code den ich nicht ganz "verstehe": so bekomme ich die wahren Werte (AC) zurückgeliefert
1 | ...
|
2 | LCD_PORT1 = 0x00; |
3 | LCD_PORT2 = 0x00; |
4 | |
5 | if(doRead){ |
6 | LCD_PORT1 |= (1 << RW); |
7 | if(data) LCD_PORT1 |= (1 << RS); |
8 | lcd_enable(); |
9 | LCD_DDR1 = 0x07; |
10 | LCD_DDR2 = 0x00; |
11 | |
12 | _delay_us(100); |
13 | }
|
14 | ...
|
und so bekomme ich die falschen Werte (0xFF):
1 | ...
|
2 | LCD_PORT1 = 0x00; |
3 | LCD_PORT2 = 0x00; |
4 | |
5 | if(doRead){ |
6 | LCD_DDR1 = 0x07; |
7 | LCD_DDR2 = 0x00; |
8 | LCD_PORT1 |= (1 << RW); |
9 | if(data) LCD_PORT1 |= (1 << RS); |
10 | lcd_enable(); |
11 | _delay_us(100); |
12 | }
|
13 | ...
|
lcd_enable() setzt lediglich das "Enable" Bit. mfg Reini.
Ich suche gar nicht erst nach dem Fehler, weil es nur ein Bruchteil des Quelltextes ist. Aber nebenbei: Wenn du bei (data) RS setzst, willst es vermutlich bei nicht-(data) auch löschen? Im oberen Fall setzst du übrigens erst die Bits, dann DDR. Wenn DDR von vorher auf 0 steht, hast du damit nicht die Ausgänge gesetzt, sondern die Pullups. Aber es bleibt dabei: Solange du nicht die grundlegenden Sachen einmal in funktionierende Funktionen packst und aus dem Teil mit der Logik nur noch aufrufst, sondern stattdessen alles planlos verquirlst, geht dieses Spiel in zwei Jahren immer noch mit den selben Fragen weiter.
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.