Forum: Compiler & IDEs Mit dem ATMega32 zwei Displays betreiben?


von Christian O. (carion)


Lesenswert?

Guten Abend,

ich zerbreche mir im Moment meinen Kopf.

Bin noch nicht sehr weit in der Materie des Programmierens eines 
Mikrocontrollers in GCC.

Trotzdem versuche ich Momentan mit meinem ATMega32 an zwei LCD`s Werte 
auszugeben.

Die Hardware steht soweit und seperat initialisieren klappt auch.

Nur tue ich mich sehr schwer darin die LCD-Routine (lcd-pollin-jumper.h) 
so umzuschreiben,
dass ich davon zwei in das Programm einbinden kann um die LCD`s 
nacheinander initialisieren zu können.
Ich hatte also vor beispielsweise LCD1 mit LCD_Init() und LCD2 mit 
LCD_Init2() zu initialisieren.
Ist diese Idee generell machbar? Oder kann mir einer zu etwas leichterem 
Raten?

Ich hoffe mir kann einer weiterhelfen.

Mit freundlichen Grüßen

Carion

von Klaus W. (mfgkw)


Lesenswert?

doch, das geht schon.
Aber auch beim Schreiben musst du natürlich sagen, welches gerade 
gemeint ist.

Falls du keine Angst vor C++ hast: ich habe hier irgendwann mal eine 
Version veröffentlicht, die ohne Änderungen mit mehreren LCDs umgehen 
kann.

von Klaus W. (mfgkw)


Lesenswert?


von Peter D. (peda)


Lesenswert?

Am einfachsten nimmst Du eine Routine für LCDs mit 2 Enable-Anschlüssen 
als Beispiel und paßt sie an.
D.h. beide LCDs werden im Programm als ein LCD angesprochen, bloß mit 
doppelter Zeilenzahl.


Peter

von Christian O. (carion)


Lesenswert?

Daran hatte ich auch schon gedacht,
Meinst du beispielsweise ein Routine für ein 4x20 Zeilen Display?

Müsste ich dann die RS und RW auf eine Strippe legen oder kann ich das 
so lassen, dass diese jeweils einen eigenen Pin zugeordnet sind? Also 
momentan habe ich die RW, RS und E
von LCD1 und LCD2 auf PortB Pin 0-6

Kennst du so eine Routine, die du mir empfehlen könntest?

In C++ wollte ich eigentlich nicht arbeiten, da dass Programm zum 
größten Teil schon steht und es schon ca 3Jahre her ist dass ich letzte 
mal in C++ programmiert hatte.

von Sam .. (sam1994)


Lesenswert?

Christian O. schrieb:
> Meinst du beispielsweise ein Routine für ein 4x20 Zeilen Display?
Müssten 4*40 (160 Zeichen) sein. Ich meine der HD-Controller kann 80 
Zeichen.
> Müsste ich dann die RS und RW auf eine Strippe legen oder kann ich das
> so lassen, dass diese jeweils einen eigenen Pin zugeordnet sind?
Natürlich lassen sich alle Pins bis auf EN übereinander legen, ist auch 
sinnvoll um Pins zu sparen.

von Christian O. (carion)


Lesenswert?

Guten Abend Leute,

ich habe mir jetzt ettliche Routinen angeguckt, aber die sind ja alle 
vom gleichen Autor. In der Routine kann man auch eintragen, wie viele 
Zeilen das Display hat, welches man verwendet.

/**
 *  @name  Definitions for Display Size
 *  Change these definitions to adapt setting to your display
 */
#define LCD_LINES           4     /**< number of visible lines of the 
display */
#define LCD_DISP_LENGTH    16     /**< visibles characters per line of 
the display */
#define LCD_LINE_LENGTH  0x40     /**< internal line length of the 
display    */
#define LCD_START_LINE1  0x00     /**< DDRAM address of first char of 
line 1 */
#define LCD_START_LINE2  0x40     /**< DDRAM address of first char of 
line 2 */
#define LCD_START_LINE3  0x14     /**< DDRAM address of first char of 
line 3 */
#define LCD_START_LINE4  0x54     /**< DDRAM address of first char of 
line 4 */
#define LCD_WRAP_LINES      0     /**< 0: no wrap, 1: wrap at end of 
visibile line */


Hab irgendwie eine Denkblockade ... Ich benötige ja eine Routine, die 
zwei Enable eingänge hat. Ich kann in der Routine aber keine zwei 
Enables eintragen.


#ifndef LCD_PORT
#define LCD_PORT PORTC
#endif

#define LCD_DATA0_PORT   PORTC     /**< port for 4bit data bit 0 */
#define LCD_DATA1_PORT   PORTC     /**< port for 4bit data bit 1 */
#define LCD_DATA2_PORT   PORTC     /**< port for 4bit data bit 2 */
#define LCD_DATA3_PORT   PORTC     /**< port for 4bit data bit 3 */
#define LCD_DATA0_PIN    4         /**< pin for 4bit data bit 0  */
#define LCD_DATA1_PIN    5         /**< pin for 4bit data bit 1  */
#define LCD_DATA2_PIN    6         /**< pin for 4bit data bit 2  */
#define LCD_DATA3_PIN    7         /**< pin for 4bit data bit 3  */
#define LCD_RS_PORT      PORTB     /**< port for RS line         */
#define LCD_RS_PIN       0         /**< pin  for RS line         */
#define LCD_RW_PORT      PORTB     /**< port for RW line         */
#define LCD_RW_PIN       1         /**< pin  for RW line         */
#define LCD_E_PORT       PORTB     /**< port for Enable line     */
#define LCD_E_PIN        2         /**< pin  for Enable line     */

Wie zum guckuck kann ich dann mit der Routine ein vierzeilen, bzw. zwei 
zweizeilen displays ansteuern

von Klaus D. (kolisson)


Lesenswert?

Ohhh Leute, was seit ihr kompliziert.
Ich kann kein C und kein C++ aber wenn es sich um ein simples
Display handelt sollte es doch wohl ausreichen den Enable PIn des
Display über zwei verschiedene Pin des MC anzusteuern.
Da muss der liebe Autor: Christian O. (carion)  doch garnicht
an der Software rumfummeln.
Er Enabled Display-1 und macht Init
dann enabled der Junge das Display 2 und initialisiert  es.

Wenn er nun noch einen simplen Transistor als Inverter einbaut
benötigt er sonst garnix mehr.

Ein einziger Portpin enabled das eine oder das andere Display.
"If not High then low"

Nach der INIT ist es doch sowieso gegessen.
Dann schreibt man im Programm halt vor dem PRINT einfach einen
befehl zusätzlich:

LDC2 = false
Print "hello World"

oder eben

LCD2 = true
Print " Ich bin der 2te"


Wo ist denn nun das Problem? Liegt es daran, dass Transistoren
3 Beine haben und das wiederum schlecht in Einklng zu bringen ist
mit dem Leben.

klaus de lisson

von Klaus W. (mfgkw)


Lesenswert?

Nicht nur, daß du kein C und kein C++ kannst - du hast auch keine 
Ahnung, was das Enable macht.
@Christian: lass dich nicht davon nicht verwirren, das hilft dir nicht 
weiter.

von Dohle (Gast)


Lesenswert?

Hi Christian,

vielleicht hab ich es übersehen. In welcher Programmiersprache willst du 
denn dein Programm schreiben. Assembler oder Basic (Bascom)? In 
Assembler ist das recht einfach. Im Grunde kannst du mehrere LCD's 
ansprechen. Wichtig ist hier tatsächlich das Enable Bit des 
angesprochenen LCD Displays. Es werden die entsprechenden Daten auf den 
Datenleitungen gelegt und mit dem Enable Bit (kurzer Impuls) in das 
gewünschte LCD eingelesen. Je nach dem welches LCD den Impuls bekommen 
hat werden die Daten auch dem entsprechenden LCD zugewiesen. Habe das 
bereits für einen Flugsimulator Autopiloten programmiert und das klappt 
tadellos. Schau dir hier einfach mal das Tutorial für Assembler an (LCD) 
dann wirst du das sicher verstehen.

Gruß Frank

von Klaus W. (mfgkw)


Lesenswert?

Dohle schrieb:
> vielleicht hab ich es übersehen.

Ja, ganz oben steht "gcc".
Das ist dann wahrscheinlich nicht Assembler und nicht BASIC.

von Dohle (Gast)


Lesenswert?

Hi Klaus,

Hmmm.... mich irritierte der Satz von Christian :

"In C++ wollte ich eigentlich nicht arbeiten, da dass Programm zum
größten Teil schon steht und es schon ca 3Jahre her ist dass ich letzte
mal in C++ programmiert hatte."

Gruß Frank

von Klaus W. (mfgkw)


Lesenswert?

Christian O. schrieb:
> ich habe mir jetzt ettliche Routinen angeguckt, aber die sind ja alle
> vom gleichen Autor.

nein, nicht alle.

Ich hatte dir oben schon mal einen Link zu der C++-Version gegegeben 
(der leider durch meinen Fehler nicht funktioniert hatte, das habe ich 
eben korrigiert).

Damit kannst du ohne irgendwelche Fummeleien in Headerdateien sehr 
einfach mehrere LCD verwenden.
Dafür muß man halt C++ nehmen, aber viel darüber wissen muß man dafür 
nicht. Dein restliches Programm darf C bleiben, macht ja nix.

Beispiel für ein 4*20 und ein 2*16, die sich nur im Enable-Pin 
unterscheiden (B.5 bzw. B.6):
1
#include "LCD44780.h"
2
...
3
  AnyWare::LCD44780    lcd1( AnyWare::LCD44780::LCD_Type_4x20,
4
                             PORTB, 0, // Port, Pin LCD-DB4
5
                             PORTB, 1, // Port, Pin LCD-DB5
6
                             PORTB, 2, // Port, Pin LCD-DB6
7
                             PORTB, 3, // Port, Pin LCD-DB7
8
                             PORTB, 4, // Port, Pin LCD-RS
9
                             PORTB, 5  // Port, Pin LCD-Enable1
10
                             );
11
  AnyWare::LCD44780    lcd2( AnyWare::LCD44780::LCD_Type_2x16,
12
                             PORTB, 0, // Port, Pin LCD-DB4
13
                             PORTB, 1, // Port, Pin LCD-DB5
14
                             PORTB, 2, // Port, Pin LCD-DB6
15
                             PORTB, 3, // Port, Pin LCD-DB7
16
                             PORTB, 4, // Port, Pin LCD-RS
17
                             PORTB, 6  // Port, Pin LCD-Enable1
18
                             );
19
20
  lcd1.printf( 0, 0, 20, "ich bin ein 4x20" );
21
  lcd2.printf( 0, 0, 16, "ich nur 2x16" );
22
...

Die Pinzuordnungen können praktisch beliebig gewählt werden soweit auf 
dem jeweiligen AVR möglich, die LCD-Typen können frei gemischt werden 
und die Verwendung ist ziemlich schlicht.
Die gezeigte printf-Methode hat vorweg drei Parameter für x und y (ab 0 
gezählt) und die maximale Ausgabelänge.
Es gibt auch noch andere Ausgabefunktionen, siehe Headerdatei.

Für ein 4*40 würde man noch zwei Parameter mehr beim Initialisieren 
angeben für den zweite Enable-Pin (Port und Bitnummer).

von Klaus W. (mfgkw)


Lesenswert?

Dohle schrieb:
> Hi Klaus,
>
> Hmmm.... mich irritierte der Satz von Christian :
>
> "In C++ wollte ich eigentlich nicht arbeiten, da dass Programm zum
> größten Teil schon steht und es schon ca 3Jahre her ist dass ich letzte
> mal in C++ programmiert hatte."
>
> Gruß Frank

Dann würde ich mal darauf tippen, daß er in C arbeiten will.

Dann soll er sich aber nicht daran stören, wenn die LCD-Ansteuerung in 
C++ gestrickt ist. Das muß er ja nicht verstehen sondern nur die 
Verwendung abkupfern und kann dann seinen ganzen restlichen Kram in C 
machen.
Die Verwendung von C++-Libs ist ja sehr einfach, wesentlich einfacher 
als das Headergefummel in C.

von Peter D. (peda)


Lesenswert?

Dieses C++ Programm hat allerdings den Nachteil, daß es die Codegröße 
verdoppelt. Es werden alle LCD-Funktionen doppelt angelegt.

Nötig ist aber nur eine unterschiedliche Ansteuerung des E-Pins.
Das kann man bequem über eine Variable enscheiden, die entsprechend der 
gewünschten Zeilennummer gesetzt wird.
1
static uint8_t lcd_select;
2
3
static void lcd_nibble( uint8_t d )
4
{
5
  LCD_D4 = 0; if( d & 1<<4 ) LCD_D4 = 1;
6
  LCD_D5 = 0; if( d & 1<<5 ) LCD_D5 = 1;
7
  LCD_D6 = 0; if( d & 1<<6 ) LCD_D6 = 1;
8
  LCD_D7 = 0; if( d & 1<<7 ) LCD_D7 = 1;
9
10
  if( lcd_select & 1<<0 )
11
    LCD_E0 = 1;
12
  if( lcd_select & 1<<1 )
13
    LCD_E1 = 1;
14
  _delay_us( 1 );                       // 1us
15
  LCD_E0 = 0; 
16
  LCD_E1 = 0;
17
}

Man kann das bis auf 8 LCDs erweitern, ohne das der Code 8-fach groß 
wird.
Beim Init kann man lcd_select auf 0xFF setzen, damit alle LCDs zugleich 
initialisiert werden.


http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=102296


Peter

von Christian O. (carion)


Lesenswert?

Mhh ... Also ich schreibe in assambler,
wollte damit auch weiter arbeiten.

Bin da noch nicht so gut drin und weiß nicht,
Wie ich da c++ einbauen soll.

Kann ich nicht zwei gleiche Routinen in das Programm einbauen
Und von einer Routine dann die Befehle
Bsp.:printf und gotoxy mit einer 2 dahinter versehen und die dann im 
Programm so ansprechen?

von Klaus W. (mfgkw)


Lesenswert?

Peter Dannegger schrieb:
> Dieses C++ Programm hat allerdings den Nachteil, daß es die Codegröße
> verdoppelt. Es werden alle LCD-Funktionen doppelt angelegt.

Sicher?

von Peter D. (peda)


Lesenswert?

Klaus Wachtler schrieb:
> Sicher?

Ist meine Vermutung, da ja alle Pins unterschiedlich definierbar sind 
und die Funktionen unterschiedlich heißen.

Ich verstehe von C++ allerdings nur Bahnhof.
Die Vermutung kann daher auch falsch sein.


Peter

von Klaus W. (mfgkw)


Lesenswert?

Es hat sich ja eh erledigt, weil er nach 2 Tagen verrät, daß er es in 
Assembler machen will.

Trotzdem zur Info:
Gegenüber deiner ursprünglichen Lösung braucht man natürlich etwas mehr 
Code, aber in erster Linie nur, weil die Pins vollkommen frei 
definierbar sind, und nicht auf demselben Port liegen müssen.
Das hat aber mit C++ nichts zu tun.

Abgesehen davon gibt es nicht mehrere Funktionen, die sich nur im Namen 
unterscheiden würden.
lcd1 und lcd2 sind die Variablen, hinter denen sich die Konfiguration 
versteckt (Pinzuordnung, LCD-Typ, aktuelle Cursorposition etc.).
Beide werden mit derselben Funktion bzw. Methode initialisiert, nämlich 
dem Konstruktor der Klasse LCD44780. Da unterscheiden sich nur jeweils 
die Parameter, aber nicht die Funktion.

Je nachdem, wie kompliziert die Situation ist, werden die Funktionen eh 
inline eingebaut.
Wenn man dieselbe Funktionalität (mehrere LCD unterschiedlichen Typs 
gleichzeitig möglich etc.) nutzt, erzeugt man in C oder Assembler auch 
entsprechend mehr Code.
Nutzt man das dagegen alles nicht, hat der Compiler halt etwas mehr Spaß 
beim Optimieren.

von Karl H. (kbuchegg)


Lesenswert?

Christian O. schrieb:

> Kann ich nicht zwei gleiche Routinen in das Programm einbauen
> Und von einer Routine dann die Befehle
> Bsp.:printf und gotoxy mit einer 2 dahinter versehen und die dann im
> Programm so ansprechen?

Natürlich KANNST du. Wer sollte dich daran hindern?

Die Frage ist aber eine andere: Wie sinnvoll ist es, 2 Funktionen 
gleichen Namens zu haben, die sich nur darin unterscheiden, dass letzten 
Endes ganz unten eine andere Funktion aufgerufen wird, die in dem einen 
Fall den (zb) PB3 Pin als Enable-Pin benutzt und im anderen Fall den 
(wieder beispielsweise) PD5.
Genau dazu gibt es in C Funktionsargumente, damit man von aussen steuern 
kann, was genau eine Funktion tun soll. Und wenn man das nicht tun will, 
gibt es immer noch die Möglichkeit, die entsprechende Information über 
globale Variablen da quer einzubringen.

Wie bei vielen anderen Dingen im Leben, hast du 2 Möglichkeiten: 
entweder du lässt deine Muskeln spielen und bereinigst die Situation 
durch Code-Duplizierung oder du benutzt deinen Verstand und 
strukturierst deinen Code so, dass er mit mehreren Displays gleichen 
Typs umgehen kann.

von Christian O. (carion)


Lesenswert?

@ Peter Dannegger,

Das was du da geschrieben hast scheint plausibel,
Kann ich denn den Code als Routine verwenden und das
Lcd_select Bit in dem normalen Programm setzen oder geht das dann nur in 
der Routine oder muss ich alles ins Programm schreiben?

Tut mir leid für die ganzen unwissenden Fragen, aber ich Brauch da 
irgendwie gerade etwas länger um den roten Faden zu finden und ich 
bedanke mich für eure Geduld

von Karl H. (kbuchegg)


Lesenswert?

Christian O. schrieb:

> Kann ich denn den Code als Routine verwenden und das
> Lcd_select Bit in dem normalen Programm setzen oder geht das dann nur in
> der Routine oder muss ich alles ins Programm schreiben?

Du kannst es so machen wie du das willst:
Vor Aufruf einer Ausgabefunktion ein LCD durch Anwählen mit dem 
Lcd_select auswählen,
oder eigene Funktion für beide LCD schreiben, die Lcd_select richtig 
stellen und dann in die gemeinsamen Ausgabefunktionen verzweigen.

So wie es DIR angenehm ist. MÜSSEN tust du gar nichts. Du bist der 
'Erfinder' und du machst das (im Rahmen der Möglichkeiten der 
Programmiersprache) so, wie du es für richtig befindest und was du 
programmieren kannst.

Letzten Endes geht es nur darum, dass in der hierarchiemässig untersten 
Funktion, der Funktion die ein Nibble ausgibt, die Information bereit 
steht. Wie sie das macht - du entscheidest das.

von Peter D. (peda)


Lesenswert?

Klaus Wachtler schrieb:
> Nutzt man das dagegen alles nicht, hat der Compiler halt etwas mehr Spaß
> beim Optimieren.

Interessant wäre, kann er sowas überhaupt optimieren?
Sich eine zusätzliche Hilfsvariable für den Unterschied zu nehmen, kann 
bestimmt nur ein Mensch.

Der AVR-GCC macht auf mich nicht den Eindruck, als daß er besonders 
clever optimiert. Er moved gerne mal Register unnötig hin und her.
Er optimiert einigermaßen brauchbar, aber nicht gut.


Peter

von Christian O. (carion)


Angehängte Dateien:

Lesenswert?

Guten Abend nochmal,

ich habe es geschafft ^^

Nach langem hin und her habe ich eine passende Routine gefunden, die 2 
Enable-Eingänge hat. Ich habe dann noch die DB0-3,RW und RS vom LCD1 
aufs LCD2 gebrückt und dann lief es. Also laufen meine beiden 2x16LCD`s 
nun theoretisch als ein 4x20LCD und das auch noch ganz gut ^^

Ich musste noch in die Routine den Befehl lcd_printf (um meine 
Fließkommazahlen ausgeben zu können) einbauen und jetzt steht dem 
weiteren programmieren des µC nichts mehr im weg.

Ich bedanke mich für die ganzen Ratschläge und Hilfestellungen, auch 
wenn Ihr es schwer hattet mir diese zu erklären, geschweige denn 
schmackhaft zu machen :)

Für alle die das gleiche Problem haben, ich habe die Routine 
winket2704.h verwendet

einen schönen Abend noch allen und viel Spaß beim programmieren, ich 
werde ihn zumindest wieder haben ^^

von Sascha W. (arno_nyhm)


Lesenswert?

Wenn ich den angezeigten Spannungswert im Display so sehe, kommt ja 
direkt meine eigene Kuriosität und Hochspannungs-Affinität durch... Da 
kann ich einfach nicht anders als zu fragen:
Was ist das denn für ein Apparat, der mit 178.49kV läuft? Und wo kommen 
die her, wie sind sie stabilisiert (dreistellige Kilovolts mit zwei 
Nachkommastellen - Oida!) wie misst Du sie und wozu sind sie überhaupt 
da?!

...viele Fragen, aber wenn ich solche Spannungwerte lese siegt bei mir 
ganz schnell die Neugierde und ich habe direkt Ozongeruch in der Nase ;)

Grüße
Sascha

von Christian O. (carion)


Lesenswert?

Die Werte sind im Moment noch unumgerechnet,
ginge lediglich erstmal darum Werte auf beiden Displays auszugeben und 
diese in Fließkommazahlen darzustellen.

Das ganze soll später mal ein Transformatoren Monitoringsystem werden 
und im idealfall unter anderem die Werte zwischen 9-11kV oder zwischen 
110-123kV anzeigen. Der bekommt seine Werte später von einer SPS (Ja ich 
weiß, ginge auch direkt auf den Mikrocontroller, aber das soll noch 
weiterentwickelbar sein) und diese bekommt die Werte umgewandelt von 
einem Simeas.

Aber das wird die nächsten Tage erst noch alles programmiert, die 
Displays waren zu hartnäckig und wollten einfach nicht so wie ich.

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.