Microcontroller der Firma AVR beinhalten eine CPU sowie RAM und EEPROM
auf
einem Chip. Es gibt Controller mit unterschiedlichen
Leistungsmerkmalen, allen gemeinsam sind aber Grundgegebenheiten
(Aufbau, Assembler-Dialekt).
Im Umfeld dieser Controller hat
sich
eine starke, weltweite Community gebildet, so dass sowohl
hardwaremässig als auch softwaremässig auf zahlreiche Vorleistungen
zurückgegriffen werden kann.
Im folgenden ist der generelle Umgang mit AVR Microcontrollern beschrieben. Es wurde ausschliesslich Open Source Software benutzt.
Man kann unter Linux und unter Windows Code für den AVR entwickeln. Im folgenden ist das Windows-Umfeld beschrieben. Am Ende gibt es noch Hinweise zur Nutzung mit Linux. Benötigt werden die folgenden Tools:
Der C-Code wird mit einem Text-Editor erzeugt. WinAVR führt das Cross-Compiling für die AVR-Plattform durch und erzeugt alle benötigten Dateien. Der eigentliche Code -als .hex-Datei- wird dann mittels PonyProg über die ISP-Schnittstelle des AVRs in den AVR hineingeladen.
Die
Erstellung der C Source
erfolgt mit einem Editor. Gezeigt ist beispielhaft das Tool
„Programmers's Notepad“.
Mit
diesem Tool kann unter „Tools“ die Compilierung mittels Make direkt
angestoßen werden, Ergebnis ist ein HEX (.hex) File. Vorbedingung ist,
dass man ein funktionierendes Makefile hat.

Das HEX-File kann direkt mittels Pony-Prog in den AVR Controller geladen werden. Pony Prog muss vorher korrekt konfiguriert (wie ist der Controller angebunden, Setup -> Interface Setup) und kalibriert werden (Setup -> Calibration).
Danach kann ein HEX-File in Pony-Prog eingeladen werden(Open Device File) und auf den Controller überspielt werden (Write Device). Es wird immer der komplette Speicher (also z.B. 8 Kbyte beim ATMega8) übertragen, auch wenn das eigentliche Programm nur ein paar Bytes lang ist. Nach dem Schreiben findet noch ein Verify statt. Das Programm startet unmittelbar nach dem Verify.
Das Einladen des kompletten Speichers dauert beim ATMega8 mit 12MHz ca. 40 Sekunden, beim ATMega32 kann es entsprechend länger dauern.

Das Device File stellt Pony Prog in einem Fenster dar. Dieses kann per Menü oder Icon in den Controller hineingeladen werden
Die benötigte Hardware ist ein AVR Controller mit etwas zusätzlicher Beschaltung. Die kann man mutig selbst erstellen oder fertig kaufen. In den Abbildungen dargestellt sind zwei Boards, ein einfaches, bei ebay fertig für ca. 15€ erstanden und ein komplexeres, von www.pollin.de für ebenfalls ca. 15€, zum Selbstaufbauen.
Für die ISP-Programmierung benötigt man ein RS232-Kabel, auf dem 1:1 die Anschlüsse 3,4,5,6,7,8,9 durchgeführt sind.
Anforderungen an den PC, mit dem die AVR-Programme erstellt werden, sind niedrig. Ein 600Mhz-Notebook ist dicke ausreichend. Unabdingbar ist die RS232-Schnittstelle, wenn seriell übertragen werden soll. Da man beim Herumbasteln theoretisch die RS232-Schnittstelle durch Fehlbeschaltung und Unachtsamkeit zerstören kann, sollte man zur Sicherheit nicht seinen besten Rechner für solche Hardware-Aktionen nutzen.
Der AVR Controller kommt mit 100-200 mA Strom aus, so dass ein einfaches Steckernetzteil mit 9V Gleich- oder Wechselstrom ausreicht, denn die Boards besitzen meist eine eigene Gleichrichtung und Stabilisierung. Vorübergehend langt auch eine 9V-Batterie.


Im folgenden ist ein einfaches Programm dargestellt, das eine LED auf dem Board blinken lässt.
Eine Verzögerungsschleife (Funktion delay_ms) sorgt dafür, dass man das An- und Ausgehen der LED auch wahrnehmen kann. Die dort genannten Werte variieren nach der Taktfrequenz des Controllers, sollten aber für 8-12 Mhz tun.
Die LED hängt im Beispiel an Port D, Bit 1. Die ganzen Spezifika des Controllers sind in include-Files avr/*.h abgelegt (Teil von WinAVR). Dort ist auch festgelegt, welches Register bei einem bestimmten Cotroller (z.B. ATMega8) für Port D, Pin 1 angesprochen werden soll. Dies verbirgt sich hinter der Preprozessor-Makro „PD1“. Inhaltlich wird das Bit 1 des Ports D auf Ausgabe geschaltet, und dann in einer while-Schleife abwechselnd auf 0 und auf 1 gesetzt. Zwischen den Wechseln wird jeweils gewartet. Im Ergebnis ist das ein Blinken.
#include
<avr/io.h>
void delay_ms(unsigned
short ms)
/* delay for a minimum of <ms> */
/*
with a 1Mhz clock, the resolution is 1 ms */
{
unsigned short outer1, outer2;
outer1 = 50*12;
while (outer1) {
outer2 = 1000;
while (outer2) {
while ( ms ) ms--;
outer2--;
}
outer1--;
}
}
void main(void)
{
/* INITIALIZE */
/* enable PD1 as output */
DDRD|= _BV(PD1);
/* BLINK, BLINK ... */
while (1) {
/* led on, pin=0 */
PORTD &= ~_BV(PD1);
delay_ms(500);
/* set output to 5V, LED off */
PORTD|= _BV(PD1);
delay_ms(500);
}
}
Wegen der Verbreitung der AVR-Controller gibt es viele C-Bibliotheken, die komplexere Tätigkeiten ausführen können. So gibt es zum Beispiel Bibliotheken für LCD-Ansteuerung, Echtzeituhransteuerung, RS232-Ansteuerung etc., so dass man nicht alles selbst programmieren muss.
Für fast jedes Problem mit dem AVR findet man im Internet schon Informationen oder fertige Lösungen. Ein bischen hiervon wird im folgenden vorgestellt.
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.
Am Beispiel eines LCD Displays vom Typ WD-C2704M-1HNN wird gezeigt, was zu tun ist.
Das Display hat 4 Zeilen zu 27 Zeichen. Auf dem Display sitzen zwei Controller, kompatibel zum HD44780. Einer ist für die ersten zwei Zeilen, der andere für die unteren zwei Zeilen verantwortlich. Beide Controller teilen sich alle I/O-Leitungen und das Display besitzt einen zusätzlichen Eingang, mit dem man zwischen beiden Controllern auswählen kann.
Als Opensource gibt es eine Bibliothek für den AVR-Controller (LCD Library von Peter Fleury), welche LCDs ansteuern kann. Diese Bibliothek 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.
Im folgenden ist die Nutzung der Funktionen dargestellt (Codeauszug):
...
#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 1\n");
/* 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 key\n\r");
/* wait until
push button PD2 (INT0) is pressed */
wait_until_key_pressed();
DO("after key\n\r");
/*
* Test 2: use lcd_command() to turn on cursor
*/
/* turn on cursor */
lcd_command(LCD_CRTLR_1, 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.
Für LCDs mit nur einem Controller ist die Bibliothek natürlich so wie sie ist zu benutzen. Die Bibliothek ist auch konfigurierbar, es gibt z.B. Controller, die mehr als zwei Zeilen beherrschen, dies kann in der Bibliothek eingestellt 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 gleich besprochen.
Im Bild ist der Anschluss des LCDs an das einfache AVR-Board zu erkennen.

Anschluss des Displays über die Datenleitungen (oben) sowie der Kontrastregelung (unten).
Experimentalaufbau - die Display-Schutzfolie wurde auf dem Display belassen, daher die schlechte Ablesbarkeit.
Neben der Datenleitungen (8 Datenbits D0..D7, Controllerauswahl, R/W, Enable sowie GND und +5V), 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 Regelspannung für den Kontrast angeschlossen, ist absolut nichts zu sehen und man kann nicht erkennen, ob das Display überhaupt funktioniert! Die Kontrastspannung wird über einen Regler angeschlossen, so dass man auf unterschiedliche Lichtverhältnisse reagieren kann.
Wie kann man mit einem Taster oder einem Schalter eine zuverlässige Eingabe für den AVR machen? Einfach ein Port abfragen langt nicht, denn der elektrische Vorgang des „Prellens“ eines Schalters produziert kein klares und eindeutiges Ein- oder Aus-Signal. Es werden pro Einschalten oder Ausschalten gewissermaßen mehrere Einsen und Nullen erzeugt, bis der Schalter zur Ruhe kommt. Man hat also die Aufgabe des Entprellens. Dies kann softwaretechnisch wie folgt gelöst werden:
void
wait_until_key_pressed(void)
{
unsigned char temp1, temp2;
unsigned int i;
do {
temp1 =
PINC;
// read input
for(i=0;i<65535;i++)
;
temp2 =
PINC;
// read input
temp1 = (temp1 &
temp2); //
debounce input
} while ( temp1
& _BV(PINC0) );
loop_until_bit_is_set(PINC,PINC0);
/* wait until key is released */
}
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, der das Low Level RS232 Handling (Bitschiebereien) schon erledigt. Man muss dann nur noch komfortabel mit Byte-Werten statt mit Bitverschiebungen operieren. Natürlich gibt es auch hierfür schon Bibliotheken. 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“ oder besser, das Freeware-programm HTERM) 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 usart\n\r");
/* initialize display, cursor off */
lcd_init(LCD_DISP_ON);
DO("after init lcd\n\r");
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)
AD Wandler - tbd
Echtzeituhr - tbd
Linux-Tooling - tbd