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 ?
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
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.
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.
>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...
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
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
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