Forum: Compiler & IDEs LCD+Optimierung


von Jochen (Gast)


Lesenswert?

Ich habe angefangen für mein LCD Funktionen zu schreiben:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
// RS    = PG2
5
// R/W    = PG0
6
// E    = PG1
7
// D0-D7  = PC0-PC7
8
9
void lcdSend(char data, int RS) {
10
  if(RS) {
11
    PORTG |= (1<<PG2);
12
  } else {
13
    PORTG &= ~(1<<PG2);
14
  }
15
  PORTG &= ~(1<<PG0);
16
  _delay_us(1);
17
  PORTG |= (1<<PG1);
18
  PORTC = data;
19
  _delay_us(1);
20
  PORTG &= ~(1<<PG1);
21
  _delay_us(1);
22
}
23
24
int main() {
25
  DDRC = 0xFF;
26
  DDRG = 0x07;
27
  _delay_ms(100);
28
  lcdSend(0x38,0);
29
  lcdSend(0x0F,0);
30
  lcdSend(0x01,0);
31
  lcdSend(0x06,0);
32
  lcdSend('H',1);
33
  lcdSend('a',1);
34
  lcdSend('l',1);
35
  lcdSend('l',1);
36
  lcdSend('o',1);
37
  lcdSend('!',1);
38
  while(1);
39
  return 1;
40
}
Das LCD gibt auch einwandfrei "Hallo!" aus. Aber nur solange ich -O0 
benutze sobald ich eine Optimierung aktiviere funktioniert es nicht 
mehr. Ich vermute, dass es wohl irgendwie am Timing liegt und die 
Optimierung mir die Delays raus wirft oder irgendso was in der Art. Da 
ich mir die _delay_xx() Funktionen nicht aus den Fingern gesaugt habe, 
und sie schon bei WinAVR/AVRStudio dabei waren gehe ich stark davon aus, 
dass diese eigentlich zuverlässig funktionieren sollten.
F_CPU habe ich auch richtig gesetzt.

Komisch ist auch, dass wenn ich es debugge und 
http://www.helmix.at/hapsim/ dabei laufen habe es mit Run NICHT 
funktioniert aber sehr wohl mit AutoStep wohl auf der Seite extra steht, 
dass auf das Timing keine Rücksicht genommen wird.

Hm was kann ich noch angeben...
AVRStudio 4.13 b528
WinAVR puh... 20070122
ATMega128L

So ich hoffe, ich hab alles angegeben und nichts vergessen und ihr könnt 
mir bei dem Problem weiter helfen.

MfG,
Jochen

von Wolfram (Gast)


Lesenswert?

Ich kann nicht nachvollziehen was du in deiner lcdSend routine machst.
Normaler weise geht das so: lege die Daten an , warten ,enable mit Read 
oder write, warten, disable

von Karl H. (kbuchegg)


Lesenswert?

Es liegt mit ziemlicher Sicherheit am Tinming.
Dazu musst du wissen, dass die _dely_xx Funktionen
ihre Zeiten nur dann einhalten, wenn die Optimierung
eingeschaltet ist. Ist sie ausgeschaltet, so haben diese
Funktionen ein Vielfaches ihrer normalen Laufzeit.

Und wenn ich mir deine Initialisierungsfunktionen so
ansehe: Die geforderten Zeiten sind um einiges höher.
Nachdem du ein Komando zum LCD geschickt hast, musst du
dem LCD Zeit geben, dieses Kommando auch zu verarbeiten.
Mach da mal 30 oder 40 ms Pause nach einem Kommando
rein.

von Oliver (Gast)


Lesenswert?

Ohne zusätzliche oder längere delays wird es wohl nicht gehen.

Schau in das Datenblatt des LCD's, wie das Timing auszusehen hat, und 
vergleiche das mit deinem (Assembler)-Code (Zyklen zählen). Gerade die 
Bit-Befehle wie "PORTG &= ~(1<<PG0);" dampft die Optimierung von 
mehreren auf einen Takt ein, und auch im Funktionsaufruf wird einiges 
eingespart.

Hat dein LCD ein busy-flag? Wenn ja, warum wohl?

Oliver

von Falk (Gast)


Lesenswert?

@Oliver

>Schau in das Datenblatt des LCD's, wie das Timing auszusehen hat, und
>vergleiche das mit deinem (Assembler)-Code (Zyklen zählen). Gerade di
Sooo tief muss man nicht im Schlamm wühlen. gerade die _delay_us() ist 
schon wasserdicht.

e
Bit-Befehle wie "PORTG &= ~(1<<PG0);" dampft die Optimierung von
mehreren auf einen Takt ein, und auch im Funktionsaufruf wird einiges
eingespart.

Sein Problem ist die rein logische Abfolge. Nicht ins Datenblat 
geschaut? Die Initialisierung braucht Pausen im ms Bereich (15/5/1)

>Hat dein LCD ein busy-flag? Wenn ja, warum wohl?

Damit es Millionen von LCDs ungenutzt lassen und trotzdem gut 
funktionieren? Ein 40us Delay nach jedem Scheibkommando löst das 
Problem. Steht alles im Datenblatt.

@Jochen

Ich würde es mal so probieren.

// RS    = PG2
// R/W    = PG0
// E    = PG1
// D0-D7  = PC0-PC7

nicht als Kommentar hinschreiben sondern als define, macht den Code 
lesbar.

#define RS   PG2
#define R_W  PG0
#define E   PG1
// Kommentare sind auch in C Programmen sinnvoll und notwendig

void lcdSend(char data, int RS) {
  if(RS) {
    PORTG |= (1<<RS);  // Kommando
  } else {
    PORTG &= ~(1<<RS);  // Daten
  }
  PORTC = data;      // Daten ausgeben
  PORTG |= (1<<E);    // steigende Flanke von E
  _delay_us(1);
  PORTG &= ~(1<<PG1);  // fallende Flanke von E
  _delay_us(40);    // Wartezeit für Kommandoausführung
}

int main() {
  DDRC = 0xFF;
  DDRG = 0x07;

  PORTG &= ~(1<<R_W);  // R_W immer low, nur Schreibzugriffe

// Initialisierung
  _delay_ms(100);
  lcdSend(0x38,0);    // das muss dreimal ausgeführt werden
  _delay_ms(15);
  lcdSend(0x38,0);
  _delay_ms(1);
  lcdSend(0x38,0);
  lcdSend(0x0F,0);
  lcdSend(0x01,0);
  lcdSend(0x06,0);
// Ende der Initialisierung

  lcdSend('H',1);
  lcdSend('a',1);
  lcdSend('l',1);
  lcdSend('l',1);
  lcdSend('o',1);
  lcdSend('!',1);
  while(1);
  return 1;
}

MFG
Falk

von Falk (Gast)


Lesenswert?

@Karl heinz Buchegger

>Es liegt mit ziemlicher Sicherheit am Tinming.
>Dazu musst du wissen, dass die _dely_xx Funktionen
>ihre Zeiten nur dann einhalten, wenn die Optimierung
>eingeschaltet ist. Ist sie ausgeschaltet, so haben diese
>Funktionen ein Vielfaches ihrer normalen Laufzeit.

Das dürfe wohl kaum ein Problem sein. Langsames Ansteuern ist für das 
LCD kein Problem, zu schnelles schon.

MfG
Falk

von Karl H. (kbuchegg)


Lesenswert?

Falk wrote:
> @Karl heinz Buchegger
>
>>Es liegt mit ziemlicher Sicherheit am Tinming.
>>Dazu musst du wissen, dass die _dely_xx Funktionen
>>ihre Zeiten nur dann einhalten, wenn die Optimierung
>>eingeschaltet ist. Ist sie ausgeschaltet, so haben diese
>>Funktionen ein Vielfaches ihrer normalen Laufzeit.
>
> Das dürfe wohl kaum ein Problem sein. Langsames Ansteuern ist für das
> LCD kein Problem, zu schnelles schon.

Sein Problem war ja:
In einer Debug Version geht es, in einer Release nicht.
Debug: die Zeiten von den _delay_xx stimmen nicht, daher geht es
Release: die Zeiten von den _delay_xx stimmen, daher geht es nicht.


Mfg
Karl Heinz

von Falk (Gast)


Lesenswert?

@Karl heinz Buchegger

>Sein Problem war ja:
>In einer Debug Version geht es, in einer Release nicht.
>Debug: die Zeiten von den _delay_xx stimmen nicht

Das mag sein, aber dennoch sind lange Wartezeiten definitiv unkritisch!

>Release: die Zeiten von den _delay_xx stimmen, daher geht es nicht.

Es fehlen delays an der richtige Stelle.

MFG
Falk

von Oliver (Gast)


Lesenswert?

"Sooo tief muss man nicht im Schlamm wühlen."
Es bildet aber ungemein, daß mal gemacht zu haben. Danach sollten sich 
alle Fragen zu Timingproblemen mit LCD's für immer erledigt haben :-)

"Damit es Millionen von LCDs ungenutzt lassen und trotzdem gut
funktionieren? Ein 40us Delay nach jedem Scheibkommando löst das
Problem. Steht alles im Datenblatt."

Mehr sag ich doch gar nicht. Natürlich funktioniert das ohne Abfrage des 
busy-flags, nur muß man dem LCD eben die Zeit geben, den vorherigen 
Befehl abzuarbeiten.

Also, hier die Kurzfassung meines Beitrags:
RTFM

Oliver

von Peter D. (peda)


Lesenswert?

Wenn mich nicht alles täuscht, werden Delays ohne Optimierung erst zur 
Laufzeit in float ausgerechnet.

Die können also durchaus 1000-fach länger sein.

Und der Code ist dann auch 10-mal größer.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Falk wrote:
> @Karl heinz Buchegger
>
>>Sein Problem war ja:
>>In einer Debug Version geht es, in einer Release nicht.
>>Debug: die Zeiten von den _delay_xx stimmen nicht
>
> Das mag sein, aber dennoch sind lange Wartezeiten definitiv unkritisch!
>
>>Release: die Zeiten von den _delay_xx stimmen, daher geht es nicht.
>
> Es fehlen delays an der richtige Stelle.


OK.
In Zukunft erkläre ich einem Fragesteller nicht mehr
warum die Symptome so sind wie er sie beobachtet :-)

von Falk (Gast)


Lesenswert?

@Karl heinz Buchegger

>In Zukunft erkläre ich einem Fragesteller nicht mehr
>warum die Symptome so sind wie er sie beobachtet :-)

???
Bist du jetzt auch schon kritikunfähig?
Dein erster Absatz war unlogisch, der zweite was IMHO OK.
Das war alles was ich sagen wollte.

MFG
Falk


von Jochen (Gast)


Lesenswert?

Hallo,

der Reihe nach:

Ich kann nicht nachvollziehen was du in deiner lcdSend routine machst.
Normaler weise geht das so: lege die Daten an , warten ,enable mit Read
oder write, warten, disable

Ich habe mich (meiner Meinung nach) dabei an das Datenblatt gehalten:
http://www.lcd-module.de/deu/pdf/doma/4_20.pdf

Auf Seite 6 das Bild Write Operation:
Dort geht eindeutig R/S und RW als erstes gesetzt. Danach soll ich t_AS, 
also mindestens 140ns, warten. Danach E high. Danach D0-D7 setzen und 
zwischen Setzen und E low müssen t_DSW >= 195ns vergehen. Also 
dazwischen noch 1us. Und zum Schluss noch eine 1us warten, damit ich 
t_CYCLE >= 1000ns garantiert erfülle.

@Karl heinz Buchegger:
arg jetzt wo du es sagst. Hab die max. Execution Times auf Seite 4 
verbummelt.

@Falk:
Das mit dem ersetzen war mir klar. Ich wollte es nur mal grob zum Laufen 
bekommen und dann später die ganzen Ersetzungen einbauen.
Trotzdem danke für den Rat.

// das muss dreimal ausgeführt werden

Laut Initialisierungbeispiel auf Seite 4 eben nicht.

@Oliver:
Ich hoffe du hast bei den obigen Zeilen gesehen, dass ich mich sehr wohl 
mit dem FM auseinander gesetzt habe und mir darüber Gedanken gemacht 
habe. Man kann immer mal etwas übersehen.

Ich werde jetzt mal sehen, wie es mit _delay_ms(40) in der letzten Zeile 
von lcdSend(); aussieht.
Oder ich teile die 40ms auf die einzelnen delays auf.
Wenn es danach immer noch nicht klappen sollte schau ich mir wohl mal 
ein anderes LCD-Datenblatt an bei dem auch der HD44780 verwendet wird. 
Vielleicht stehen dort andere Angaben, die ich versuchen kann.

Nochmal danke an alle, die sich die Zeit genommen haben sich den Thread 
anzuschauen und ihre Meinung gepostet haben.

von Falk (Gast)


Lesenswert?

@Jochen

Auf Seite 6 das Bild Write Operation:
>Dort geht eindeutig R/S und RW als erstes gesetzt. Danach soll ich t_AS,
>also mindestens 140ns, warten. Danach E high. Danach D0-D7 setzen und
>zwischen Setzen und E low müssen t_DSW >= 195ns vergehen. Also
>dazwischen noch 1us. Und zum Schluss noch eine 1us warten, damit ich
>t_CYCLE >= 1000ns garantiert erfülle.

Ist erstmal korrekt, allerdings kann man das sinvoll vereinfachen.

RS, RW, D0-7 setzen
140ns warten
E auf 1
mind 450 ns warten
E auf 0

Die minimale Zykluszeit von 1us kommt automatisch zustande da deine 
Routine incl call/retrun einiges an Zeit braucht.

>// das muss dreimal ausgeführt werden

>Laut Initialisierungbeispiel auf Seite 4 eben nicht.

In einem anderen Datenblatt wird aber dreimal gefordert, und eben auch 
mit den Pausen dazwischen.

>Ich werde jetzt mal sehen, wie es mit _delay_ms(40) in der letzten Zeile
>von lcdSend(); aussieht.

_delay_us(40), MIKROsekunden, nicht Millisekunden!

MFG
Falk

von Jochen (Gast)


Lesenswert?

>Ich werde jetzt mal sehen, wie es mit _delay_ms(40) in der letzten Zeile
>von lcdSend(); aussieht.

_delay_us(40), MIKROsekunden, nicht Millisekunden!


Meinte ich auch... naja eigentlich meinte ich _delay_ms(2) um auch 
sicher die "Clear Display" und "Cursor at Home" ausgeführt werden.


Habe mir mittlerweile aber auch eine funktionierende lcdIsBusy() 
Funktion geschrieben.

Vielen Dank nochmals an alle.

von Daniel Held (Gast)


Lesenswert?

@Falk: hättest Du den Beitrag von Karl-Heinz richtig gelesen und 
verstanden wäre der Thread einige Beiträge kürzer. Ihr sagt im Prinzip 
das gleiche nur das karl-Heinz noch das Beobachtete erklärt.
(Im Debug: delays dauern länger = funktioniert
ohne Debug: delays sind wie programmiert = zu kurz = geht nicht)

Falk wrote:
> @Karl heinz Buchegger
>
>>Sein Problem war ja:
>>In einer Debug Version geht es, in einer Release nicht.
>>Debug: die Zeiten von den _delay_xx stimmen nicht
>
> Das mag sein, aber dennoch sind lange Wartezeiten definitiv unkritisch!
>
>>Release: die Zeiten von den _delay_xx stimmen, daher geht es nicht.
>
> Es fehlen delays an der richtige Stelle.


OK.
In Zukunft erkläre ich einem Fragesteller nicht mehr
warum die Symptome so sind wie er sie beobachtet :-)

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.