Atmel AVR Mikrocontroller mit OpenSuse: Ansteuerung vonLCD-Displays

LCD-Displays gibt es in großer Mannigfaltigkeit. Üblich ist, dass die Displays bereits einen Controller enthalten, der die Pixel ansteuert. Auch in diesem Bereich hat eine Art Standardisierung stattgefunden, so dass nur gegen wenige Controllertypen programmiert werden muss. Ein sehr gebräuchlicher Controller ist der HD44780.

LCD-Display Seiko L2432 mit 2 Zeilen zu 24 Zeichen

Auf dem Display sitzt ein Controller HD44780, also absoluter „Standard“.
Das Display hat 2 Zeilen zu 24 Zeichen.


Vorderseite des Seiko L2432.

Unterseite des Seiko L2432, rechts der Controller HD44780. Ganz rechts der 14-polige Konnektor, an den ein Flachbandkabel angelötet wurde.

Das Display wird über einen Connector mit 14 Pins angeschlossen. Sogar die Pin-Belegung des Connectors ist quasi-standardisiert.

Pin Function
1 GND
2 V+, normalerweise +5V
3 VLC, Kontrastreglung
4 RS – Register Set
5 RW – Read/Write
6 E – Enable
7 DB0
8 DB1
9 DB2
10 DB3
11 DB4
12 DB5
13 DB6
14 DB7
15 (A – Anode for Backlight) optional
16 (K – Kathode for Backlight) optional

In Rot sind die für die 4-Bit-Ansteuerung notwendigen Pins gekennzeichnet. Diese müssen mit freien Ports des AVRs (bzw. V+ und GND) verbunden werden.

Neben der Datenleitungen (8 Datenbits D0..D7 bzw. 4 Datenbits D4..D7 im 4-Bit-Modus, Controllerauswahl, R/W, RS, Enable sowie GND und Vcc), muss unbedingt auch -wenn vorhanden- die Kontrastregelung angeschlossen werden. Das gezeigte Display benötigt beispielsweise eine Spannung, die irgendwo zwischen 0 und 5V liegt. Wird keine passende Regelspannung für den Kontrast eingestellt, ist absolut nichts zu sehen und man kann nicht erkennen, ob das Display überhaupt funktioniert! Die Kontrastspannung wird über einen Trimmer angeschlossen von typischerweise 10 KOhm, so dass man auf unterschiedliche Lichtverhältnisse reagieren kann.

Als Open Source gibt es eine Bibliothek für den AVR-Controller (LCD Library von Peter Fleury), welche LCDs ansteuern kann. Sie nutzt die 4-Bit-Ansteuerung (oder einen Memory-Mapped-Mode, der bei bestimmten AVRs möglich ist). Je nach Controller wird ein 8-Bit-Modus, ein 4-Bit-Modus oder beides unterstützt. Der 4-Bit-Modus hat den Vorteil, dass am AVR weniger Pins benötigt werden.

Hinweis: Im 4-Bit-Modus werden die Datenleitungen D4..D7 eines LCD-Moduls/Controllers genutzt. Auch wenn in der Peter-Fleury-Bibliothek immer von D0..D3 (=die logischen Datenleitungen des 4-Bit-Modes) die Rede ist, gemeint sind im 4-Bit-Modus immer D4..D7 (=die physikalischen Datenleitungen des LCD-Moduls).

Im folgenden ist die Nutzung der Funktionen dargestellt (Codeauszug, kompletten Source Code gibts weiter unten):

...
#include "lcd.h"
 
...
/* clear display and home cursor */
lcd_clrscr();
 
/* put string to display (line 1) with linefeed */
lcd_puts("LCD Test Line 1n");
 
/* cursor is now on second line, write second line */
lcd_puts("Line 2");
 
/* move cursor to position 8 on line 2 */
lcd_gotoxy(7,1);
 
/* write single char to display */
lcd_putc(':');
 
DO("wait for keynr");
/* wait until push button PD2 (INT0) is pressed */
wait_until_key_pressed();
DO("after keynr");
 
/*
 * Test 2: use lcd_command() to turn on cursor
 */
 
/* turn on cursor */
lcd_command(LCD_DISP_ON_CURSOR);
...

Die Bibliothek muss im Makefile als zusätzliche Bibliothek eingetragen werden. Die Funktionsprototypen stehen alle im Header „lcd.h“, den man im eigenen Programm eintragen muss.


Seiko L2432 angeschlossen und erste Datenausgabe

Die Bibliothek ist auch konfigurierbar, es gibt z.B. Controller, die mehr als zwei Zeilen beherrschen, dies kann in der Bibliothek eingestellt werden. Die Spezifika des Displays (Anzahl Zeilen, Anzahl Zeichen, ..) und die Anschlussbelegung am AVR muss in „lcd.h“ eingetragen werden.

Im Code findet sich übrigens auch der Makroaufruf DO(„text“), dies ist ein Debug-Makro („DEBUG OUT“) von mir, welchen Daten via RS232 ausgibt. Damit kann man erkennen, was der Controller gerade tut. Wie die RS232-Schnittstelle angesprochen wird, wird weiter unten in meinem Text besprochen.

LCD-Displays Wintek WD-C2704M-1HNN mit 4 Zeilen zu 27 Zeichen

Das Display hat 4 Zeilen zu 27 Zeichen. Auf dem Display sitzen zwei Controller, kompatibel zum HD44780. Einer ist für die oberen zwei Zeilen, der andere für die unteren zwei Zeilen verantwortlich. Beide Controller teilen sich alle I/O-Leitungen. Statt einem E(nable)-Pin gibt es zwei Enable-Pins E1 und E2 für die Auswahl zwischen den beiden Controllern.

Die Bibliothek von Peter Fleury unterstützt allerdings nur Displays mit einem Controller. Ich habe das Problem dadurch gelöst, dass ich in alle Funktionen der Bibliothek wo notwendig zwischen beiden Controllern unterschieden habe. So wird z.B. aus der Funktion lcd_clrscr(void) eine Funktion lcd_clrscr(int controllerId), der die Werte LCD_CRTLR_1 und LCD_CRTLR_2 übergeben werden können.

Die Pin-Belegung des Moduls ist in folgender Tabelle dargestellt. Die oben erwähnte „Quasi-Standardisierung“ist hier leicht verändert, da 2 Enable-Pins existieren.

Pin Function
1 GND
2 V+, normalerweise +5V
3 VLC, Kontrastreglung (0..4V)
4 RS – Register Set
5 RW – Read/Write
6 E1 – Enable Controller 1
7 E2 – Enable Controller 2
8 DB0
9 DB1
10 DB2
11 DB3
12 DB4
13 DB5
14 DB6
15 DB7
16-21 Key Input for Input Keys

In Rot sind die für die 4-Bit-Ansteuerung notwendigen Pins gekennzeichnet. Diese müssen mit freien Ports des AVRs (bzw. V+ und GND) verbunden werden.

Im folgenden ist die Nutzung der modifizierten Funktionen dargestellt (Codeauszug, kompletten Source Code gibts weiter unten):

...
#include "lcd.h"
 
...
/* clear display and home cursor */
lcd_clrscr(LCD_CRTLR_1);
lcd_clrscr(LCD_CRTLR_2);
 
/* put string to display (line 1) with linefeed */
lcd_puts(LCD_CRTLR_1, "LCD Test Line 1n");
 
/* cursor is now on second line, write second line */
lcd_puts(LCD_CRTLR_1, "Line 2");
 
/* move cursor to position 8 on line 2 */
lcd_gotoxy(LCD_CRTLR_1, 7,1);
 
/* write single char to display */
lcd_putc(LCD_CRTLR_1,':');
 
DO("wait for keynr");
/* wait until push button PD2 (INT0) is pressed */
wait_until_key_pressed();
DO("after keynr");
 
/*
 * Test 2: use lcd_command() to turn on cursor
 */
 
/* turn on cursor */
lcd_command(LCD_CRTLR_1, LCD_DISP_ON_CURSOR);
...

Für LCDs mit nur einem Controller ist die Bibliothek natürlich so wie sie ist zu benutzen.


Im Bild ist der Anschluss des LCDs an das einfache AVR-Board zu erkennen.
Experimentalaufbau – die Display-Schutzfolie wurde auf dem Display belassen, daher die schlechte Ablesbarkeit.

Hier der Source-Code zum Display:  lcd_2controller.zip

Erschöpfende Infos zur LCD-Ansteuerung unter http://www.mikrocontroller.net/articles/AVR-Tutorial:_LCD

Atmel AVR Mikrocontroller mit OpenSuse: RS232-Ansteuerung

RS232-Ansteuerung mit dem AVR kann durch direktes Ansteuern zweier Pins und entsprechendem Code per Hand gemacht werden. Dies ist allerdings aufwendig und nicht notwendig, weil in den meisten AVRs ein UART enthalten ist (manchmal auch zwei), der das Low Level RS232 Handling (Bitschiebereien) schon erledigt. Man muss dann nur noch komfortabel mit Byte-Werten statt mit Bitverschiebungen operieren.

Keine serielle Schnittstelle am PC? Es gibt für unter 10 Euro Kabel, die als Adapter zwischen RS232 und USB dienen können. Unter Linux klinkt sich eine solches Kabel in den Gerätebaum z.B. als „/dev/ttyUSB0“ ein und kann dann genauso wie eine „echte“ serielle Schnittstelle genutzt werden. Bei meinem OpenSuse 11.2 sieht die Einbindung (mittels dmesg ausgegeben) wie folgt aus:

[ 3529.694778] usbserial: USB Serial Driver core
[ 3529.705376] USB Serial support registered for ch341-uart
[ 3529.705409] ch341 6-1:1.0: ch341-uart converter detected
[ 3529.718371] usb 6-1: ch341-uart converter now attached to ttyUSB0
[ 3529.718650] usbcore: registered new interface driver ch341

Die Ansteuerung des UARTs kann mittels Interrupt oder Polling erfolgen.

RS232 mit Polling

Das Polling kann unter C elegant gelöst werden, indem die Funktion fdevopen() genutzt wird. Dieser Funktion werden die beiden grundlegenden Funktionen für das Schreiben und Lesen einzelner Zeichen übergeben. Von da an werden diese Funktionen von den restlichen Funktionen aus „stdio.h“ benutzt, z.B. printf() oder scanf(). Wenn man nur Schreiben will, braucht man die Lesefunktion nicht implementieren und auch nicht übergeben.

In der main() Funktion wird fdevopen() aufgerufen. Die Funktion ioinit() initialisiert den UART.

int main(void) {
     unsigned int c;
 
     ioinit();     fdevopen(uart_putchar, uart_getchar ); 
 
     printf("nnMotor testnn");
 
     for(;;) {
         // input processing via rs232
         c = getchar();
         printf("you entered: %c, %un", c, c);
     }
}

Die Funktion ioinit():

void ioinit(void) {
     UCSRB = _BV(TXEN) | _BV(RXEN); /* tx/rx enable */
     UBRR = (F_CPU / (16 * 19200UL)) - 1; /* 19200 Bd */
     /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
#if defined(TWPS0)
     /* has prescaler (mega128 & newer) */
     TWSR = 0;
#endif
     TWBR = (F_CPU / 100000UL - 16) / 2;
}

Hier die Implementierung der beiden Funktionen für Schreiben und Lesen, welche die UART nutzen (getestet auf ATmega8, bei anderen Chips können sich die  Registernamen unterscheiden):

int uart_putchar(char c, FILE *notused ) {
     if (c == 'n')
         uart_putchar('r',notused);
     loop_until_bit_is_set(UCSRA, UDRE); 
     UDR = c;
     return 0;
}
 
int uart_getchar(FILE *notused) {
     unsigned char c;
 
     loop_until_bit_is_set (UCSRA,RXC); // Wait until a char is received
     c = UDR;
     //uart_putchar (c,NULL);
     return c;
}

uart_putchar() und uart_getchar() können beispielsweise auch so geschrieben werden, dass sie nicht den UART nutzen, sondern z.B. auf ein LCD-Display ausgeben. Das „tolle“ an der ganzen Sache ist, dass dann die Standard-C-IO-Funktionen genutzt werden können.

Wenn Zeichen nur fehlerhaft kommen (Pollin-Board): Sicherstellen, dass die Versorgungsspannung hoch genug ist. Wenn ein AVR via 7805 seine Spannung erhält, die Spannung die in den 7805 hineingeht aber zu niedrig ist (also z.B. auch nur 5V), funktioniert der AVR zwar selbst noch ganz gut, die serielle Schnittstelle ist dann aber nicht mehr fehlerfrei.

RS232 mit Interrupts

Peter Fleury hat eine Bibliothek geschrieben, die Interrupts benutzt und damit nebenläufig zur eigentlichen Anwendung genutzt werden kann.

Ich benutze diese Bibliothek eigentlich immer, weil damit ähnlich wie beim log4j von Java Logging-Ausgaben produziert werden können, so dass man über den Programmverlauf informiert ist. Dazu kann man auf einem PC ein Terminalprogramm (z.B. das schreckliche Standardprogramm bei Windows XP, „Hyperterminal“) laufen lassen und die Ausgaben mitverfolgen. Ist kein Terminal angeschlossen, stört dies den Programmablauf nicht. Allerdings wird der Code etwas langsamer laufen.

Im folgenden Codeauszug ist die Nutzung der Bibliothek dargestellt. „interrupt.h“ und signal.h müssen eingebunden werden, da die Bibliothek Interrupts und Signale benutzt.

Die UART_BAUD_RATE gibt die zu nutzende Baudrate an (das Terminalprogramm muss passen eingestellt werden). In zwei Trivialmakros habe ich mir die häufigsten Aufrufe gekapselt: DI() initialisiert die Bibliothek, DO() gibt einen String mittels uart_puts() aus.

#include "../uart/uart.h"
#include <avr/interrupt.h>
#include <avr/signal.h>
#define UART_BAUD_RATE      19200     /* 9600 baud */
 
#define DI() uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,XTAL_CPU) )
#define DO( m ) uart_puts(m)

...

DI();
/*
 * now enable interrupt, since UART library is interrupt controlled
*/
sei();
DO("after init usartnr");
/* initialize display, cursor off */
lcd_init(LCD_DISP_ON);
 
DO("after init lcdnr");

Im Codebeispiel wird die Bibliothek initialisiert mittels DI() und Interrupts freigegeben (sei()). Danach kann man mittels DO() Ausgaben an das Terminal senden. Dies wird im Beispiel vor und nach dem Funktionsaufruf lcd_init() gemacht.

Die Bibliothek muss im Makefile als zusätzliche Bibliothek eingetragen werden. Die Funktionsprototypen stehen alle im Header „uart.h“, den man im eigenen Programm eintragen muss.


Ausgaben des AVR Controllers mittels der UART-Bibliothek auf einem Terminalprogramm  (Fotografie vom Bildschirm, daher die schlechte Qualität)