Hallo, ich hätte nochmal eine kurze frage. Nach drücken der Reset Taste an meinem Mega 8 stellt sich unterschiedliches verhalten bezüglich eines LCD dar. Es ist ein HD447 kompatibles display. Nach Reset Funktioniert es meist. Leider tritt ab und zu ein Fehler auf, sodass das Display initialisiert, aber der Cursor nicht blinkt. Teilweise bleiben zeichen stehen. Woran könnte das liegen? Muss das Display ebenso, wie der uc ausgeschaltet werden, damit es richtig funktioniert? Vielen dank
>> Muss das Display ebenso, wie der uc >> ausgeschaltet werden, damit es richtig funktioniert? Na dann sieht man wenigstens die Fehler nicht mehr. Takt ? Software ? Beschaltung ? Gruss K
Hi Sieht nach einer zu kurzen Wartezeit vor der Initialisierung aus. MfG Spess
Unvollständige Initialisierung, Du setzt zu Anfang nicht korrekt in den 8Bit-Modus zurück. Die Sequenz im Datenblatt enthält nicht umsonst "überflüssige" Befehle. 8Bit-Kommandos im 4Bit-Modus oder sogar auf dem falschen Nibble, geht schief. Peter
Okay, also hier mal der Code für die initialisierung. An Port D habe ich eine LED hängen. Damit ich sehe, dass erst ein paar mal geblinkt wird, bevor das LCD initialisiert wird. leider erscheint danach kein cursor. Trotz beseitigung meines verdrahtungsfehlers. R/W liegt auf GND. Aktuell ist der interne 1mhz takt aktiviert (per fuses). Code: #include <avr/io.h> #define F_CPU 1000000UL #include <util/delay.h> int main(void){ int i=0; DDRC=0xff; PORTC=0x00; DDRD=0xff; PORTD=0b00100000; for(i=0;i<5;i++){ PORTD^=(1<<5); _delay_ms(200); } PORTC=0b00000011; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(100); PORTC=0b00000011; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); PORTC=0b00000011; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); /*4 bit*/ PORTC=0b00000010; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); /*2 Zeilen*/ PORTC=0b00000010; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); PORTC=0b00001000; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); /*display aus*/ PORTC=0b00000000; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); PORTC=0b00001000; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); /*löschen*/ PORTC=0b00000000; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); PORTC=0b00000001; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); /*cursor rechts shift*/ PORTC=0b00000000; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); PORTC=0b00000110; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); /*einschalten*/ PORTC=0b00000000; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); PORTC=0b00001100; _delay_ms(1); PORTC|=(1<<4); _delay_ms(1); PORTC&=~(1<<4); _delay_ms(50); while(1){ PORTD^=(1<<5); _delay_ms(1000); }; return 0; }
Mit dem Code macht es echt keinen Spass, da Fehlersuche zu betreiben. Das ist ein Spaghetticode erster Güte. Schau doch mal ins Tutorial, wie man das richtig macht. http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung#Datei_lcd-routines.c: PS: Nachdem du das LCD auf 4-Bit Modus umgestellt hast, musst du jedes Byte (auch die nachfolgenden Konfigurationsbytes) als 2 Nibbles übertragen. Ich kann das in deinem Code nicht erkennen, muss aber zugeben auch nicht so genau analysiert zu haben.
Hallo, entschuldige bitte, ich hatte mir erlaubt, das ganze zusammenzufassen. Hier die Version, die ich zu Anfang hatte. Darin ist erstmal das Initialisieren enthalten. #include <avr/io.h> #define F_CPU 16000000UL #include <util/delay.h> /*PORT B für Display*/ /*R/W auf GND*/ #define EN 4 #define RS 5 #define D4 0 #define D5 1 #define D6 2 #define D7 3 void wartefunktion(void){ _delay_ms(500); void einschalt_verzoegerung(void){ wartefunktion(); } void display_loeschen(void){ PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(0<<D5)|(0<<D6)|(0<<D7); //PORTB=0b00000000; _delay_us(10); PORTB|=(1<<EN); _delay_us(10); PORTB&=~(1<<EN); _delay_us(10); PORTB=(0<<EN)| (0<<RS)|(1<<D4)|(0<<D5)|(0<<D6)|(0<<D7); //PORTB=0b00000001; _delay_us(10); PORTB|=(1<<EN); _delay_us(10); PORTB&=~(1<<EN); _delay_ms(20); } void cursor_rechts(){ PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(0<<D5)|(0<<D6)|(0<<D7); //PORTB=0b00000000; _delay_us(20); PORTB|=(1<<EN); _delay_us(20); PORTB&=~(1<<EN); _delay_us(20); PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(1<<D5)|(1<<D6)|(0<<D7); //PORTB=0b0000011; _delay_us(20); PORTB|=(1<<EN); _delay_us(20); PORTB&=~(1<<EN); _delay_ms(20); } void display_ein(void){ PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(0<<D5)|(0<<D6)|(0<<D7); //PORTB=0b00000000; _delay_us(20); PORTB|=(1<<EN); _delay_us(20); PORTB&=~(1<<EN); _delay_us(20); PORTB=(0<<EN)| (0<<RS)|(1<<D4)|(1<<D5)|(1<<D6)|(1<<D7); //PORTB=0b00001111; _delay_us(20); PORTB|=(1<<EN); _delay_us(20); PORTB&=~(1<<EN); _delay_us(20); } void display_aus(void){ //wartezeiten hier wichtig! PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(0<<D5)|(0<<D6)|(0<<D7); //PORTB=0b00000000; _delay_us(20); PORTB|=(1<<EN); _delay_us(20); PORTB&=~(1<<EN); _delay_us(20); PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(0<<D5)|(0<<D6)|(1<<D7); //PORTB=0b00001000; _delay_us(20); PORTB|=(1<<EN); _delay_us(20); PORTB&=~(1<<EN); _delay_ms(20); } void zwei_zeilig(void){ PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(1<<D5)|(0<<D6)|(0<<D7); //PORTB=0b00000010; _delay_us(10); PORTB|=(1<<EN); _delay_us(10); PORTB&=~(1<<EN); _delay_us(10); PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(0<<D5)|(0<<D6)|(1<<D7); //PORTB=0b00001000; _delay_us(10); PORTB|=(1<<EN); _delay_us(10); PORTB&=~(1<<EN); _delay_ms(20); } void cursor_home(void){ PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(1<<D5)|(0<<D6)|(0<<D7); _delay_ms(1); PORTB|=(1<<EN); _delay_us(1); PORTB&=~(1<<EN); _delay_ms(20); PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(1<<D5)|(0<<D6)|(0<<D7); _delay_ms(1); PORTB|=(1<<EN); _delay_us(5); PORTB&=~(1<<EN); _delay_ms(20); } void display_init(void){ //DDRB|=(1<<EN) | (1<<RS) | (1<<D4) | (1<<D5) | (1<<D6) | (1<<D7); /*first*/ PORTB =(0<<EN) | (0<<RS) | (1<< D4)| (1<<D5) | (0<<D6)| (0<<D7); //PORTB=0b00000011; _delay_us(10); PORTB|=(1<<EN); _delay_us(10); PORTB&=~(1<<EN); _delay_ms(40); /*second*/ PORTB =(0<<EN) | (0<<RS) | (1<< D4)| (1<<D5) | (0<<D6)| (0<<D7); //PORTB=0b00000011; _delay_us(10); PORTB|=(1<<EN); _delay_us(10); PORTB&=~(1<<EN); _delay_ms(20); /*drei*/ PORTB =(0<<EN) | (0<<RS) | (1<< D4)| (1<<D5) | (0<<D6)| (0<<D7); //PORTB=0b00000011; _delay_us(10); PORTB|=(1<<EN); _delay_us(10); PORTB&=~(1<<EN); _delay_us(10); PORTB =(0<<EN) | (0<<RS) | (0<< D4)| (1<<D5) | (0<<D6)| (0<<D7); //PORTB=0b00000010; _delay_us(10); PORTB|=(1<<EN); _delay_us(10); PORTB&=~(1<<EN); _delay_us(10); /*vier*/ zwei_zeilig(); _delay_ms(50); /*fünf*/ display_aus(); _delay_ms(50); /*sechs*/ display_loeschen(); /*sieben*/ _delay_ms(50); cursor_rechts(); /*acht*/ _delay_ms(50); display_ein(); _delay_ms(50); } int main(void){ DDRB=0b00111111; DDRD=0xff; PORTB=0x00; einschalt_verzoegerung(); display_init(); while(1){ }; return 0; }
Anton schrieb: > ich hatte mir erlaubt, das ganze zusammenzufassen. > Hier die Version, die ich zu Anfang hatte. Die ist auch nicht besser, bzw. noch schlimmer. Schreib das doch erst mal einfacher hin. Kein Mensch hat hier Lust deine Bitkonstanten aufzudröseln und sich zusammenzusuchen, was du da eigentlich ans LCD schickst, ob du dich vielleicht irgendwo ein Bit vergessen hast. Funktionen einführen, die Bitkonstanten durch was Lesbares ersetzen. Das Tutorium-Beispiel zeigt dir wie man eine lesbare Struktur hinkriegt welche Aufteilung in Funktionen bzw. Subfunktionen sinnvoll ist.
Du kannst auch mal probieren die Tutorial-Funktionen einfach zu übernehmen. Im Header File die Konfiguration anpassen und dann nachsehen ob die zuverlässiger laufen. Wenn ja, dann ist es irgendwas in deiner Software. Wenn nein, dann wird es wohl deine Hardware sein.
PS: Es muss jetzt natürlich nicht sein, dass dein Code nach der Aufräumarbeit stabiler läuft. Aber zumindest ist es einfacher sich davon zu überzeugen, dass du da keinen Bock geschossen hast.
Hmm, also ich finde das eigentlich garnicht so schlecht lesbar. Prinzipiell, ins port schreiben, enable an/aus, nächster befehl...
Anton schrieb: > Hmm, also ich finde das eigentlich garnicht so schlecht lesbar. Dann bist du nicht sehr anspruchsvoll > > Prinzipiell, > > ins port schreiben, enable an/aus, nächster befehl... Mach dir wenigstens für enable an/aus eine eigene Funktion
1 | void enablePuls() |
2 | {
|
3 | _delay_us(20); |
4 | PORTB|=(1<<EN); |
5 | _delay_us(20); |
6 | PORTB&=~(1<<EN); |
7 | }
|
und schon verkürzt sich der Rest. Zb.:
1 | void display_loeschen(void) |
2 | {
|
3 | PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(0<<D5)|(0<<D6)|(0<<D7); |
4 | //PORTB=0b00000000;
|
5 | enablePuls(); |
6 | _delay_us(10); |
7 | |
8 | PORTB=(0<<EN)| (0<<RS)|(1<<D4)|(0<<D5)|(0<<D6)|(0<<D7); |
9 | //PORTB=0b00000001;
|
10 | enablePuls(); |
11 | _delay_ms(20); |
12 | }
|
13 | |
14 | void cursor_rechts() |
15 | {
|
16 | PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(0<<D5)|(0<<D6)|(0<<D7); |
17 | //PORTB=0b00000000;
|
18 | enablePuls(); |
19 | _delay_us(20); |
20 | |
21 | PORTB=(0<<EN)| (0<<RS)|(0<<D4)|(1<<D5)|(1<<D6)|(0<<D7); |
22 | //PORTB=0b0000011;
|
23 | enablePuls(); |
24 | _delay_ms(20); |
25 | }
|
26 | |
27 | ...
|
Jetzt bedenken wir noch, dass ein Kommando übertragen IMMER nach dem gleichen Muster abläuft: Zuerst das höherwertige Nibble, dann das niederwertige Nibble und schreiben dafür auch noch eine Funktion
1 | void display_command( uint8_t command ) |
2 | {
|
3 | PORTB = ( command & 0xF0 ); |
4 | enablePuls(); |
5 | _delay_us(20); |
6 | |
7 | PORTB = ( command & 0x0F ) << 4; |
8 | enablePuls(); |
9 | _delay_us(20); |
10 | }
|
und schon können die Einzelfunktionen schon wieder kürzer und übersichtlicher geschrieben werden
1 | void display_loeschen(void) |
2 | {
|
3 | display_command( 0b00000001 ); |
4 | _delay_ms(20); |
5 | }
|
6 | |
7 | void cursor_rechts() |
8 | {
|
9 | display_command( 0b00000110 ); |
10 | _delay_ms(20); |
11 | }
|
Für die magischen Konstanten noch ein paar #define
1 | #define LCD_COMMAND_CLEAR 0b00000001
|
2 | #define LCD_COMMAND_CURSOR_RIGHT 0b00000110
|
3 | |
4 | void display_loeschen(void) |
5 | {
|
6 | display_command( LCD_COMMAND_CLEAR ); |
7 | _delay_ms(20); |
8 | }
|
9 | |
10 | void cursor_rechts() |
11 | {
|
12 | display_command( LCD_COMMAND_CURSOR_RIGHT ); |
13 | _delay_ms(20); |
14 | }
|
und jetzt vergleich mal deine Funktionen mit dieser letzten Version. Welche ist leichter zu lesen und zu verstehen? Aus 15 Zeilen Code für display_löschen ist ein schicker 2-Zeiler geworden, den jeder mit einmal Hinsehen verifizieren kann, solange die Konstanten richtig sind display_command richtig geschrieben ist. Und das beste am ganzen: display_löschen ist nicht mehr abhängig davon, wie und wo du dein LCD angeschlossen hast. Ändert sich da was, dann ist die einzige Stelle die Anpassung benötigt, die Funktion display_command bzw. enablePuls. Und die gilt dann automatisch auch für alle anderen Kommando-Funktionen.
Okay, da bin ich jetzt überzeugt :) Ich werde meinen code nochmal überarbeiten. Nebenher: Auf meinem Board gibt es zwei steckplätze. Ich habe das Programm jetzt mal auf einen Mega 8538 geladen. es funktioniert. Das "aufhängen" und stehenlassen von zeichen ist verschwunden. Anscheindn ist etwas mit der Verdrahtung des Sockels nicht in Ordnung? Vielleicht ist auch der Mega8 defekt. Ich werde morgen einen neuen kaufen. Bis dann programmier ich auf dem anderen.
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.