Habe zwar die LCD-Routine aus dem AVR-GCC-Tutorial mit Erfolg getestet:
Lcd.c:
1
#include<avr/io.h>
2
#include"lcd-routines.h"
3
intmain(void){
4
lcd_string("Hello World!");
5
while(1);
6
return0;
7
}
Leider habe ich aber eine vorgegebene Hardware, an der RS und EN nicht
an PD4 bzw. PD5 liegen, sondern
#define LCD_RS PB7
#define LCD_EN PB6
Diese Umdefinitionen reichen allein natürlich nicht aus, um das Programm
wieder zum Laufen zu bringen.
Habe auch keine Ahnung vom AVR (Kenne nur 8051, wo man mit sbit RS=P1^2;
und dann RS=1; einzelne Portleitungen setzen kann) und weiß nicht wie
das bei Atmel geht, geschweige denn, was so komplexe Sequenzen wie
LCD_DDR = LCD_DDR | 0x0F | (1<<LCD_RS) | (1<<LCD_EN);
genau machen.
Vielleicht findet sich ja hier ein Profi, der sich herausgefordert fühlt
das zu realisieren (oder hat so etwas schon einmal für verschiedene
Portpins gemacht)?
Wäre sehr, sehr dankbar, wenn er es hier posten könnte !!!
------------------------------------------------------------------------
------------------------------------------------
Hier nochmal alle Quelltexte:
lcd-routines.h:
1
voidlcd_data(unsignedchartemp1);
2
voidlcd_string(char*data);
3
voidlcd_command(unsignedchartemp1);
4
voidlcd_enable(void);
5
voidlcd_init(void);
6
voidlcd_home(void);
7
voidlcd_clear(void);
8
voidset_cursor(uint8_tx,uint8_ty);
9
#define F_CPU 8000000
10
#define CLEAR_DISPLAY 0x01
11
#define CURSOR_HOME 0x02
12
#define LCD_PORT PORTD
13
#define LCD_DDR DDRD
14
#define LCD_RS PD4
15
#define LCD_EN PD5
lcd-routines.c:
1
2
#include<avr/io.h>
3
#include"lcd-routines.h"
4
#include<util/delay.h>
5
voidlcd_data(unsignedchartemp1)
6
{
7
unsignedchartemp2=temp1;
8
LCD_PORT|=(1<<LCD_RS);// RS auf 1 setzen
9
temp1=temp1>>4;
10
temp1=temp1&0x0F;
11
LCD_PORT&=0xF0;
12
LCD_PORT|=temp1;// setzen
13
lcd_enable();
14
temp2=temp2&0x0F;
15
LCD_PORT&=0xF0;
16
LCD_PORT|=temp2;// setzen
17
lcd_enable();
18
_delay_us(42);
19
}
20
voidlcd_command(unsignedchartemp1)
21
{
22
unsignedchartemp2=temp1;
23
LCD_PORT&=~(1<<LCD_RS);// RS auf 0 setzen
24
temp1=temp1>>4;// oberes Nibble holen
25
temp1=temp1&0x0F;// maskieren
26
LCD_PORT&=0xF0;
27
LCD_PORT|=temp1;// setzen
28
lcd_enable();
29
temp2=temp2&0x0F;// unteres Nibble holen und maskieren
30
LCD_PORT&=0xF0;
31
LCD_PORT|=temp2;// setzen
32
lcd_enable();
33
_delay_us(42);
34
}
35
voidlcd_enable(void)
36
{
37
LCD_PORT|=(1<<LCD_EN);
38
_delay_us(1);// kurze Pause
39
LCD_PORT&=~(1<<LCD_EN);
40
}
41
voidlcd_init(void)
42
{
43
LCD_DDR=LCD_DDR|0x0F|(1<<LCD_RS)|(1<<LCD_EN);// Port auf Ausgang schalten
Hans Mustermann wrote:
> Habe zwar die LCD-Routine aus dem AVR-GCC-Tutorial mit Erfolg getestet:> Lcd.c:>
1
>#include<avr/io.h>
2
>#include"lcd-routines.h"
3
>intmain(void){
4
>lcd_string("Hello World!");
5
>while(1);
6
>return0;
7
>}
8
>
>> Leider habe ich aber eine vorgegebene Hardware, an der RS und EN nicht> an PD4 bzw. PD5 liegen, sondern> #define LCD_RS PB7> #define LCD_EN PB6
Was ist mit den restlichen Datenleitungen? Liegen die ebenfalls
am Port B? Welche Pins?
Wenn die Datenleitungen ebenfalls am PORTB, Pins PB0 bis PB4 liegen,
dann reichen folgende Änderungen aus:
> #define LCD_PORT PORTB> #define LCD_DDR DDRB> #define LCD_RS PB7> #define LCD_EN PB6
und das LCD sollte am PortB laufen.
Pin #-LCD Bezeichnung-LCD Pin-µC
1 Vss GND
2 Vcc 5V
3 Vee GND oder Poti (siehe oben)
4 RS PD4 am AVR
5 RW GND
6 E PD5 am AVR
7 DB0 offen
8 DB1 offen
9 DB2 offen
10 DB3 offen
11 DB4 PD0 am AVR
12 DB5 PD1 am AVR
13 DB6 PD2 am AVR
14 DB7 PD3 am AVR
das oben steht in der beschreibung. also musste db4 bis db7 an
port0-port3 anschliessen.
Wenn die Datenleitungen wie im Tutorial an PORTD liegen und nur die
Steuerleitungen wie von dir angegeben an PORTB, dann könntest du so
vorgehen
1
#define LCD_PORT PORTD
2
#define LCD_DDR DDRD
3
#define LCD_RS PD4
4
#define LCD_EN PD5
ersetzt du durch
1
#define LCD_CTRL_PORT PORTB
2
#define LCD_CTRL_DDR DDRB
3
#define LCD_RS PB7
4
#define LCD_EN PB6
5
6
#define LCD_DATA_PORT PORTD
7
#define LCD_DATA_DDR DDRD
In jedem Statement (Zeile) mit LCD_RS oder LCD_EN ersetzt du LCD_PORT
durch LCD_CTRL_PORT.
Und in jeder anderen Zeile mit LCD_PORT ersetzt du LCD_PORT durch
LCD_DATA_PORT
Den Abschnitt
kbuchegg wrote:
>Wenn die Datenleitungen ebenfalls am PORTB, Pins PB0 bis PB4 liegen,>dann reichen folgende Änderungen aus: ...
Für so eine einfache Portsubstitution hätte ich dann doch das Forum
nicht bemüht :)
stefan wrote:
>...
Habe jetzt wie beschrieben
#define LCD_CTRL_PORT PORTB
#define LCD_CTRL_DDR DDRB
#define LCD_RS PB7
#define LCD_EN PB6
#define LCD_DATA_PORT PORTD
#define LCD_DATA_DDR DDRD
und in LCD_RS- bzw LCD_EN-Zeilen LCD_PORT durch LCD_CTRL_PORT ersetzt
und in jeder anderen Zeil LCD_PORT durch LCD_DATA_PORT ersetzt und
es klappt tatsächlich!
Weiß nur noch nicht ganz sicher, ob alle andern, "freien" Pins
(PD4...PD7 und PB0...PB5) noch uneingeschränkt anderweitige Aufgaben
erfüllen können, ohne von LCD-Routinen beeinflußt zu werden.
Denn für einen AVR-Neuling sind im Gegensatz zu RS=1 bei Intel so Arten
von “Schiebeoperationen“ zum Portsetzen wie
PORTB |= (1<<PB0);
doch etwas kryptisch (muß mich bei Zeiten mal da einarbeiten; auch ADW
und Interruptsteuerung...).
Dank‘ Dir auf jeden Fall Stefan für die Hilfe!
Hans Mustermann wrote:
> kbuchegg wrote:>>Wenn die Datenleitungen ebenfalls am PORTB, Pins PB0 bis PB4 liegen,>>dann reichen folgende Änderungen aus: ...> Für so eine einfache Portsubstitution hätte ich dann doch das Forum> nicht bemüht :)
Dann solltest du dir zumindest vormerken, das nächste
vollständige Angaben in einer Anfrage zu machen :-)
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
da drin ist die lcd-routine ein bisschen beschissen erstellt worden.
der lernende fragt sich , wo werden die pins0-pins3 in den routinen
herausgestellt. es ist kein bezug da.
also, wenn was dargestellt werder soll, ein bisschen mehr licht ins
dunkel, dann hört auch die diskussion auf.
klaus wrote:
> http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial>> da drin ist die lcd-routine ein bisschen beschissen erstellt worden.
Ein "bisschen beschissen" gibt es nicht. Du solltest dir überlegen,
welche Motivation du einem Artikelautor mit einem solchen Statement
gibst, für dich und andere das Tutorial zu verbessern. Nein, ich bin
nicht der Autor. Ja, ich finde, der Autor hat gute Arbeit geleistet.
> der lernende fragt sich , wo werden die pins0-pins3 in den routinen> herausgestellt. es ist kein bezug da.>> also, wenn was dargestellt werder soll, ein bisschen mehr licht ins> dunkel, dann hört auch die diskussion auf.
Ich habe versucht aus deinem Text deinen Wunsch heraus zu klauben. Wenn
es darum geht anzuzeigen, wo die Leitungen DB4 bis DB8 des LCD am AVR
angeschlossen sind, dann steht das im Tutorialtext eindeutig drin. In
dem Sourcecode steht es nicht eindeutig drin. Die Pinbelegung habe ich
mit einem Kommentar im Quelltext ergänzt. Ich hoffe, das ist auch im
Sinn des Tutorialautors.
klaus wrote:
> http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial>> da drin ist die lcd-routine ein bisschen beschissen erstellt worden.> der lernende fragt sich , wo werden die pins0-pins3 in den routinen> herausgestellt. es ist kein bezug da.
Es wird auch davon ausgegangen, dass 'der Lernende' zumindest
über Grundkenntnisse in C verfügt, bevor er sich an ein LCD wagt.
Für jemanden mit Grundkenntnissen, der auch bereit ist
den Code den er da abtippt zu verstehen, ist es absolut kein
Problem im veröffentlichten Code die entsprechenden Stellen
zu finden, zu verstehen und zu ändern.
Hans Mustermann wrote:
> Habe auch keine Ahnung vom AVR (Kenne nur 8051, wo man mit sbit RS=P1^2;> und dann RS=1; einzelne Portleitungen setzen kann) und weiß nicht wie> das bei Atmel geht
Eigentlich ganz genau so, wie beim 8051, nur daß man noch die
Direction-Bits setzen muß.
Die Bitzugriffe kann man mit einem Macro (SBIT) definieren.
Anbei mal ein Beispiel für 2*40 LCD im 4Bit-Mode mit jedem beliebigen
Pin.
Peter
...Für jemanden mit Grundkenntnissen, der auch bereit ist
den Code den er da abtippt zu verstehen, ist es absolut kein
Problem im veröffentlichten Code die entsprechenden Stellen
zu finden, zu verstehen und zu ändern....
da sehe ich in einem tut anders, da gehört aufklärung rein, auch im
code.
....Anbei mal ein Beispiel für 2*40 LCD im 4Bit-Mode mit jedem
beliebigen
Pin.......
dieses beispiel ist auch für anfänger wunderbar geschrieben, klasse.
mfg
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned long u32;
typedef signed long s32;
welchen vorzug hat diese schreibweise? text zu sparen? u8 statt uint8_t
!
roboter wrote:
> ...Für jemanden mit Grundkenntnissen, der auch bereit ist> den Code den er da abtippt zu verstehen, ist es absolut kein> Problem im veröffentlichten Code die entsprechenden Stellen> zu finden, zu verstehen und zu ändern....>> da sehe ich in einem tut anders, da gehört aufklärung rein, auch im> code.
Ist ganz einfach.
Wenn du denkst, dass diese Stellen nicht gut genug einen
Sachverhalt beschreiben, dann ändere sie. Genau aus diesem
Grund ist das ja ein Wiki.
Warum wird eigentlich bei dir der C code So Farbig angezeigt?
Bei mir geht das garnicht!
Das beispiel von Peter Danneger klappt super!
Damit habe ich vorhin zum ersten mal ein Display angesteuert!
Genial, genau nach so einer angepassten LCD Routine habe ich die letzten
Tage gesucht und nun funktioniert endlich mein LCD an dem AVR NET I/O
von Pollin, vielen Dank an Chris ;-)
Ich verwende PortD und Pin0 sowie Pin1 sind schon belegt vom seriellen
Anschluss, somit musste ich die Daten Pins ab Port2 bis Port5 verwenden
und irgendwie habe ich es vorher mit der Maskierung nie hinbekommen. RS
habe ich an PD6 und EN an PD7, RW konnte ich zum Glück auf GND legen
denn der PortD ist nun voll belegt.
Das LCD funktioniert jetzt jedenfalls problemlos. Was mich nur etwas
verwundert, ich bekomme folgende Warnung beim Make All:
lcd-routines.c:185: warning: 'tmp' may be used uninitialized in this
function
In der Zeile 185 steht --> uint8_t tmp;
Der Befehl set_cursor funktioniert bei mir trotzdem ohne Probleme.
Sebastian Z. wrote:
>.......> Das LCD funktioniert jetzt jedenfalls problemlos. Was mich nur etwas> verwundert, ich bekomme folgende Warnung beim Make All:>> lcd-routines.c:185: warning: 'tmp' may be used uninitialized in this> function>> In der Zeile 185 steht --> uint8_t tmp;
Ja schon, aber was ist mit tmp, wenn "y" < 1 oder > 4 ist?
dann wäre tmp nicht initialisiert, und das prog stürzt ab.
>> Der Befehl set_cursor funktioniert bei mir trotzdem ohne Probleme.
weil Du die Function sicher immer nur mit 1 < y < 5 aufrufst.
Gruß Hanns
Hanns Weil wrote:
>> weil Du die Function sicher immer nur mit 1 < y < 5 aufrufst.>> Gruß Hanns
Ja, stimmt. Ich verwende für y in meinen "set_cursor" Befehlen immer nur
1 oder 2, da ich ein zweizeiliges LCD besitze.
Sollte ich dann lieber tmp am Anfang standardmäßig auf irgendeinen Wert
setzen? Prinzipiell sollte es bei mir ja keinen Programmabsturz geben,
da meine Befehle korrekt mit Zeile 1 oder Zeile 2 verwendet werden.
Sebastian Z. wrote:
...
> Sollte ich dann lieber tmp am Anfang standardmäßig auf irgendeinen Wert> setzen? Prinzipiell sollte es bei mir ja keinen Programmabsturz geben,> da meine Befehle korrekt mit Zeile 1 oder Zeile 2 verwendet werden.
Im Prinzip ja, wenn Du "sauber" arbeiten möchtest.Oder Du lässt es so,
und schaltest die Warnungen ab.
Zum "sauber" arbeiten gehörte auch im switch ein default-statement und
dann keine ANzeige:
z.b. so, wobei du Dich wunderst, warum nichts angezeigt wird:
1
voidset_cursor(uint8_tx,uint8_ty)
2
{
3
uint8_ttmp;
4
// vermeide Compliler-Meldung
5
tmp=0;
6
7
switch(y){
8
case1:tmp=0x80+0x00+x;break;// 1. Zeile
9
case2:tmp=0x80+0x40+x;break;// 2. Zeile
10
case3:tmp=0x80+0x10+x;break;// 3. Zeile
11
case4:tmp=0x80+0x50+x;break;// 4. Zeile
12
// falsche Parameter übergaben:
13
default:{
14
//kannst DU ne Warnung ausgeben und notieren?
15
// log-fle oder so? auf alle Fälle zurück aus der Funktion
16
return;
17
}
18
19
}
20
// hier hätten wir dann die korrekten Parameter,
21
// und können den cursor setzen
22
lcd_command(tmp);
23
}
Wobei Du im Fehler-Falle dann weiter oben in Deinem Programm - nach dem
Aufruf - Probleme mit der eigentlichen Textanzeige bekommst, wenn der
Cursor nicht gesetzt werden konnte.
Daher kannst Du folgendes machen:
aus der void machst Du ne boolean:
Vielen Dank Euch beiden, ich habe mich dann doch für die letztere
Möglichkeit entschieden, da es am einfachsten und schnellsten ging.
Nun bekomme ich keine Warnung mehr und etwas anderes als 1 oder 2 werde
ich bei diesem zweizeiligen LCD sowieso nicht verwenden.
Grüße
Sebastian
Simon K. wrote:
> Chris W. wrote:> Die Warnung kriegst du ganz einfach weg:>>
1
>voidset_cursor(uint8_tx,uint8_ty)
2
>{
3
>uint8_ttmp;
4
>
5
>switch(y){
6
>case1:tmp=0x80+0x00+x;break;// 1. Zeile
7
>case2:tmp=0x80+0x40+x;break;// 2. Zeile
8
>case3:tmp=0x80+0x10+x;break;// 3. Zeile
9
>case4:tmp=0x80+0x50+x;break;// 4. Zeile
10
>default:
11
>return;
12
>}
13
>lcd_command(tmp);
14
>}
15
>
Simon, hast Du es mal getestet?
Ich meine im default Falle ist tmp immer noch nicht mit einem Wert
versehen, und das meckert der Kompiler doch an!
Sebastian, ist mit Simons Variante die Fehlermeldung tatsächlich weg?
Gruß Hanns
Hanns Weil wrote:
> Sebastian, ist mit Simons Variante die Fehlermeldung tatsächlich weg?>>> Gruß Hanns
Ja, ich bekomme nun wirklich keine Warnung mehr angezeigt!
Grüße
Sebastian
Hanns Weil wrote:
> Simon, hast Du es mal getestet?> Ich meine im default Falle ist tmp immer noch nicht mit einem Wert> versehen, und das meckert der Kompiler doch an!
Ja, aber andererseits wird die (uninitialisierte) Variable auch nicht
mehr verwendet, wenn man die Funktion per return beendet ;)
Compiler sind verdammt schlaue Leute! :D
Sebastian Z. wrote:
> Genial, genau nach so einer angepassten LCD Routine habe ich die letzten> Tage gesucht und nun funktioniert endlich mein LCD an dem AVR NET I/O> von Pollin, vielen Dank an Chris ;-)
Ich wundere mich bloß, warum es keinem auffällt, das sehr viel Code
verschwendet wird.
Z.B. taucht dieser Abschnitt 4-mal auf:
1
temp1=temp1&0x0F;
2
3
LCD_PORT_4&=~(1<<LCD_D4);
4
LCD_PORT_5&=~(1<<LCD_D5);
5
LCD_PORT_6&=~(1<<LCD_D6);
6
LCD_PORT_7&=~(1<<LCD_D7);// maskieren
7
8
if(temp1&0x01)LCD_PORT_4|=(1<<LCD_D4);// setzen
9
if(temp1&0x02)LCD_PORT_5|=(1<<LCD_D5);
10
if(temp1&0x04)LCD_PORT_6|=(1<<LCD_D6);
11
if(temp1&0x08)LCD_PORT_7|=(1<<LCD_D7);
12
lcd_enable();
Ehe ich etwas zweimal hinschreibe, mache ich daraus ne Unterfunktion.
Das ist zum einen Schreibfaulheit und zum anderen Zuverlässigkeit.
Was nur einmal dasteht, kann auch nur einmal falsch sein (muß nur einmal
korrigiert werden).
Es ist eine beliebte Fehlerquelle, in Copy&Paste Codemonstern etwas
nicht an allen Stellen zu korrigieren und sich dann zu wundern, warum
der Fehler noch da ist.
Mit Unterfunktionen wird dann so ein LCD-Treiber gleich viel schlanker
und durchschaubarer:
Beitrag "Re: LCD nicht nur für einen Port in C"
Zum Thema Default-Zweig:
Ich würde als Default die 1.Zeile setzen.
Return halte ich für die schlechteste Lösung, da man damit Fehler
verschleiert, d.h sich die Fehlersuche nur selber schwer macht.
Peter
Peter Dannegger wrote:
------
> Es ist eine beliebte Fehlerquelle, in Copy&Paste Codemonstern etwas> nicht an allen Stellen zu korrigieren und sich dann zu wundern, warum> der Fehler noch da ist.>>> Mit Unterfunktionen wird dann so ein LCD-Treiber gleich viel schlanker> und durchschaubarer:>
Völlige Zustimmung
> Zum Thema Default-Zweig:> Ich würde als Default die 1.Zeile setzen.> Return halte ich für die schlechteste Lösung, da man damit Fehler> verschleiert, d.h sich die Fehlersuche nur selber schwer macht.
Peter, wenn Du die 1.Zeile als default benutzt, verschleierst Du
ebenfalls Fehler, indem Du fehlerhafte Parameter einem fehlerfreien
gleichsetzt!
Ich bin nach wie vor der Meinung, daß ein
1
default:returnfalse;
einer boolean funktion, mit auswertung des Rückgabewertes in der
aufrufenden func ( z.B Anzeige in Zeile 1 blinkend) des sicherere Weg
ist.
Klar ist mir, daß in diesem Fall sicherlich keine Zeile außerhalb des
Displays aufgerufen werden wird; aber, wie Du selber sagst, gehört
mehrfach zu benutzender Code in eine eine Subroutine. Wenn diese sich
später einmal verselbständigt, und void bleibt, wirst Du irgenwann mal -
bei Deiner Lösung - nicht mehr wissen, ob nun ein Fehler auftrat oder
nicht.
Aber vielleicht bin ich da zu pingelig
Gruß Hanns
Hallo,
ich habe mit disem Code gerade mein LCD am laufen zu bekommen :-) bzw.
Teilerweise :-(
habe ein 4x20 LCD und wenn ich mit dem gesamte Code compiliere und
flashe kommen nur die erste 2 Zeile mit meine String
"hallo1ghhghjgjghgjkk" (20 Zeile)
wie bekomme ich die 3 und 4 Zeile ??
hier steht:
// 4Bit 2 Zeilen 5x7
lcd_command(0x28);
wie ist dann für die 4 Zeile? habe rum probiert aber leider nichts !
Gruß
Martin
4 zeilige Displays haben meistens zwei Controller mit 2 Enable
Leitungen.
Such mal nach Wintek 2704. Das ist so ein Dislay und hierfür gibt es
auch schon fertigen Code. (Ist ein Pollin Display)
Ich hab das Problem das ich an einem Port bin nur ein durcheinander mit
den Pins habe
kann mir da mal jemand das auseinanderdividieren
ich bring das LCD nicht ans laufen.
Pinnbelegung
// LCD_RS =PA3 , LCD_EN = PA1 ; DB4= PA0 DB5= PA2 DB6 = PA4 DB7=PA6 ==
LCD_AD =0x55 LCD_ADA = 0xAA
1
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
Hallo,
4x20 haben üblicherweise nur einen Controller, 80 Zeichen ist die
Ramgröße der 44780 und kompatiblen.
Initialisiert als 2-zeilig, die 1 und 3. Zeile sowie die 2. und 4. Zeile
sind dann von den Adressen direkt hintereinander.
Erst bei mehr als 80 Zeichen (z.B. das Pollin 4x27) ist ein zweiter
Controller nötig.
Gruß aus Berlin
Michael
Es geht nicht um die Adresse der speicherstellen sondern um die
Portbelegung.
ich wollte nicht das grosse programm verwenden das jeden pin des LCD an
einem anderen port hat
>ich wollte nicht das grosse programm verwenden das jeden pin des LCD an>einem anderen port hat
Das wirst du bei deiner verpfuschten Portbelegung aber müssen.
>win reinschiften der temp in die portD adresse>nach dem motto>PortD.0=temp1.0 geht nicht?
Jetzt auch noch mit Strukturen arbeiten?
Wenn du wenigstens die Datenleitungen für
das LCD in einer Reihe am Port angeschlossen hättest,
dann könnte man das Beispiel aus dem Tutorial
relativ einfach anpassen. Du musstest sie aber
unbedingt alle auseinanderreissen. Nimm Peters
Code. Das was man tun müsste um deine Pinbelegung
gerade zu biegen wird auch nicht kleiner.