Hallo zusammen, ich komme leider nicht weiter beim Betrieb eines DOG162 Display über SPI und den gleichzeitigen Betrieb eines Sensors, der auch über SPI an einen Atmega 168 hängt. Das Problem ist, dass das Display nur ab und zu initialisiert. Ohne einen mir bekannten Grund erscheinen am Dispay Zeichen, oder es bleibt leer. Nach dem Neuflashen des Programms scheint es grundsätzlich nach Neustart häufiger zu initialisieren, aber auch nicht immer. Der Code für die Init: void InitSPI (void) { /* Set MOSI and SCK output, andere input */ DDR_SPI |= (1<<DD_MOSI)|(1<<DD_SCK); _delay_ms(40); // warte auf Display /* aktiviere SPI, Master, setze clock rate fck/16 */ SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR0); // Setze CSB als Ausgang und low DD_CSB |= (1<<PIN_CSB); PORT_CSB &= ~(1 << PIN_CSB); // Setze RS-Pin als Ausgang und low DD_RS |= (1<<PIN_RS); PORT_RS &= ~(1 << PIN_RS); _delay_us(50); // warte auf Display } Was kann der Grund sein, dass das Display nur manchmal funktioniert ? Es kann ja nichts grundsätzlich falsch sein. Hattet ihr auch schon mal das gleich Problem ?
Wenn dein Sensor und dein LCD andere SPI modi benutzen hast du ein Problem, dann überschreibt deine Sensorinit deine LCD einstellungen und das LCD versteht nix mehr und umgekehrt beim Sensor natürlich auch. Das geht nur gut wenn beide den gleichen SPI Modus benutzen, ansonsten setz mal immer ein Init bevor du den jeweiligen slave benutzen willst.
Muss ich dann für das Display und den Sensor eine Init_SPI im Programm vorsehe, oder reicht es aus, wenn ich die SPI nur einmal initialisiere ?
>dass das Display nur manchmal funktioniert ?
Timing Probleme, konkurierende SPI-Modi etc...
>Muss ich dann für das Display und den Sensor eine Init_SPI im Programm >vorsehe, oder reicht es aus, wenn ich die SPI nur einmal initialisiere ? Das meinte doch Stefan, wenn du unterschiedliche SPI-Modi für das Display und den Sensor nutzt, musst du den jeweiligen vorher durch aufruf von spiinit wiederherstellen. pseudo code:
1 | init_spi_display(); |
2 | draw_display(); |
3 | ...
|
4 | |
5 | init_spi_sensor(); |
6 | read_sensor(); |
7 | ...
|
8 | |
9 | init_spi_display(); |
10 | draw_display(); |
11 | ...
|
Also vor der Verwendung jeweils initialisieren. Gruß J
:
Bearbeitet durch User
jup so wies Jonas gesagt hat. Mal prinzipiell: Fehlersuche du lässt NUR das LCD laufen, falls keine Probleme auftauchen, sensor dazunehmen, falls Probleme auftreten Sensor wieder wegnehem, aus Code und testen. Für Sensor eben dasselbe machen, simples Ausschlussprinzip, da muss man nichtmal das Hirn anschalten, man macht nur Haken bie ufnktionierendem und Kreuze für Fehler, so kann man immer vorgehen, klappt jedesmal bis zur Fehlerursache.
Jonas B. schrieb: > Timing Probleme, konkurierende SPI-Modi etc... Timing Probleme heißt, dass die Delay-Schleifen zum Warten zu kurz/lang sind ?
Stefan S. schrieb: > Mal prinzipiell: Fehlersuche Ich habe, um den Fehlerfall konkurrierende SPI-Modi auszuschließen, die SPI-Init für den Sensor ausgeklammert. Das Display funktioniert leider immer noch nicht, es muss also was mit der Init des Display selbst zu tun haben.
nicht ganz unwichtige Information :) mach ruhig weiter du findest das schon
>Timing Probleme heißt, dass die Delay-Schleifen zum Warten zu kurz/lang >sind ? Zu lang gibt es hier quasi nicht, nur zu kurz. Also mal länger machen... Gruß J
Ich habe se eben mit einen neuen Display ausprobiert, hier erscheinen nun das gewollte. Jetzt könnte man folgern, dass das Display kaputt ist, da ich heute Vormittag ähnliches festgestellt habe. Aber es kann doch nicht sein, dass man durch falsche SPI-Initialisierung das Display schrottet, oder ? Zumal das neue Display wohl auch nur eine Zeit funktionieren wird, weil's heute schon mal so war. Ich bin echt am Verzweifeln...
@Michael, das Chipselect (CSB) ist doch L-aktiv, warum setzt du es dann schon in der Init auf L? Das gehört doch dann erst in die Transferfunktion. Wenn du nach dem Init was an deinen Sensor sendest wird das auch vom Display ausgewertet und wer weiß was das dann macht. Sascha
Sascha W. schrieb: > das Chipselect (CSB) ist doch L-aktiv, warum setzt du es dann schon in > der Init auf L? Das gehört doch dann erst in die Transferfunktion. Danke für den Hinweis, ich hab's in der Init auskommentiert. Meine Transferfunktion (String-senden) schaut aktuell so aus: void Disp_String(char *cData) { _delay_us(50); // warte auf Display // Data Mode vorbeireiten; setze RS high PORT_RS |= (1 << PIN_RS); while (*cData) { /* Starte übertragung */ SPDR = *cData; _delay_us(100); // warte auf Display /* Warte auf ende der Übertragung */ while(!(SPSR & (1<<SPIF))); //Übertragung abschließen durch RS toggle PORT_RS &= ~(1 << PIN_RS); //low PORT_RS |= (1 << PIN_RS); // high _delay_us(50); // warte auf Display //while (*cData != 0) ++cData; } } Den Chipselect verändere ich hier gar nicht mehr, und funktioniert trotz Ausklammerung in der Init trotzdem, leider nur mit dem neuen Display. Ich nehme an, dass diese wirklich defekt sind. Wie kann "Disp_String" überhaupt funktionieren, wenn CS nicht auf low geschaltet ist ?
Michael schrieb: > Wie kann "Disp_String" überhaupt funktionieren, wenn CS nicht auf low > geschaltet ist ? Sorry, ich hab die Lösung selbst gefunden, ich ziehe vor jeder Messung des Sensors den CS-Port (PB0) manuell nach unten...
CS muss ja low sein, damit der µC mit dem Display/Sensor kommunizieren kann. Was macht eigentlich dann der RS ?
Jetzt funktionieren auch die defekten Displays wieder ! Bei Verwendung folgender Init: void InitSPI (void) { /* Set MOSI and SCK output, andere input */ //DDR_SPI |= (1<<DD_MOSI)|(1<<DD_SCK); /* aktiviere SPI, Master, setze clock rate fck/16 */ //SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR0); // Setze CSB als Ausgang und low DD_CSB |= (1<<PIN_CSB); // Setze RS-Pin als Ausgang und low DD_RS |= (1<<PIN_RS); _delay_us(50); // warte auf Display } Sowohl CSB und RS werden als Ausgang definiert, aber weder hoch noch runter gezogen.
Michael schrieb: > Jetzt funktionieren auch die defekten Displays wieder ! Mist, doch nicht, da muss einanderer Effekt dahinter stehen. Hab die Displays nach zwei Minuten wieder angesteckt, und schon kommt wieder nix.
Michael schrieb: > Hab die Displays nach zwei Minuten wieder angesteckt, und schon kommt > wieder nix. Hast Du mit dem Timing schon gespielt? Wichtig ist (oft) auch, dass nach Power-On genügend lange gewartet wird bis zur ersten Ansprache. Die Spannung am Display muss lange genug stabil anliegen, bevor das Programm anfängt. Gruß Dietrich
Ein Delay vor den SPI-Init ? Ich habe eben 1000ms als Warteschleife davor. Bleibt leider weiterhin Zufall ob das Display geht oder nicht.
Wie sieht denn deine Display Initialisierung aus ? Gruß JackFrost
Ist PB2/SS bei dir ein Ausgang? Wenn nicht dann mach PB2 zum Ausgang. Und das BEVOR du das SPI auf Master initialisierst.
Ist etwas länger her und ein ATMega164A mit dem DOM162, das funktioniert aber: Beitrag "DOGM162 an ATMega164PA"
Bastian W. schrieb: > Wie sieht denn deine Display Initialisierung aus ? Die sieht aktuell so aus: void InitDisp(void) { _delay_ms(300); // Warte auf Display int BIAS,PWR,FOLLOW; if (VOLTAGE == 5) { BIAS = 0x1c; PWR = 0x51; FOLLOW = 0x6a; } else { BIAS = 0x14; PWR = 0x55; FOLLOW = 0x6d; } // Function Set 0 0 1 DL N DH IS2 IS1 // DL: interface data is 8/4 bits // N: number of line is 2/1 // DH: double height font // IS[2:1]: instruction table select Disp_Command(0x39); // Function Set ; 8 Bit; 1Zeile, Istr.Tab 1 0x31 _delay_ms(10); // Auf Display warten Disp_Command(BIAS); // Bias Set _delay_ms(10); // Auf Display warten Disp_Command(PWR); // Power Control _delay_ms(10); // Auf Display warten Disp_Command(0x6a); // Follower Control _delay_ms(10); // Auf Display warten Disp_Command(CONTRAST); // Kontrast setzten _delay_ms(10); // Auf Display warten Disp_Command(0x38); // Instruction Table 0 setzten 0x30 _delay_ms(10); // Auf Display warten Disp_Command(0x0c); // Display ein, kein Cursor, kein Blinken Disp_Command(0x01); // Disoplay löschen _delay_ms(25); // Auf Display warten Disp_Command(0x06); // Entry Mode set - Display wartet auf Eingabe _delay_ms(15); // Auf Display warten PORT_RS |= (1 << PIN_RS); //auf high funktionierts besser }
holger schrieb: > Ist PB2/SS bei dir ein Ausgang? Wenn nicht dann > mach PB2 zum Ausgang. Und das BEVOR du das SPI > auf Master initialisierst. Ich habe PB2/SS auf Ausgang definiert. Leider bringt das keine Verbesserung. void InitSPI (void) { _delay_us(500); // warte auf Display DDRB |= ( 1 << DDB2 ); //PB2/SS als Ausgang /* Set MOSI and SCK output, andere input */ DDR_SPI |= (1<<DD_MOSI)|(1<<DD_SCK); /* aktiviere SPI, Master, setze clock rate fck/16 */ //SPCR |= (1<<SPE)|(1<<MSTR); // Setze CSB als Ausgang und low DD_CSB |= (1<<PIN_CSB); //PORT_CSB &= ~(1 << PIN_CSB); //PORT_CSB |= (1 << PIN_CSB); // Setze RS-Pin als Ausgang und low DD_RS |= (1<<PIN_RS); //PORT_RS &= ~(1 << PIN_RS); _delay_us(500); // warte auf Display }
Hallo, mit RS schaltest du zwischen Daten und Kommando um. Wenn du am SPI noch einen Sensor hast, kannst du CSB nicht dauerhaft auf Low lassen. Jeder Slave am SPI muss einen eigenen CS haben und logischer Weise darf immer nur einer aus Low sein! Sascha
Rudolph R. schrieb: > Ist etwas länger her und ein ATMega164A mit dem DOM162, das funktioniert > aber:... Bei der dortigen SPI-Initialisierung wird RS und CS auf low gezogen, um etwas zu schreiben, oder ? Der Beispielcode, auf den ich mich beziehe (Beitrag "EA DOGM162 an SPI"), zieht RS nach oben um etwas zu schreiben. Was ist denn der richtige Weg ? // init 2x16 display void display_init(void) { PORTB &= ~(1<<PB4); // Display auswählen PORTB &= ~(1<<PB3); // RS auf Low -> Kommandos SPDR0 = 0x39; // Function-Set: 8 Bit, 2 Zeilen, Befehlstabelle 1 while(!(SPSR0 & (1<<SPIF0))); SPDR0 = 0x1c; // Bias Set: 1/4, 2-zeiliges Display while(!(SPSR0 & (1<<SPIF0))); SPDR0 = 0x52; // Power Control: Booster aus, Kontrast C5, C4 setzen while(!(SPSR0 & (1<<SPIF0))); SPDR0 = 0x69; // Follower Control: Spannungsfolger und Verstärkung setzen while(!(SPSR0 & (1<<SPIF0))); SPDR0 = 0x74; // Contrast Set: Kontrast C2 setzen while(!(SPSR0 & (1<<SPIF0))); SPDR0 = 0x0c; // Display ON/OFF: Display einschalten while(!(SPSR0 & (1<<SPIF0))); SPDR0 = 0x06; // Entry Modet Set: Auto-Inkrement while(!(SPSR0 & (1<<SPIF0))); PORTB |= (1<<PB4); // Display abwählen }
Sascha W. schrieb: > mit RS schaltest du zwischen Daten und Kommando um. Wenn du am SPI noch > einen Sensor hast, kannst du CSB nicht dauerhaft auf Low lassen. Jeder > Slave am SPI muss einen eigenen CS haben und logischer Weise darf immer > nur einer aus Low sein! CSB wird verwendet, um den entsprechenden SPI-Slave auszuwählen (auf low wenn angespcohen), chipselect eben, das habe ich verstanden. Der Beispielcode geht wohl davon aus, dass nur das Display am µC hängt, deshalb das Dauer-Low bei Init. Das mit RS habe ich noch nicht kapiert. Wird RS genauso wie das CS programmiert, aber nur invertiert ?
Wenn ich bei der Initialisierung das Dauer-high vom CSB für das Display weglassen muss, müsste ich bei Disp_Command ja dann immer den CSB nach unten, und dann wieder nach oben ziehen, so wie hier, oder ? Aber wozu wird der RS benötigt und warum wird der getoggelt ? void Disp_Command(int Data) { PORT_CSB &= ~(1 << PIN_CSB); _delay_us(50); // warte auf Display // Command Mode vorbeireiten; setze RS low PORT_RS &= ~(1 << PIN_RS); /* Starte übertragung */ SPDR = Data; /* Warte auf ende der Übertragung */ while(!(SPSR & (1<<SPIF))); //Übertragung abschließen durch RS toggle PORT_RS |= (1 << PIN_RS); // high _delay_us(5); PORT_RS &= ~(1 << PIN_RS); //low _delay_us(50); // warte auf Display PORT_CSB |= (1 << PIN_CSB); }
Michael schrieb: > Wenn ich bei der Initialisierung das Dauer-high vom CSB für das Display > weglassen muss, müsste ich bei Disp_Command ja dann immer den CSB nach > unten, und dann wieder nach oben ziehen, so wie hier, oder ? genau - so ist das nun mal bei SPI > Aber wozu wird der RS benötigt und warum wird der getoggelt ? Datenblatt? RS=L Command / RS=H Data was das togglen hier soll kann ich dir auch nicht sagen, bei SPI wird die Übertragung nach den 8 übertragenen Bits abgeschlossen bzw. mit CS=H > void Disp_Command(int Data) > { > PORT_CSB &= ~(1 << PIN_CSB); > _delay_us(50); // warte auf Display > > // Command Mode vorbeireiten; setze RS low > PORT_RS &= ~(1 << PIN_RS); > > /* Starte übertragung */ > SPDR = Data; > /* Warte auf ende der Übertragung */ > while(!(SPSR & (1<<SPIF))); > > //Übertragung abschließen durch RS toggle > PORT_RS |= (1 << PIN_RS); // high > _delay_us(5); > PORT_RS &= ~(1 << PIN_RS); //low > > _delay_us(50); // warte auf Display > PORT_CSB |= (1 << PIN_CSB); > } Wie sieht denn die Funktion füt Disp_Data aus dem Beispiel aus? Sascha
Laut Datenblatt für den ST7036 wird bei der Initialisierung der Code für Functionset zweimal gesendet und es sollte nach Follower control >200 ms gewartet werden. Seite 43 vom Datenblatt http://www.lcd-module.de/eng/pdf/zubehoer/st7036.pdf Du kannst deine Inititalisierung ja mal so aufbauen und schauen ob es besser geht. Gruß JackFrost
Sascha W. schrieb: > Wie sieht denn die Funktion füt Disp_Data aus dem Beispiel aus? Hier die Funktion für den String_Übertrag: void Disp_String(char *cData) { _delay_us(50); // warte auf Display // Data Mode vorbeireiten; setze RS high PORT_RS |= (1 << PIN_RS); while (*cData) { /* Starte übertragung */ SPDR = *cData; _delay_us(100); // warte auf Display /* Warte auf ende der Übertragung */ while(!(SPSR & (1<<SPIF))); //Übertragung abschließen durch RS toggle PORT_RS &= ~(1 << PIN_RS); //low PORT_RS |= (1 << PIN_RS); // high _delay_us(50); // warte auf Display //while (*cData != 0) ++cData; } }
Ist es vielleicht möglich, dass das 3,3V Display nicht mit 5V Pegeln am SPI-Bus zurecht kommt ? Das würde erklären, dass die Initialisierung bei einem neuen Display gut funktioniert und bei den älteren nur sporadisch ?
Michael schrieb: > Der Beispielcode geht wohl davon aus, dass nur das Display am µC hängt, > deshalb das Dauer-Low bei Init. Nicht so ganz, auch mit xx SPI slaves würde mein init so aussehen. Es soll in dem Moment nichts anderes auf dem SPI passieren, es gibt also keinen Grund, CS bei jedem einzelnen Byte wieder auf High zu setzen. Die Daten werden einfach mit den Takten übernommen, man kann quasi beliebig viele Bytes auf einmal in das LCD schreiben. Dennoch sollte man CS nicht dauerhaft auf Low lassen, weil das auch mal durcheinander kommen könnte, wird da ein Bit verschluckt ist alles danach nur noch Bit-Brei.
wer bein den oben gezeigten Beispielen ein toggeln von RS eingebaut hat kann offenbar auch das Datenblatt nicht lesen. Im dort dargestellten Timingdiagramm ist nur zu sehen das RS ab dem letzten Bit eines Bytes stabil sein muss. Insofern könnten die Funktionen schon kritisch sein da sofort nach dem Transferende RS umgeschaltet wird. Lt. Datenblatt sollte RS nach dem Byte noch 150-250ns (je nach Ub) stabil sein. Bei 16MHz könnte das so schon kritisch sein. Sascha
Nach dem letzten Bit ein Delay einfügen wär dann die Lösung ?
nicht nötig, nur das toggeln weglassen. Also: (Empfehlung) Command Parameter 1x Byte 1. RS low 2. CSB low 3. Datenbyte senden 4. 2 nop 5. CSB high Data Parameter String/Char-Ptr 1. RS high 2. CSB low 3. alle Bytes senden 4. 2 nop 5. CSB high Sascha
Michael schrieb: > Sascha W. schrieb: >> 2 nop > > Damit ist ein Delay gemeint ? Ja, du kannst auch ein Delay nehmen doch das wird eigentlich unnötig lang. Aber probiers ruhig mal auch mit ein paar μs - schaden tuts nicht. Sascha
Hi >Also: (Empfehlung) Meine Empfehlung ist einen aktuellen AVR zu benutzen, der UART im SPI-Mode besitzt. Da kann man den TX-Complete-Interrupt zum zurückschalten benutzen. Andererseits kann man auch Software-SPI benutzen. Bei den paar Zeichen die ein 16x2-Display besitzt merkt man den Unterschied überhaupt nicht. Das wird erst bei Grafik-Dogs interessant. Das Hardware-SPI benutze ich eigentlich kaum. MfG Spess
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.