Hallo, Mein LCD funktioniert immer noch nicht. Ich habe eine (angepasste) Displayansteuerung, mache nach der Init-Funktion aber nur einen Port-Test, dass ich messen kann, ob da etwas passiert. Wenn ich die init Funktion auskommentiere, funktioniert mein Port-Test. Wenn sie nicht auskommentiert ist, passiert einfach nichts. Als würde der µC irgendwo drin "hängen". In der Simulation mit AVR Studio funktioniert alles, die Ports ändern ihren Wert, nur nicht in echt, außer wenn ich init weg lasse. Das Programm kompiliert ohne Fehler. Die Initialisierung an sich funktioniert (schwarzer Balken verschwindet), nur kann ich keinen Text anzeigen lassen, was mich dazu gebracht hat, erst einmal die Ports zu testen. Ich benutze einen Atmega32. Ich habe die lcd.c vorübergehend in main.c gepackt.
>void lcd_data( uint8_t data ) { > LCD_CTRL |= (1<<LCD_RS); // RS auf 1 setzen > lcd_out( data ); // zuerst die oberen, > lcd_out( data<<4 ); // dann die unteren 4 Bit senden > _delay_us( LCD_WRITEDATA_US ); >} static void lcd_out( uint8_t data ) { >// data &= 0xF0; // obere 4 Bit maskieren >// LCD_PORT &= ~(0xF0>>(4-LCD_DB)); // Maske löschen >// LCD_PORT |= (data>>(4-LCD_DB)); // Bits setzen > LCD_PORT = data; // Bits setzen > lcd_enable(); >} Also ich denke da ist der Wurm drinne. ich würde es so mal machen! Erst mal das Highnibble ins Low verschieben und Ausmaskieren und dann Senden. int data2 data2 = (data >> 4); data2 &= 0x0F; LCD_PORT = data2; Danach einfach data2 = (data & 0x0F); und das Lowniblle senden, Wenn du deine lcd_out und LCD_Data so anpasst brauchst nur das Datenbyte übergeben.
Ohh noch was endeckt! Warum benutzt du nicht einen PORT wenn du schon in 4-Bit Modus arbeitest. >// 4 Bit LCD Datenbus DB4-DB7, das unterste Bit DB4 kann auf den Portbits >0..4 liegen >// LCD DB4-DB7 <--> PORTC Bit PC0-PC3 >#define LCD_DB PC4 >// LCD Steuersignale RS und EN >// LCD RS <--> PORTC Bit PC4 (RS: 0=Data, 1=Command) >#define LCD_RS PD3 >// LCD EN <--> PORTC Bit PC5 (EN: 1-Impuls für Daten) >#define LCD_EN PD4 ich meine Du könntest es auch so definieren. #define LCD_PORT PORTB #define LCD_DDR DDRB #define LCD_PIN PINB #define LCD_RS 0 #define LCD_RW 1 #define LCD_EN 2 #define LCD_D4 3 #define LCD_D5 4 #define LCD_D6 5 #define LCD_D7 6
Was steht denn als letztes auf dem LCD_PORT?
Hallo Simon! Diese Anfangsadressen der 2 und 3 Zeile bitte auch noch anpassen #define LCD_DDADR_LINE3 0x10 muss 0x14 #define LCD_DDADR_LINE4 0x50 muss 0x54 sein.
vielen Dank für die ausführlichen Antworten. meine lcd_data sieht jetzt so aus: void lcd_data( uint8_t data ) { LCD_CTRL |= (1<<LCD_RS); // RS auf 1 setzen int data2; data2 = (data >> 4); data2 &= 0x0F; LCD_PORT = data2; data2 = (data & 0x0F); LCD_PORT = data2; _delay_us( LCD_WRITEDATA_US ); } und meine lcd_out: static void lcd_out( uint8_t data ) { int data2; data2 = (data >> 4); data2 &= 0x0F; LCD_PORT = data2; data2 = (data & 0x0F); LCD_PORT = data2; } War's so gemeint? Was ist mit lcd_command? Dort das gleiche auch nehme ich an. Die Ports habe ich so definiert: #define LCD_D4 4 #define LCD_D5 5 #define LCD_D6 6 #define LCD_D7 7 Ich benutze getrennte Ports für Daten und Steuerung, weil ich mir zu wenig Gedanken über die Software gemacht habe, als ich die Hardware entwickelt habe... Die Anfangsadressen habe ich auch angepasst. Zwischenzeitlich hatte ich mal ein undefinierbares Zeichn und einen blinkenden Cursor, mehr tat sich noch nicht.
Moin Simon, wo sind in den zuletzt geposteten Funktionen denn die Aufrufe von lcd_enable geblieben? Ohne die kriegt der Controller das Bitmuster am LCD_PORT ja gar nicht mit. Außerdem maskierst Du jetzt die oberen Bits weg statt die unteren. Ich glaube nicht, dass so noch irgendwelche Daten rüberkommen ;-) Also ich würde die lcd_out so lassen, wie Du sie zuerst hattest, und die lcd_data und lcd_command auch. Und auch Deine lcd_init sieht für mich so weit ok aus, die extra ms-Delays nach dem dritten Soft-Reset und dem Switch auf 4bit können wohl entfallen, sollten aber nicht wirklich stören. Ich beziehe mich dabei einerseits auf das Datenblatt des HD44780U, das bei alldatasheet.com zu finden ist, und andererseits auf meine eigene Implementierung dieses Controllers, die ich vor Jahren in Assembler geschrieben habe, und mit der ich bisher jedes Character-LCD von 1 bis 4 Zeilen je 16 bis 40 Zeichen im 4bit-Modus auf Anhieb zum Spielen gekriegt habe. Ich denke daher, dass Dein eigentliches Problem, nämlich dass Dein Porttest nicht funktioniert, nichts mit den Ausgaben an das LCD zu tun hat, sondern mit etwas anderem. Und da bleiben praktisch nur die gcc includes, die Du verwendest. Ich würde da mal nachsehen...
wahlostfriese schrieb: > Also ich würde die lcd_out so lassen, wie Du sie zuerst hattest, aber bitte so, wie es im Original stand. Nicht so wie er es geschrieben hat. Dafür darf dann in der lcd_init ruhig auch der Port für die Control-Signale auf Ausgang gestellt werden. Aber bitte auch nicht wieder so, wie er es bei den Datenleitungen gemacht hat, sondern so wie es im Original stand. Keine Rundumschlag-Port Veränderungen, sondern mit der feinen Klinge nur die Bits beeinflussen, die tatsächlich gebraucht werden. Es fragt sich, warum er da überhaupt geändert hat. So wie das aussieht, steht er noch auf der Stufe des Faustkeils und arbeitet noch nicht mit der feinen Klinge, die auch ein paar Eventualitäten berücksichtigt an die der Faustkeilträger nicht denkt. > Ich benutze getrennte Ports für Daten und Steuerung, weil ich mir > zu wenig Gedanken über die Software gemacht habe, als ich die > Hardware entwickelt habe... Solange du die 4 Datenleitungen an einem Port gelassen hast und die auch auf aufeinanderfolgenden Pins liegen, ist das kein Problem. Die Steuerleitungen aus dem Originalcode 'rauszuoperieren' ist nicht das große Problem. Aber du musst das ein bißchen besser angehen, als du das getan hast. Du hast einfach nur draufgehauen und dabei mehr zerstört als du gut gemacht hast.
Ich weiß jetzt nicht, was du an den Originalroutinen noch so alles geändert hast. Aber ich würde mal sagen: Geh zurück zu den Originalroutinen und pass die nochmal neu an. Zurück an den Start. Aber diesmal mit etwas mehr Sachverstand! Im Original steht da ....
1 | #define LCD_PORT PORTD
|
2 | #define LCD_DDR DDRD
|
... weil alle Leitungen über denselben Port abgewickelt werden. Das geht bei dir natürlich nicht. Die Steuerleitungen brauchen ihren eigenen Port. Also führen wir den gleich mal ein
1 | #define LCD_PORT PORTD
|
2 | #define LCD_DDR DDRD
|
3 | |
4 | #define LCD_CTRL_PORT PORTD
|
5 | #define LCD_CTRL_DDR DDRD
|
Wenn ein Port dazukommt, muss man sich sofort fragen: Wie und wo wird der auf die Datenrichtungen eingestellt, wo wird er initialisiert? Im Original ist das in der init() Funktion. Da steht:
1 | void lcd_init( void ) |
2 | {
|
3 | // verwendete Pins auf Ausgang schalten
|
4 | uint8_t pins = (0x0F << LCD_DB) | // 4 Datenleitungen |
5 | (1<<LCD_RS) | // R/S Leitung |
6 | (1<<LCD_EN); // Enable Leitung |
7 | LCD_DDR |= pins; |
8 | ...
|
d.h. da werden alle am Port LCD_DDR benutzen Leitungen (inklusive Steuerleitungen) auf Ausgang gestellt. Das geht bei dir natürlich nicht mehr. Bei dir sind die Steuerleitungen getrennt und es gibt ein #define dafür. Also trennst du die Dinge auf. Alles was mit den Datenleitungen zu tun hat, bleibt so wie es ist, nur die Steuerleitungen werden rausoperiert.
1 | void lcd_init( void ) |
2 | {
|
3 | LCD_DDR |= (0x0F << LCD_DB); |
4 | LCD_CTRL_DDR |= (1<<LCD_RS) | (1<<LCD_EN); |
5 | ...
|
so geschrieben passiert immer noch das gleiche, wenn Datenport und Controlport identisch sind. Wenn sie es aber nicht sind, dann berücksicht das dieser Code. Wie gehts in der FUnktion weiter?
1 | // initial alle Ausgänge auf Null
|
2 | LCD_PORT &= ~pins; |
ok. normalerweise braucht das keiner. Es schadert aber auch nichts. Aber auch hier wieder. Den Anteil der Datenbits von den Steuerleitungen trennen. Die Datenbits werden behandelt wie bisher, die Steuerleitungen gehen auf ihren eigenen Port
1 | // initial alle Ausgänge auf Null
|
2 | LCD_PORT &= ~(0x0F << LCD_DB); |
3 | LCD_CTRL_PORT &= ~((1<<LCD_RS) | (1<<LCD_EN)); |
Damit ist die Intialisierung soweit geändert, dass die Steuerleitungen auf ihren eigenen Port laufen. Wo muss noch geändert werden? Zu diesem Zwecke geht man jetzt alle Zugriffe auf LCD_PORT durch und stellt sich die Frage: Womit haben wir es hier zu tun? Geht es hier um die Datenleitungen oder um die Steuerleitungen. Alles was mit Datenleitungen zu tun hat, BLEIBT SO WIE ES IST! Alles was mit Steuerleitungen zu tun hat, wird geändert. Falls es einen Zugriff auf LCD_PORT gibt, in dem beide Teilbereiche gleichzeitig beeinflusst werden, muss man die Dinge auseinanderdividieren. Aber schaun wir mal. Erste Funktion
1 | static void lcd_enable( void ) |
2 | {
|
3 | LCD_PORT |= (1<<LCD_EN); // Enable auf 1 setzen |
4 | _delay_us( LCD_ENABLE_US ); // kurze Pause |
5 | LCD_PORT &= ~(1<<LCD_EN); // Enable auf 0 setzen |
6 | }
|
Datenleitungen oder Steuerleitungen? Ganz klar Steuerleitungen. Also wird geändert
1 | static void lcd_enable( void ) |
2 | {
|
3 | LCD_CTRL_PORT |= (1<<LCD_EN); // Enable auf 1 setzen |
4 | _delay_us( LCD_ENABLE_US ); // kurze Pause |
5 | LCD_CTRL_PORT &= ~(1<<LCD_EN); // Enable auf 0 setzen |
6 | }
|
Nächste Funktion
1 | static void lcd_out( uint8_t data ) |
2 | {
|
3 | data &= 0xF0; // obere 4 Bit maskieren |
4 | |
5 | LCD_PORT &= ~(0xF0>>(4-LCD_DB)); // Maske löschen |
6 | LCD_PORT |= (data>>(4-LCD_DB)); // Bits setzen |
7 | lcd_enable(); |
8 | }
|
Daten oder Steuer? Ganz klar Daten. Und zwar nur Daten. Ergo: Die Funktion bleibt so wie sie ist! Nächste Funktion
1 | void lcd_data( uint8_t data ) |
2 | {
|
3 | LCD_PORT |= (1<<LCD_RS); // RS auf 1 setzen |
4 | |
5 | lcd_out( data ); // zuerst die oberen, |
6 | lcd_out( data<<4 ); // dann die unteren 4 Bit senden |
7 | |
8 | _delay_us( LCD_WRITEDATA_US ); |
9 | }
|
Der direkte Portzugriff: Daten oder Steuer? Ganz klar Steuer. Also wird geändert
1 | void lcd_data( uint8_t data ) |
2 | {
|
3 | LCD_CTRL_PORT |= (1<<LCD_RS); // RS auf 1 setzen |
4 | |
5 | lcd_out( data ); // zuerst die oberen, |
6 | lcd_out( data<<4 ); // dann die unteren 4 Bit senden |
7 | |
8 | _delay_us( LCD_WRITEDATA_US ); |
9 | }
|
Nächste
1 | void lcd_command( uint8_t data ) |
2 | {
|
3 | LCD_PORT &= ~(1<<LCD_RS); // RS auf 0 setzen |
4 | |
5 | lcd_out( data ); // zuerst die oberen, |
6 | lcd_out( data<<4 ); // dann die unteren 4 Bit senden |
7 | |
8 | _delay_us( LCD_COMMAND_US ); |
9 | }
|
same type, selbes Spiel. Ganz klar Steuer. Also...
1 | void lcd_command( uint8_t data ) |
2 | {
|
3 | LCD_CTRL_PORT &= ~(1<<LCD_RS); // RS auf 0 setzen |
4 | |
5 | lcd_out( data ); // zuerst die oberen, |
6 | lcd_out( data<<4 ); // dann die unteren 4 Bit senden |
7 | |
8 | _delay_us( LCD_COMMAND_US ); |
9 | }
|
Nächster direkter Portzugriff ... es gibt keinen mehr. Damit wurden im Code die Datenleitungen von den Steuerleitungen getrennt. Wenn dein LCD nicht noch im Timingbereich ein Problem hat, müsste es jetzt reichen, wenn du im Header File hier
1 | // Konfiguration für die Datenleitungen
|
2 | #define LCD_PORT PORTD
|
3 | #define LCD_DDR DDRD
|
4 | |
5 | // LCD DB4-DB7 <--> LCD_PORT Bit PD0-PD3
|
6 | #define LCD_DB PD0
|
7 | |
8 | // Konfiguration für die Steuerleitungen
|
9 | #define LCD_CTRL_PORT PORTD
|
10 | #define LCD_CTRL_DDR DDRD
|
11 | |
12 | // LCD RS <--> LCD_CTRL_PORT Bit PD4 (RS: 1=Data, 0=Command)
|
13 | #define LCD_RS PD4
|
14 | |
15 | // LCD EN <--> LCD_CTRL_PORT Bit PD5 (EN: 1-Impuls für Daten)
|
16 | #define LCD_EN PD5
|
deine korrekte Konfiguration einträgst und alles ist paletti. -> Wenn du in deinen Programmier-Studien weiterkommen willst, dann MUSST du weg von direkten kompletten Portzugriffen! Einzelbits setzen und Einzelbits löschen dürfen dir kein Kopfkratzen verursachen! Du musst wissen, wie man mit |= bzw. &= einzelne Bits in einem Register gezielt beeinflusst, ohne das restliche Register zu zerstören. Das muss im Schlaf um 3 Uhr morgens nach dem Aufwecken zu 100% sicher klappen! Und wenn es sein muss auch unter Sauerstoffmangel im Handstand auf dem Gipfel des Mt. Everest. Sowohl lesend (bei der Analyse von fremdem Code) als auch schreibend (wenn man selbst programmiert). Alle Vorübungen in Büchern und Tutorien mit LED (LED ein, LED aus, blinkende LED, LED-Muster) bzw. Tastern zielen nur darauf ab, dem Einsteiger in diesem Bereich eine gewisse Grundfertigkeit zu vermitteln. Die LED ist nicht das Interessante, das Interessante ist die Bitmanipulation, mit der man dem einen Portpin seinen Willen aufzwingt. Und zwar nur diesem EINEN Portpin.
Und wenn du dann in main die DDR Register, die zum LCD gehören erst mal in Ruhe lässt, und nicht mit Rundumschlag aktionen ala while(1) { ... DDRC = 0xf0; eingreifst, dann funktioniert das auch. Im AVR-Studio hast du die Taktfrequenz deines µC korrekt eingetragen (bzw. im Makefile korrekt gesetzt) und auch kontrolliert ob der µC auch wirklich mit dieser Frequenz läuft? Wenn diese Werte nicht zusammenstimmen, dann arbeiten alle _delay_xx Funktionen nicht richtig und das Timing stimmt nicht mehr. Und da du gesagt hats, du benutzt einen Mega32: Das JTAG Interface am Port C hast du per Fuse abgeschaltet?
Hallo zusammen, vielen Dank an alle Helfer. Ich habe meinen Atmega32 ausgewechselt mit einem Atmega644p (der war noch da). Denn, als ich mit dem Oszi gemessen habe und festgestellt habe, dass wenn ich an einem Pin messe und gleichzeitig die Spitze berühre, die Ausgangsspannung sich verändert, obwohl die Pins (im Gegensatz zu mir) niederohmig sein müssen. Musste also am µC liegen! Da ich ohne Berührung korrekte Signale messen konnte, habe ich das nie bemerkt. Und siehe da, es funktioniert mit einem Atmega644p! :) Wenn ich den Code etwas aufgeräumt habe, kann ich ihn auch gerne nochmal posten, falls jemand Interesse hat.
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.