Hallo,
ich benutze einen Atmega16 und ein 4x20 Display, dass ich im 8-Bit Modus
anspreche. Meine Kenntnisse im Programmieren sind noch nicht sehr weit
fortgeschritten. Ich programmiere übrigens mit dem AVR Studio.
Ich würde mich sehr freuen, wenn mir jemand ein kleines Testprogramm
hätte, in dem z.B. das Wort "Hallo Welt" oder ähnliches ausgegeben wird.
Mich interessiert lediglich der Block, in dem der Text ans Display
gesendet wird. Die Init vom Display habe ich schon.
Ich verstehe nicht ganz, woher das Studio, bzw. der Compiler wissen
soll, welche Signale er über die 8 Datenleitungen übertragen muß, damit
der gewollte Text erscheint?
Ich hoffe auf eure Hilfe.
Gruß Jens
Hi AVR Freund,
sorry, ich dachte es wäre klar dass ich in C programmieren will, weil
ich meinen Thread im GCC Forum eingestellt habe.
Also, ich möchte gern in C programmieren.
Danke für deinen Link, das Tutorial kenne ich schon, beim Assembler hat
es mir geholfen mein Display zu initialsieren, was ich nun in meinem
C-Programm auch übertragen habe, aber leider ist in dem Tutorial keine
Textausgabe dabei.
Habe das Forum auch schon nach möglichen Threads durchsucht. Bin auch
weiterhin auf der Suche.
Gruß Jens
Hi Joe,
das sieht ja mal nicht schlecht aus.
Also, wenn du mit Zeichenausgabe meinst, dass ich die Daten an den
richtigen Port schicke, dann ja, allerdings halt momentan für einzelne
Buchstaben und nicht für nen ganzen Text. Den Buchstabe schicke ich als
hex-Wert ans Display. Deswegen tue ich mich schwer, mir vorzutellen, wie
das bei einem langen Text geht.
Kannst du mir kurz erklären was deine Funktion da macht?
Ich verstehe es so, du übergibst der while-Schleife den lcd-string.
Und in der Schleife wird in der Funktion "Zeichenausgabe" der string
hochgezählt. Aber was zählt er denn hoch? Die Buchstaben?
Wie du siehst, fehlt mir noch bissle Verständnis der Materie.
Aber wer nicht fragt wird nicht aufgeschlaut.
Gruß Jens
Das Problem ist warscheinlich das du noch nie was Pointern gehört hast?
Ein Pointer ist ein Zeiger auf eine Speicheradresse. Mit dem *-Operator
kann man nun die Adresse dereferenzieren und erhält das Zeichen an der
Stelle im Speicher. Mit dem ++-Operator wird nichts am Zeichen geändert
dondern der Pointer zeigt nun auf das nächste Zeichen im Speicher.
Jedes Char-Array (so nent man diese "Strings") hat am Ende als Letztes
Zeichen eine NULL. Soblad die dereferenzierung im Schleifenkopf zu
dieser NULL führt wird die Schleife abgebrochen, da der String zu ende
ist
> Also, wenn du mit Zeichenausgabe meinst, dass ich die Daten an den> richtigen Port schicke, dann ja, allerdings halt momentan für einzelne> Buchstaben
Ja, das meine ich. Eine Erklärung hast du ja bereits von Ulrich
bekommen. Probiers einfach mal aus, ist doch in 2 Minuten ausgestanden.
Danke an Ulrich für die gute Erklärung, hattest Recht, von Pointern
wußte ich nichts.
Ich habe mal mein Programm angehängt. Ist bestimmt verbesserungswürdig
aber darum gehts ja jetzt net.
Ich steh grad auf dem Schlauch, habe den Tip von Joe integriert, nun
hänge ich grad an der Zeichenausgabe.
Vorher habe ich immer sowas geschrieben:
PORTC=0x57 // Entspricht dem Buchstaben "w"
Jetzt kann ich den Buchstaben ja nicht definieren, sondern will ja z. B.
"moin moin" übergeben. Deswegen die Fragezeichen unter void
Zeichenausgabe.
Was muß denn da alles rein?
Hab mir deinen Code jetzt nicht angesehen aber was du nun brauchst ist
eine Funktion welche die Zeichen aufnimmt, das RS BIT setzt und nach
anlegen der Daten das ENABLE signalt schaltet.
Das sieht dann z.B. so aus:
1
voiddisplay_data_out(uint8_tdsp_char_out_){
2
SETBIT(PORTB,DATA);// Daten an Display => RS BIT=1
3
PORTC=dsp_char_out_;
4
SETBIT(PORTB,OENABLE);// ENABLE = HIGH
5
CLRBIT(PORTB,OENABLE);// ENABLE = LOW, Datenübernahme des Displays
6
}
Bei den PORT PIN's mußt du natürlich deine verwenden.
> Du könntest auf die fertige Lib von Fleury nutzen, zumindest mal reinschauen:
Machs nicht, sorry Michael, aber er will lernen und als Ergebnis hat er
dann seine eigene Routine.
Die Fleury LIB ist für einen Anfänger ne Zumutung. Wenn diese auch den
Eindruck einer eierlegenden Wollmilchsau macht, ich find se Grotte.
Zum Reinschauen und Ausprobieren, gerade als Anfänger, fand ich die
einzelnen Funktionen doch hilfreich. Aber das sollte jeder für sich
entscheiden.
Gruß
Michael
Mahlzeit,
danke für den Tip mit der Fleury Lib, die hab ich mir schon angeschaut
und wie Joe richtig vermutet hat, bin ich daraus nicht schlau geworden,
eher im Gegenteil.
Okay, das mit Enable und RS weiß ich.
Du übergibst dann an den Port den Inhalt von dsp_char_out.
Jetzt muß ich doch den LCD_puts-Befehl irgendwie mit dem dsp_char_out
verwurschteln, damit mein Text in der Variable dsp_char_out steht
richtig?
Ja, das ist schon geschehen:
Zeichenausgabe (*lcd_string++); // String auslesen -> LCD
dsp_char_out (*lcd_string++); // String auslesen -> LCD
Alles klar ? und wenn du nur ein einzelnes Zeichen ausgeben willst dann
schreibst du:
dsp_char_out ('b'); oder dsp_char_out (0x57);
Sorry, hab ich was übersehen ?
LCD_puts ist ja deine Funktion und an die übergibst du:
LCD_puts ("moin");
Vielleicht hast du es noch nicht erkannt ? Du übergibst einfach einen
Wert oder Pointer an eine Funktion.
Hey, klasse, das gefällt mir gut so.
Mein Studio meckert jetzt nur noch weil ich dsp_char_out_ und LCD_puts
nicht deklariert habe.
Was für ein Typ sind die denn? Char? Int?
Das Prinzip habe ich schon mal verstanden.
Großes Lob an Joe !!
Deklarier sie so wie sie heißen:
void display_data_out (uint8_t dsp_char_out_);
wobei du nur die #include <stdint.h> ISO C99 Integer types noch
hinzufügst.
Du kannst aber auch unsigned char verwenden.
Joe du bist wirklich spitze.
Er hats geschluckt und er gibt genau den Text wieder, der in meinem Code
steht.
Hatte noch ein paar Probleme im Code aber jetzt ist alles aufgeräumt.
Da wäre ich sicher nie drauf gekommen.
Jetzt werde ich mal bissle experimentieren.
Nochmals danke an Dich, Joe, speziell und die anderen.
So machts Spaß.
Grüße aus m Süden.
Wenn du mit Pointern noch auf Kriegsfuss stehst,
kann man das ganze auch mit Array Syntax schreiben.
Ev. hilft dir das besser zu verstehen, was da abgeht.
1
voidLCD_puts(constchar*lcd_string)
2
{
3
inti=0;
4
5
while(lcd_string[i]!='\0'){
6
Zeichenausgabe(lcd_string[i]);
7
i++;
8
}
9
}
Mit etwas Glück optimiert der Compiler beide Versionen
(Pointer / Array Indizierung) auf gleichwertigen
Code.
Ja, der Karl Heinz hat schon recht, die neue WINAVR Version würde
meckern.
void LCD_puts (const char *lcd_string)
const char steht dafür das die Zeichenkette im Programmspeicher abgelegt
wird.
Ja, der Vorschöag von Heinz macht es etwas verstädnlicher.
Nebenbei könnte einer kurz erklären, woher das Programm weiß, wie es die
Buchstaben in hex-Werte umzuwandeln hat?
Weil, ich seh dass es funktioniert, aber verstanden habe ich es soweit
noch nicht.
Anbei mal der aktuelle Code.
Gruß jens
Jensgzm wrote:
> Ja, der Vorschöag von Heinz macht es etwas verstädnlicher.> Nebenbei könnte einer kurz erklären, woher das Programm weiß, wie es die> Buchstaben in hex-Werte umzuwandeln hat?
Das macht bereits der Compiler.
Ich verrat dir ein Geheimnis: Im Rechner gibt es keine
Buchstaben. Im Rechner gibt es nur Zahlen. Je nachdem
wie diese Zahlen verwendet werden, stellen die Zahlen
was anderes dar.
Deine Zeichenausgabe Funktion gibt keine Buchstaben aus.
Deine Zeichenausgabefunktion schickt zb. die Zahl 0x41 an
das Display. Im Display ist eine Tabelle eingebaut, in der
steht, welche Pixel beim Empfang der Zahl 0x41 aufleuchten
sollen. Aus der Tabelle folgt zb. dass das Muster so aussehen
soll
. . * . .
. * . * .
. * . * .
. * . * .
. * .
. * . * .
. . . . .
und dein Gehirn macht dann ein 'A' daraus. Nun könnte man
viele beliebige Zuordnungen machen. Bei 0x41 könnte man auch
andere 'Buchstaben' ausgeben. Daher hat man sich auf einige
Schemata geeinigt. Das am weitesten verbreitet ist sicherlich
das ASCII Schema.
http://de.wikipedia.org/wiki/ASCII
Wenn du also in deinem Programm den Text "Testtext" verwendest.
dann sind da keine Buchstaben gespeichert (Erinnerung: im
Rechner ist alles eine Zahl), sondern die Zahlenfolge:
0x54 0x65 0x73 0x74 0x74 0x65 0x78 0x74 0x00
Diese Zahlenfolge kann jetzt alles mögliche bedeuten.
Schickt man aber diese Zahlenfolge an ein Anzeigegerät
welches ASCII versteht, dann pinselt es "Testtext" hin.
(Das letzte 0x00 ist ein Zugeständnis an C. Daran wird in
C erkannt, dass ein Text zu Ende ist. Daher wird auch in
der weiter oben gezeigten Schleife abgeprüft, ob 0x00
schon erreicht wurde).
Jensgzm wrote:
> Anbei mal der aktuelle Code.
Ein wichtiger Punkt in der Programmierung, der leider von
Neueinsteigern immer unterschätzt wird, ist die Codeformatierung.
Gewöhn dir gleich von Anfang an an, eine ordentliches Einrückschema
sowie ein konssistentest { } Schema zu machen.
Auch wenn es oft belächelt wird, ist es doch wichtig das zu tun.
Weiters: In Standard-C gibt es keine funktionslokalen Funktionen.
Auch wenn gcc dies als Erweiterung erlaubt, würde ich dir doch
empfehlen, dich hier an Standard-C zu halten.
PORTC=0x06;// Entry Mode Set, Incrementieren, no Shift
44
E_toggle();
45
PORTC=0x02;// Cursor home
46
}
47
48
intmain(void)
49
{
50
unsignedlonginti;
51
52
for(i=0;i<10000;i++)
53
;
54
55
LCD_init();
56
57
LCD_puts("Druck und Temperatur");
58
59
while(1){
60
;
61
}
62
63
/* wird nie erreicht */
64
return0;
65
}
Sieht doch gleich viel besser aus. Mit den Warteschleifen
in E_toggle must du dir noch was überlegen. Spätestens
wenn du den Compiler optimieren lässt, fliegen die sowieso
raus. Zumindest die 2.te Warteschleife ist überflüssig. Das
Display ist schnell genug, sodass du den Pin nur setzten und
gleich darauf wieder löschen kannst.
1
voidE_toggle(void)
2
{
3
for(i=0;i<1000;i++)
4
;
5
PORTA|=(1<<5);// Enable High
6
PORTA&=~(1<<5);// Enable Low
7
}
Die erste Warteschleife ist wie gesagt problematisch. Zum
einen ist deren Ausführungszeit von der Taktfrequenz des µC
abhängig. D.h. die 1000 sind manchmal viel zu viel, und bei
einem schneller getaktetem µC sind sie viel zu wenig.
Zum anderen wird sie dir der Optimizer sowieso rauswerfen,
wenn er mal optimieren darf.
Für solche Warteschleifen gibt es in "delay.h" eigene Funktionen,
denen man die gewünschte Zeit übergibt und die sich ausrechnen,
wieviele Schleifenwiederholungen dafür notwendig sind.
Schau aber unbedingt in "delay.h" rein, denn diese Funktionen
haben eine beschränkung für die maximal mögliche Zeit.
> Daher wird auch in der weiter oben gezeigten Schleife abgeprüft, ob 0x00> schon erreicht wurde).
Und das macht eben auch ne while Schleife, in C ist alles Null
terminiert.
Danke für die Änderung des Codes, muß zugeben es sieht übersichtlicher
aus.
Ich denke halt immer, dass ich es auch noch aufräumen kann wenn ich mal
fertig bin, aber eigentlich ist das Programm ja nie fertig.
Werd versuchen mich dran zu halten.
Mit "keine funktionslokalen Funktionen" meinst du, dass du die Init
ausgelagert hast oder?
So, wir optimieren noch ein bischen. Du hast eine Funktion die Zeichen
ausgibt und eine die Zeichenketten beherrscht. Machen wir doch noch eine
für Steuerzeichen (für die Initialisierung und clear display, cursor
home etc.).
Diese Funktion wird immer mit RS = 1 verlassen und somit ist innerhalb
der Routine der Focus auf LCD Daten ausgeben. Du kannst also in
lcd_char_out RS=1 weglassen.
Wenn du nun noch #define lcd_clear 0x01 verwendest dann schreibst du:
lcd_command (lcd_clear);
Der Code wird immer lesbarer und du kannst deine Initialisierung mittels
dieser lcd_command Funktion durchführen.
Für die delays verwendest du den Tipp von Karl Heinz. Den delay Aufruf
habe ich schonmal eingefügt.
Jensgzm wrote:
>> Mit "keine funktionslokalen Funktionen" meinst du, dass du die Init> ausgelagert hast oder?
Dein ursprüngliches Pgm hat so ausgesehen
1
intmain()
2
{
3
voidE_toggle(void)
4
{
5
...
6
}
7
8
voiddsp_char_out_(uint8_tdsp_char_out_)
9
{
10
...
11
}
12
13
voidLCD_puts(constchar*lcd_string)
14
{
15
...
16
}
17
18
...
19
}
d.h. die Funktionen E_toggle, dsp_char_out_ und LCD_puts waren
innerhalb von main() definiert (Siehst du, ohne konsequent
durchgezogene Einrückung hättest du das gar nicht gesehen).
Das ist aber in Standard-C nicht erlaubt.
Wenn ich aber den Vorschlag von Karl Heinz in meinem Studio compiliere,
dann meckert es, weil das "i" in der E_Toggle nicht definiert ist.
Deswegen hatte ich es vorhin auch unten angehängt. Eben nach der
Definition von i. Mach ich da einen Fehler?
Jensgzm wrote:
> Wenn ich aber den Vorschlag von Karl Heinz in meinem Studio compiliere,> dann meckert es, weil das "i" in der E_Toggle nicht definiert ist.> Deswegen hatte ich es vorhin auch unten angehängt. Eben nach der> Definition von i. Mach ich da einen Fehler?
Nein. Ich hab nicht aufgepasst.
E_Toggle muss natürlich so aussehen:
1
voidE_toggle(void)
2
{
3
uint16_ti;
4
5
for(i=0;i<1000;i++)
6
;
7
8
PORTA|=(1<<5);// Enable High
9
PORTA&=~(1<<5);// Enable Low
10
}
d.h. die Funktion E_toggle kriegt ihre eigene Variable i
Ich will jetzt nicht zuviel Verwirrung hereinbringen aber ne seperate
Togglefunktion braucht es nicht, man muß nur das Programm entsprechend
aufbauen.
void dsp_char_out_ (uint8_t dsp_char_out_)
Hier gehört das Togglen mit hinein und die Wartezyklen packst du in die
Steuerzeichenausgabe.
Denn togglen mußt du immer wenn du ein Zeichen an das LCD übergibst, ob
Steuerzeichen oder Daten.
Ist das mit der Steuerzeichenausgabe klar ?
Ja, das mit der Steuerzeichenausgabe ist klar.
Ich muß sagen Ihr legt ein gutes Tempo vor, aber bis jetzt komm ich noch
mit.
So schnell bin ich bis jetzt noch nie voran gekommen. Mußte immer ewig
suchen bis ich mal was hilfreiches (auf meinem Niveau) gefunden habe.
Hier mal wieder der aktuelle Code.
Das Delay mußte ich ausschalten, weil ich keine delay.h Datei habe.
Konnte auch keine auf meiner Fetsplatte finden.
Man man, du bist ja unersättlich.
Aber es hilft ja.
Ich hoffe du bist öfters im Forum, denn ich muß noch ein paar Sachen
umsetzen, die ich aber heute noch nicht umsetzen kann und da könnte ich
deine Hilfe gut gebrauchen. ;-)
Für die Zeichen reichen 42 uSec, und die Steuerzeichen haben ja ihre 4,1
mSec. schon. Nach Power ON sinds dann 15 mSec.
Wenn du noch folgende #Defines hinzufügst:
#define cursor_line_1 0x80
#define cursor_line_2 0xC0
dann setzt dieser Befehl:
lcd_command (cursor_line_2);
Deine Ausgabe in die 2te Zeile.
Ja, dass mit dem Cursor wollte ich auch noch ausprobieren.
So ähnlich hab ich es mir vorgestellt.
Mittlerweile ist der Code echt übersichtlich und geschrumpft ist er
auch.
Habe den Cursorsprung in die 4.Zeile gemacht.
So stelle ich mir einen Display Code vor, gut gemacht ;-))
Nun Vergleiche mal die Codegröße mit irgendeiner LIB und du verstehst
warum ich zum selber schreiben rate.
Im Prinzip ist es fertig aber man kann noch nen bischen dran schrauben.
1
voiddsp_char_out(uint8_tdsp_char_out){
2
3
PORTC=dsp_char_out;
4
PORTA|=(1<<5);// Enable High
5
PORTA&=~(1<<5);// Enable Low
6
_delay_us(42);
7
}
Auch die Anschlüsse RS und ENABLE können noch in ein #define. Wenn man
es richtig macht dann kann man auch:
ENABLE = HIGH;
ENABLE = LOW;
schreiben. Im Anhang findest du meinen Code für eine 4 BIT Ansteuerung.
Du wirst feststellen das deiner nun fast genauso aussieht ;-))
Hier findest du noch eine schöne Erklärung zu den PORT's & BIT's, wie du
siehst lerne auch ich aus dem Forum.
Beitrag "sbit macro für avr-gcc"
Hi Joe,
ja, das Programm ähnelt schon ziemlich.
Werde weiterhin Optimierungen durchführen.
Wie gesagt, es gibt noch einiges zu tun.
Für heute ist aber mal Schluß.
Vielleicht treffen wir uns mal wieder in einem anderen Thread.
Nochmals danke für die Lehrstunde, ich bin heute weit gekommen.
Gruß Jens