Hallo, hab folgenden code mit dem ich das LCD mit HD44780 Controller anspreche. #include <avr/io.h> #include <inttypes.h> #include <avr/iom8.h> int warte(void) { PORTC |= (1 << 2); //RS auf 1 DDRB = 0x00; //PORTB auf Eingang while (PORTB & 0b10000000); //BusyFlag gesetzt DDRB = 0xFF; //PORTB auf Ausgang PORTC &= ~(1 << 2); //RS auf 0 return 0; } int main(void) { DDRB = 0xff; DDRC = 0xff; PORTC = 0x00; PORTD = 0x00; warte(); PORTD |= (1 << 4) | (1 << 5); //Function Set warte(); PORTD = 0x0E; //Display ein warte(); PORTD = 0x01; //Display löschen warte(); PORTD = 0x06; //Cursor Autoinkrement warte(); PORTC |= (1 << 3); //RS auf 1 warte(); PORTB = 0xA3; //# auf LCD ausgeben warte(); PORTC |= (1 << 1); //enable ein warte(); PORTC &= ~(1 << 1); //enable aus warte(); while (1); return 0; } passieren tut gar nix. Ich benutze das im 8 Bit Modus, Wobeo PORTB die 8 Datenleitungen übernimmt und PORTC Pin 1: Enable, Pin2: RW, Pin3: RS
Ohne das jetzt alles genau gelesen zu haben... (zumal du dein Pin-Mapping nirgends beschrieben hast). Hast du dir mal das Datenblatt eines HD44780 angeguckt? Während der Initialisierung musst du einiges an Timing einhalten, selbst wenn du ansonsten ein BUSY-Flag lesen kannst und im 8-Bit-Modus arbeitest. Auf den Power-on-Reset würde ich mich nicht verlassen. Ähem, eins fällt mir gerade noch auf:
1 | DDRB = 0x00; //PORTB auf Eingang |
2 | while (PORTB & 0b10000000); //BusyFlag gesetzt |
Du hast eben ein 0x00 nach PORTB geschrieben, was bringt dich zu der Annahme, dass beim anschließenden Rücklesen da was anderes als 0x00 stehen könnte? Um die Eingänge zu lesen, musst du von PINB lesen.
blöder Fehler, aber daran lags nicht. Pinmapping hab ich doch in meinem letzten Satz geschrieben.
Wenn Du schreibst, dass Du für die Ansteuerung Port B und Port C benutzt, im Programm aber Port D und Port C schreibst, solltest Du Dich vielleicht erst mal entscheiden, was Du genau willst. Abgesehen davon kann das Busy-Flag soweit ich mich erinnere am Anfang der Init noch gar nicht gelesen werden. Und selbst wenn es das kann: Du liest es jeweils nur ein einziges Mal. Da es bei diesem ersten Lesen gesetzt sein dürfte, hängt sich Dein Programm in der Abfrage while(PINB & 0b00000001); auf. Du solltest wirklich das Datenblatt vom Display genau durchlesen. Da steht meist ein Ablaufdiagramm für die Initialisierung drin und auch, ab welchem Schritt Du das Busy-Flag lesen kannst. Gruß Johnny
nach etlichen Verbesserungen und lesen des Datenblattes des HD44780 komm ich zu folgendem code der ein # ausgeben soll, dies aber immer noch nicht tut. Stattdessen leuchten alle verfügbaren dots. int warte(void) { PORTC |= (1 << 3); //RS auf 1 PORTC &= ~(1 << 2); //RW auf 0 DDRB = 0x00; //PORTB auf Eingang while (!(PINB & 0b10000000)); //BusyFlag gesetzt DDRB = 0xFF; //PORTB auf Ausgang PORTC &= ~(1 << 3); //RS auf 0 PORTC |= (1 << 2); //RW auf 1 return 0; } int warte_ms(int ms) { int zahl = 0; int zahl2; for (zahl = 0; zahl < ms; zahl++) { zahl2 = 0; while (zahl2 < 1000) zahl2++; } return 0; } int sende(void) { PORTC |= (1 << 1); //enable ein warte_ms(0); PORTC &= ~(1 << 1); //enable aus return 0; } int main(void) { DDRB = 0xff; DDRC = 0xff; PORTC = 0x00; PORTB = 0b00110000; sende(); PORTB = 0b00110000; sende(); PORTB = 0b00110000; sende(); warte(); PORTB = 0b00111000; //Function Set sende(); warte(); PORTB = 0b00001000; //Display off sende(); warte(); PORTB = 0x01; //Display löschen sende(); warte(); PORTB = 0x07; //Entry Mode sende(); warte(); PORTB = 0b00001111; //Display on sende(); warte(); PORTC |= (1 << 3); //RS auf 1 warte(); PORTB = 0x23; //# auf LCD ausgeben sende(); while (1) { } return 0; }
Du setzt beim Lese- und Schreib-Versuch das Enable nicht. Die Abfrage vom Busy-Flag ist auch immer noch nicht OK. Die Daten werden nur in der Zeit, in der Enable gesetzt ist, übertragen. Das gilt für lesen und Schreiben. Das Busy-Flag muss in der Warteschleife immer wieder eingelesen werden. Es reicht nicht, es nur einmal abzufragen! Außerdem macht es wenig Sinn, zu warten, bis das Flag gesetzt ist, sondern eher bis es gelöscht ist. Müsste wohl eher heißen while(PINB & 10000000){BUSY_FLAG lesen} Außerdem solltest Du Dir angewöhnen, Bezeichner für die Steuerbits zu vergeben und diese dann auch zu benutzen. Ausdrücke wie PORTC |= 1 << 3 sind besser lesbar, wenn Du schreibst #define RS 3 //Irgendwelcher Code.... PORTC |= 1 << RS; Auch die Binärausdrücke solltest Du durch so was ersetzen. Macht das Lesen für andere erheblich leichter! Gruß Johnny
also die Abfrage nach dem BF hab ich schonmal abgeändert: int warte(void) { PORTC &= ~(1 << 3); //RS auf 0 PORTC |= (1 << 2); //RW auf 1 DDRB = 0x00; //PORTB auf Eingang while (PINB & 0b10000000) //BusyFlag gesetzt sende(); DDRB = 0xFF; //PORTB auf Ausgang PORTC |= (1 << 3); //RS auf 1 PORTC &= ~(1 << 2); //RW auf 0 return 0; } während des enable wird ja nichts gesendet oder empfangen, sondern erst bei der fallenden Flanke des enable Signals, was in der sende() funktion gemacht wird. Von daher sehe ich da keinen Fehler. Mit den Änderungen an der BF Abfrage hat sich an der Funktionalität aber nichts getan.
>während des enable wird ja nichts gesendet >oder empfangen, sondern erst bei der fallenden >Flanke des enable Signals Stimmt so nur beim Schreiben. Beim Lesen vom Display (also auch beim Lesen des Busy-Flag) liegen die Display-Daten nur so lange am Port an, wie Enable High ist! Du musst beim lesen den Port (in eine Variable) sichern, während Enable High ist. Wenn Du ein Display von EA hast, kann es sein, dass die Beschreibung im Datenblatt ein wenig zu kurz gefasst ist. Ich habe mal ein Display von Sharp mit einem ebenfalls HD44780-kompatiblen Controller programmiert, und da waren die erforderlichen Wartezeiten zwischen den Initialisierungsschritten erheblich länger. Da gab es im Datenblatt auch richtige Ablaufdiagramme für die Init inkl. der Zeitangaben, die ich auch heute (für die EA-Displays) immer noch verwende. Habe damit nie Probleme gehabt. Möglicherweise solltest Du einfach mal bei der Initialisierung die Wartezeiten erhöhen (ruhig auf ein paar zig ms) und schauen, ob es was bringt. BTW, warum verwendest Du keine Bibliotheksfunktionen für die Wartezeiten? Und wenn Du den Code schon änderst, dann ändere doch bitte auch die '1 << irgendwas'-Ausdrücke. Es ist auch weniger fehleranfällig. Vorschlag: #include <avr/delay.h> #define EN 1 #define BUSY 7 unsigned char dd_in; //Variable für Datenportsicherung //.... int sende(void) { PORTC |= (1 << EN); //enable ein delay_us(1); dd_in = PINB; //PORTB (Display-Daten) sichern PORTC &= ~(1 << EN); //enable aus } //... dd_in = 1 << BUSY; //Variable setzen f. Schleife while(dd_in & (1 << BUSY)) { sende(); } Das nur als Idee... Gruß Johnny
Hab das LCD-Handbuch von Sharp mal ausgegraben (im Anhang). Ist zwar schon was älter, aber es funktioniert. Da steht eigentlich alles sehr schön detailliert beschrieben drin. Auf S. 13 die Beschreibung (Ablaufdiagramm) der Init mit Timing. Ich habe mit der Beschreibung schon vor Jahren ein 2x16-LCD (LM162KS von Sharp) an einem ST6-µC zum laufen gebracht und verwende das Prinzip immer noch bei allen HD44780-kompatiblen Display-Controllern. Versuchs einfach mal mit den angegebenen Zeiten. Gruß Johnny
also ich hab das deinen Tipps zufolge umgeschrieben, das Manual auch gelesen und mit meinem code verglichen. Mehr als einmal. Viel mehr als einmal. Aber ich find den Fehler einfach nicht. Hätte nicht gedacht dass es so schwer ist ein Zeichen auf ein LCD zu kriegen. #include <avr/io.h> #include <inttypes.h> #include <avr/iom8.h> #include <avr/interrupt.h> #define EN 1 #define BUSY 7 #define RW 2 #define RS 3 volatile int temp; int ReadChannel(int mux) { int result = 0; //Initialisieren wichtig, da lokale Variablen //nicht automatisch initialisiert werden und //zufällige Werte haben. Sonst kann Quatsch rauskommen ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0); // Frequenzvorteiler // setzen auf 8 (1) und ADC aktivieren (1) ADMUX = mux; // Kanal waehlen ADMUX |= (1<<REFS0) | (1<<ADLAR); // interne Referenzspannung nutzen /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */ ADCSRA |= (1<<ADSC); // eine ADC-Wandlung while ( ADCSRA & (1<<ADSC) ); // auf Abschluss der Konvertierung warten /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */ // for(i=0;i<4;i++) // { ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion" while ( ADCSRA & (1<<ADSC) ); // auf Abschluss der Konvertierung warten result = ADCH; // Wandlungsergebnisse aufaddieren // } ADCSRA &= ~(1<<ADEN); // ADC deaktivieren (2) // result /= 4; // Summe durch vier teilen = arithm. Mittelwert return result; } //ISR(TIMER0_OVF_vect) int warte_ms(int ms) { int zahl = 0; int zahl2; for (zahl = 0; zahl < ms; zahl++) { zahl2 = 0; while (zahl2 < 1000) zahl2++; } return 0; } int sende(void) { PORTC |= (1 << EN); //enable ein warte_ms(0); PORTC &= ~(1 << EN); //enable aus return 0; } int empfange(void) { PORTC |= (1 << EN); //enable ein warte_ms(1); temp = PINB; PORTC &= ~(1 << EN); //enable aus return 0; } int warte(void) { PORTC &= ~(1 << RS); //RS auf 0 PORTC |= (1 << RW); //RW auf 1 DDRB = 0x00; //PORTB auf Eingang temp |= (1 << BUSY); //BusyFlag setzen while (temp & BUSY) //solange BusyFlag gesetzt empfange(); DDRB = 0xFF; //PORTB auf Ausgang PORTC |= (1 << RS); //RS auf 1 PORTC &= ~(1 << RW); //RW auf 0 return 0; } int main(void) { DDRB = 0xff; DDRC = 0xff; PORTC = 0x00; warte_ms(100); PORTB = 0b00110000; sende(); warte_ms(10); PORTB = 0b00110000; sende(); warte_ms(10); PORTB = 0b00110000; sende(); warte(); PORTB = 0b00111000; //Function Set sende(); warte(); PORTB = 0b00001000; //Display off sende(); warte(); PORTB = 0x01; //Display löschen sende(); warte(); PORTB = 0x07; //Entry Mode sende(); warte(); PORTB = 0b00001111; //Display on sende(); warte(); PORTC |= (1 << RS); //RS auf 1 PORTC &= ~(1 << RW); //RW auf 0 warte(); PORTB = 0x23; //# auf LCD ausgeben sende(); while (1) { } return 0; }
> #include <avr/iom8.h>
Bitte nicht. #include <avr/io.h> zusammen mit -mmcu=atmega8
ist der richtige Weg.
Warum weigerst Du Dich, die Bibliotheksfunktionen für die Wartezeiten zu benutzen???? Ich weiß zwar nicht, mit welcher Taktfrequenz der µC läuft, aber das was Du da programmiert hast gibt mit großer Wahrscheinlichkeit keine Millisekunden Wartezeit.
weil ich dann haufenweise Fehlermeldungen kriege. Selbst wenn meine Funktion nicht die Anzahl ms wartet die sie soll, dann wartet sie höchstens mehr. Und dass das LCD nicht funktioniert wegen zulangen Wartezeiten kann doch wohl nicht sein. Der µC ist übrigens mit 1MHz getaktet.
aber das hat doch mit dem LCD an sich nichts zu tun...aber naja, wer weiss....ich nicht. Warnungen: E:/Programme/WinAVR/avr/include/util/delay.h:136:3: warning: #warning "F_CPU not defined for <util/delay.h>" ../LCD.c: In function `sende': ../LCD.c:47: warning: implicit declaration of function `delay_us' Fehlermeldungen: ../LCD.c:47: undefined reference to `delay_us' LCD.o: In function `empfange': ../LCD.c:55: undefined reference to `delay_us' LCD.o: In function `main': ../LCD.c:80: undefined reference to `delay_us' ../LCD.c:83: undefined reference to `delay_us' ../LCD.c:86: undefined reference to `delay_us' F_CPU hab ich gleich am anfang auf 1000000 festgesetzt. Warum der delay_us jetzt nicht kennt...keine Ahnung, steht in der delay.h drin. Aber wie gesagt, dürfte mit dem eigentlichen Problem nichts zu tun haben.
> F_CPU hab ich gleich am anfang auf 1000000 festgesetzt.
Vor (richtig) oder nach (falsch) dem #include <util/delay.h>
? also ganz am anfang der main() ich kann ja nicht einfach so irgendwo irgendwelche Befehle hinschreiben.
hat vielleicht jemand ein fertiges hex file das ein Zeichen auf ein lcd schreibt? Müsste dann natürlich noch die Pinbelegung wissen. Um zu sehn ob das LCD vielleicht nicht kaputt ist.
> also ganz am anfang der main() Falsch. > ich kann ja nicht einfach so irgendwo irgendwelche Befehle > hinschreiben. C hat keine ,Befehle'. F_CPU ist ein Makro für den C-Präprozessor. Damit die Funktionen von <util/delay.h> so funktionieren wie designt, muss dieser Makro vor dem #include <util/delay.h> definiert sein. Das kann man entweder so machen:
1 | #define F_CPU 1000000ul
|
2 | #include <util/delay.h> |
oder aber du reichst F_CPU gleich von der Kommandozeile aus durch, indem du im Makefile ein -DF_CPU=1000000ul mit in die CFLAGS aufnimmst. Letzteres ist die Methode, die der Makefile-Generator des GCC-Plugins im AVR Studio macht (und dabei hat er einen Bug, dass er bei jedem Aufmachen des entsprechenden GUI-Fensterchens noch ein neues "UL" mit anhängt). > ../LCD.c:47: warning: implicit declaration of function `delay_us' > ../LCD.c:47: undefined reference to `delay_us' Die Funktionen heißen _delay_us() usw., mit einem führenden Unterstrich, damit sie nicht mit dem application name space kollidieren.
> hat vielleicht jemand ein fertiges hex file das ein Zeichen auf ein > lcd schreibt? Zumindest Bibliotheken dafür gibt's zur Genüge: Procyon AVRlib, Peter Fleury's Codeschnipsel. Die avr-libc kommt auch mit einem Demo, das ein Beispiel für eine LCD-Ansteuerung hat.
jetzt hat ers tatsächlich ohne Warnungen und Fehler kompiliert. LCD funktioniert natürlich immer noch nicht. <Zumindest Bibliotheken dafür gibt's zur Genüge: Procyon AVRlib, Peter Fleury's Codeschnipsel. Problem ist dass die alle die stdio.h benutzen. Wenn ich die einbinde stürtzt AVRStudio ab. Soll wohl einen Patch geben, hab aber nichts dergleichen gefunden.
> Problem ist dass die alle die stdio.h benutzen. Ach wo, du musst doch kein stdio benutzen. Kann ja sein, dass die in ihren Beispielen vielleicht ein sprintf() drin haben, aber das musst du doch nicht nachmachen. Auch eine ,echte' printf-Einbdindung (also fdevopen() oder fdev_setup_stream()) musst du nicht haben, wenn du die Funktionalität (und den Overhead) von stdio nicht nehmen willst. > Wenn ich die einbinde stürtzt AVRStudio ab. Soll wohl einen Patch > geben, hab aber nichts dergleichen gefunden. Oh weh... Installier' dir bitte den letzten Servicepack von AVR Studio. Da sind einige Bugs repariert worden.
Hallo, habe grad noch mal kurz Deine letzte verbesserte Version überflogen und zumindest einen fatalen Fehler gefunden: while(temp & BUSY) muss heißen while(temp & (1 << BUSY)). Gruß Johnny
ups, danke, wars aber nicht. hier nochmal die aktuelle nicht funktionierende Version mit soweit allen berücksichtigten Verbesserungsvorschlägen: #include <avr/io.h> #include <inttypes.h> #include <avr/interrupt.h> #define F_CPU 1000000ul #include <util/delay.h> #define EN 1 #define BUSY 7 #define RW 2 #define RS 3 volatile int temp; int sende(void) { PORTC |= (1 << EN); //enable ein _delay_us(1); PORTC &= ~(1 << EN); //enable aus return 0; } int empfange(void) { PORTC |= (1 << EN); //enable ein _delay_us(1); temp = PINB; PORTC &= ~(1 << EN); //enable aus return 0; } int warte(void) { PORTC &= ~(1 << RS); //RS auf 0 PORTC |= (1 << RW); //RW auf 1 DDRB = 0x00; //PORTB auf Eingang temp |= (1 << BUSY); //BusyFlag setzen while (temp & (1 << BUSY)) //solange BusyFlag gesetzt empfange(); DDRB = 0xFF; //PORTB auf Ausgang PORTC |= (1 << RS); //RS auf 1 PORTC &= ~(1 << RW); //RW auf 0 return 0; } int main(void) { DDRB = 0xff; DDRC = 0xff; PORTC = 0x00; _delay_us(100000); PORTB = 0b00110000; sende(); _delay_us(10000); PORTB = 0b00110000; sende(); _delay_us(10000); PORTB = 0b00110000; sende(); warte(); PORTB = 0b00111000; //Function Set sende(); warte(); PORTB = 0b00001000; //Display off sende(); warte(); PORTB = 0x01; //Display löschen sende(); warte(); PORTB = 0x07; //Entry Mode sende(); warte(); PORTB = 0b00001111; //Display on sende(); warte(); PORTC |= (1 << RS); //RS auf 1 PORTC &= ~(1 << RW); //RW auf 0 warte(); PORTB = 0x23; //# auf LCD ausgeben sende(); while (1) { } return 0; }
Zitat aus der Definition der _delay_us-Funktion in der Header-Datei: The maximal possible delay is 768 us / F_CPU in MHz. D.h. bei 1 MHz maximal 768 µs. 10000 bzw. 100000 ist zu viel. Dafür gibts _delay_ms (max. 262.14 ms / F_CPU in MHz).
So es noch nicht funktioniert ? Ein Paar Jahre vorher ich habe die gleiche mit ein 68HC11 gemacht aber in assembler. Bist du sicher, daß R/W* *RS und E haben die richtiche status ? Zu Schreiben "command": R/W* = 0 E = 1 *RS = 0 So in sende ich wurde schreben: void sende (unsigned char b) { unsigned char portc = PORTB & 0xf8; PORTC = portc; PORTB = b; PORTC = portc | EN; // delay b = b + 1; b = b + 1; b = b + 1; PORTC = portc; } (Bist du sicher, daß die LCD und die PORTB/C sind in Ordnuing ?) Ich mache die gleiche "circuit" zwischen Heute und Morgen, aber ich wurde in assembler programmieren. When meine funktioniert ich kann die Programme senden.
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.