Forum: Mikrocontroller und Digitale Elektronik DOGM162 Display funktioniert nur sporadisch


von Michael (Gast)


Lesenswert?

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 ?

von Stefan S. (mexakin)


Lesenswert?

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.

von Michael (Gast)


Lesenswert?

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 ?

von Jonas B. (jibi)


Lesenswert?

>dass das Display nur manchmal funktioniert ?

Timing Probleme, konkurierende SPI-Modi etc...

von Jonas B. (jibi)


Lesenswert?

>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
von Stefan S. (mexakin)


Lesenswert?

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.

von Michael (Gast)


Lesenswert?

Jonas B. schrieb:
> Timing Probleme, konkurierende SPI-Modi etc...

Timing Probleme heißt, dass die Delay-Schleifen zum Warten zu kurz/lang 
sind ?

von Michael (Gast)


Lesenswert?

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.

von Stefan S. (mexakin)


Lesenswert?

nicht ganz unwichtige Information :) mach ruhig weiter du findest das 
schon

von Jonas B. (jibi)


Lesenswert?

>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

von Michael (Gast)


Lesenswert?

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

von Sascha W. (sascha-w)


Lesenswert?

@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

von Michael (Gast)


Lesenswert?

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 ?

von Michael (Gast)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

CS muss ja low sein, damit der µC mit dem Display/Sensor kommunizieren 
kann. Was macht eigentlich dann der RS ?

von Michael (Gast)


Lesenswert?

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.

von Michael (Gast)


Lesenswert?

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.

von Dietrich L. (dietrichl)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

Ein Delay vor den SPI-Init ?
Ich habe eben 1000ms als Warteschleife davor. Bleibt leider weiterhin 
Zufall ob das Display geht oder nicht.

von Bastian W. (jackfrost)


Lesenswert?

Wie sieht denn deine Display Initialisierung aus ?

Gruß JackFrost

von holger (Gast)


Lesenswert?

Ist PB2/SS bei dir ein Ausgang? Wenn nicht dann
mach PB2 zum Ausgang. Und das BEVOR du das SPI
auf Master initialisierst.

von Rudolph R. (rudolph)


Lesenswert?

Ist etwas länger her und ein ATMega164A mit dem DOM162, das funktioniert 
aber:

Beitrag "DOGM162 an ATMega164PA"

von Michael (Gast)


Lesenswert?

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

}

von Michael (Gast)


Lesenswert?

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

}

von Sascha W. (sascha-w)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

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
}

von Michael (Gast)


Lesenswert?

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 ?

von Michael (Gast)


Lesenswert?

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);
}

von Sascha W. (sascha-w)


Lesenswert?

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

von Bastian W. (jackfrost)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

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;

    }
}

von Michael (Gast)


Lesenswert?

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 ?

von Rudolph (Gast)


Lesenswert?

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.

von Sascha W. (sascha-w)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

Nach dem letzten Bit ein Delay einfügen wär dann die Lösung ?

von Sascha W. (sascha-w)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

Sascha W. schrieb:
> 2 nop

Damit ist ein Delay gemeint ?

von Sascha W. (sascha-w)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.