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
voidlcdSend(chardata,intRS){
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
intmain(){
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
return1;
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
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
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.
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
@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
@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
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
@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
"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
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
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 :-)
@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
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.
@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
>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.
@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 :-)