www.mikrocontroller.net

Pollin Funk-AVR-Evaluationsboard

Inhaltsverzeichnis

[bearbeiten] Motivation

Auf dieser Seite geht es um Erfahrungen eines Einsteigers mit dem Bausatz Funk-AVR-Evaluationsboard v1.1 von der Firma Pollin und um die C-Programmierung mithilfe des Boards und AVR-GCC/WinAVR.

In den Beispielprogrammen wird nach Möglichkeit ein ATmega8 mit 12 MHz Quarz Taktquelle verwendet. Die Hardware auf dem Board ist sehr überschaubar (ein Taster, zwei LEDs, s. PDF unter Weblinks), so dass die Beispiele vielleicht zum eigenen Experimentieren anregen z.B. mit einem Steckbrettaufbau wie im AVR Tutorial.

Bitte nutzt die Diskussionsseite oder das Forum, wenn beim Lesen Fragen, Anregungen oder Kritik auftauchen,

[bearbeiten] Aufbau des Bausatzes

Praktisch ist bei dem Aufbau des Bausatzes nichts besonderes zu vermelden. Für einen Ungeübten (mich) hat der Aufbau ca. 2 h gedauert. Gegen Ende der Löterei liess meine Konzentration nach und ich musste ein paar unsaubere Lötstellen mit Entlötlitze nachbehandeln. Besser eine Pause machen.

Technisch/elektrisch siehe unter Weblinks der Erfahrungsbericht von Marco Schmoll (www.controller-designs.de).

Nach der genauen Betrachtung mit einer Lupe und keinen Auffälligkeiten wurde eine 9V Gleichspannung an die Klemme J5 angelegt. LED NETZ leuchtet. LED1 und LED2 sind aus. Mein Netzteil kann den Strom anzeigen. Folgende Werte wurden beobachtet:

  • ca. 20 mA - AVR nicht eingesetzt, MAX232 nicht eingesetzt
  • ca. 22 mA - AVR nicht eingesetzt, MAX232 nicht eingesetzt, RESET gedrückt
  • ca. 25 mA - AVR nicht eingesetzt, MAX232 eingesetzt
  • ca. 26 mA - AVR nicht eingesetzt, MAX232 eingesetzt, RESET gedrückt
  • ca. 30 mA - ATmega8 eingesetzt, MAX232 eingesetzt

[bearbeiten] Einstellen der Taktquelle

Die folgenden Kommandozeilen zur Einstellung der Taktquelle in den AVR Fuses beziehen sich auf einen Windows PC und die ISP Programmiersoftware AVRDUDE. Kann nützlich sein, dafür kleine Batchdateien zu schreiben.

Wenn ein Funkmodul installiert ist, kann es zu Problemen beim ISP-Programmieren kommen [1]. Dies kann sich auch beim Ändern der Fuses auswirken. Im schlimmsten Fall wird der AVR unbrauchbar. Vorher kontrollieren!

[bearbeiten] Serieller ISP auf dem Board

ATmega8 Fuses lesen

@echo off
echo Serieller ISP auf dem Board
echo Lese ATmega8 Fuses
d:\winavr\bin\avrdude -v -p atmega8 -c ponyser -P com1

Die Schnittstelle COM1 ist an die verwendete Schnittstelle auf dem PC anzupassen. Wichtig ist, dass kein zusätzlicher Parallelport-ISP angeschlossen ist. Wenn doch, wird der Atmega8 nicht erkannt!

ATmega8 1 MHz interner RC-Oszillator

@echo off
echo Serieller ISP auf dem Board
echo Setze ATmega8 Fuses auf 1 MHz interner RC-Oszillator
d:\winavr\bin\avrdude -v -p atmega8 -c ponyser -P com1 -U lfuse:w:0xC1:m -U hfuse:w:0xD9:m

ATmega8 12 MHz Quarz

Wenn das Board nach Anleitung aufgebaut wurde, d.h. in Q2 der 12,000 MHz Quarz eingesetzt wurde, kann man den ATmega8 auf max. 12 MHz einstellen:

@echo off
echo Serieller ISP auf dem Board
echo Setze ATmega8 Fuses auf 12 MHz Quarz
d:\winavr\bin\avrdude -v -p atmega8 -c ponyser -P com1 -U lfuse:w:0x2F:m -U hfuse:w:0xD9:m

[bearbeiten] Parallelport ISP Typ STK200

ATmega8 Fuses lesen

@echo off
echo Parallelport ISP Typ STK200
echo Lese ATmega8 Fuses
d:\winavr\bin\avrdude -v -p atmega8 -c stk200 -P lpt1
Werkseinstellung Fuses Atmega8 (Anzeige im AVR Fuse Calculator)
Werkseinstellung Fuses Atmega8 (Anzeige im AVR Fuse Calculator)

Die Schnittstelle LPT1 ist an die verwendete Schnittstelle auf dem PC anzupassen. Ebenso der Pfad zu dem Programm avrdude.exe.

Die ausgelesenen Fuses bei einem Fabrikneuen Atmega8 sollten dem Bild rechts entsprechen.

Siehe auch

  • AVR Fuse Calculator von Mark Hämmerling. In den dortigen Default-Einstellungen ist der Watchdog aktiviert. Lässt man das so, funktioniert das Programm Blinky (s.u.) nicht wie erwartet: Nur LED2 zappelt, LED1 ist meist aus, weil vor dem Umschalten von LED1 der Watchdog den Atmega8 resettet, d.h. das Programm von neuem starten lässt.

ATmega8 1 MHz interner RC-Oszillator

@echo off
echo Parallelport ISP Typ STK200
echo Setze ATmega8 Fuses auf 1 MHz interner RC-Oszillator
d:\winavr\bin\avrdude -v -p atmega8 -c stk200 -P lpt1 -U lfuse:w:0xC1:m -U hfuse:w:0xD9:m

ATmega8 12 MHz Quarz

Wenn das Board nach Anleitung aufgebaut wurde, d.h. in Q2 der 12,000 MHz Quarz eingesetzt wurde, kann man den ATmega8 auf max. 12 MHz einstellen:

@echo off
echo Parallelport ISP Typ STK200
echo Setze ATmega8 Fuses auf 12 MHz Quarz
d:\winavr\bin\avrdude -v -p atmega8 -c stk200 -P lpt1 -U lfuse:w:0x2F:m -U hfuse:w:0xD9:m

[bearbeiten] Programm ins Flash-ROM schreiben

Im folgenden wird angenommen, dass die zu programmierende Datei im iHEX-Format unter dem Namen atmega8.hex im aktuellen Verzeichnis befindet. Wenn ein Funkmodul installiert ist, kann es zu Problemen beim Flashen kommen [2].

[bearbeiten] Serieller ISP auf dem Board

@echo off
echo Serieller ISP auf dem Board
echo Programmiere Atmega8 Flash-ROM mit Datei atmega8.hex
d:\winavr\bin\avrdude -p atmega8 -c ponyser -P com1 -e -U flash:w:atmega8.hex

[bearbeiten] Parallelport ISP Typ STK200

@echo off
echo Parallelport ISP Typ STK200
echo Programmiere Atmega8 Flash-ROM mit Datei atmega8.hex
d:\winavr\bin\avrdude -p atmega8 -c stk200 -P lpt1 -e -U flash:w:atmega8.hex

[bearbeiten] Beispielprogramme

[bearbeiten] Blinky

Die beiden LED LED1 und LED2 auf dem Board sollen im 1s Takt wechselweise An und Aus gehen.

Die beiden On-board-LEDs sind active-high geschaltet, d.h. wenn am Pin des AVR ein logische 1 (HIGH Pegel) ausgegeben wird, leuchtet die LED. Wird eine logische 0 (LOW Pegel) ausgegeben, leuchtet die LED nicht. Das ist andersrum als im AVR Tutorial.

Beschaltung des Tasters und der LEDs

/*
    Atmega8
    Pollin Funk-AVR-Evaluationsboard v1.1
 
    Project -> Configuration Options in AVR Studio:
    Frequency:    1000000 bzw. 12000000
    Optimization: -Os
*/
#include <avr/io.h>
#include <util/delay.h>
 
// LEDs sind active-high geschaltet
#define LED_AN(LED)	(PORTD |=  (1<<(LED)))
#define LED_AUS(LED)	(PORTD &= ~(1<<(LED)))
#define LED_TOGGLE(LED)	(PORTD ^=  (1<<(LED)))
#define LED1		PD6
#define LED2		PD5
#define TASTER	        PB1
 
int main(void)
{
  DDRB &= ~(1<<TASTER);          // Port B: Eingang für Taster
  DDRD |= (1<<LED1) | (1<<LED2); // Port D: Ausgang für LED1 und LED2
 
  // Anfangseinstellung
  LED_AN(LED1);
  LED_AUS(LED2);
 
  while(1)
  {
    _delay_ms(1000);  // Wert 1000 erlaubt ab avr-libc 1.6
    LED_TOGGLE(LED1);
    LED_TOGGLE(LED2);
  }
}

[bearbeiten] Tasty

Wenn der On-board-Taster TASTER1 nicht gedrückt ist (Ruhezustand), soll die LED1 leuchten und LED2 soll nicht leuchten. Solange der User den Taster TASTER1 gedrückt hält, soll sich der Zustand der LEDs umkehren.

Der On-board-Taster TASTER1 ist ebenfalls active-high (siehe AVR-GCC-Tutorial) geschaltet, d.h. wenn der Taster geschlossen ist, liegt am Pin PB1 des AVR eine logische 1 (HIGH Pegel) an. Ist der Taster offen, liegt eine eine logische 0 (LOW Pegel) an. Das ist andersrum als im AVR Tutorial.

/*
    Atmega8
    Externer Quarz-Oszillator: 12 MHz
 
    Pollin Funk-AVR-Evaluationsboard v1.1
*/
 
#include <avr/io.h>
#include <util/delay.h>
 
// LEDs sind high-active geschaltet
#define LED_AN(LED)	(PORTD |=  (1<<(LED)))
#define LED_AUS(LED)	(PORTD &= ~(1<<(LED)))
#define LED_TOGGLE(LED)	(PORTD ^=  (1<<(LED)))
#define LED1		PD6
#define LED2		PD5
 
// TASTER ist high-active geschaltet 
#define TASTER	PB1
#define TASTER_GEDRUECKT()	(PINB & (1<<TASTER))
 
int main(void)
{
  DDRB &= ~(1<<TASTER);	         // Port B: Eingang für Taster
  DDRD |= (1<<LED1) | (1<<LED2); // Port D: Ausgang für LED1 und LED2
 
  while(1)
  {
    if (!TASTER_GEDRUECKT())
    {
      // Taster ist nicht (!) gedrückt
      LED_AN(LED1);
      LED_AUS(LED2);
    }
    else
    {
      // Taster ist gedrückt
      LED_AUS(LED1);
      LED_AN(LED2);
    }
  }
}

[bearbeiten] 2-Bit Zähler

Jeder Tastendruck auf TASTER1 soll eine Variable um Eins hochzählen. Der Inhalt der unteren beiden Bits der Zählvariable soll mit den beiden LEDs angezeigt werden.

Das Hochzählen darf nur erfolgen, wenn ein Wechsel von Offen nach Geschlossen stattfindet. Das Programm muss also berücksichtigen, ob ein Wechsel von "Taster offen" zu "Taster geschlossen" stattfindet und ob der Taster in einer Position gehalten wird.

Mit diesem Beispiel kann man grob feststellen, ob der TASTER1 auf dem Board zum Prellen neigt, d.h. wenn sich der Zählerstand nicht wie gewollt pro Tastendruck um Eins erhöht und ob deshalb eine spezielle Routine zur Entprellung erforderlich ist.

Mein Board zeigt bei diesem Programm keine Neigung zum Prellen. Die auf dem Board vorhandene Hardwareentprellung über einen Tiefpassfilter mit C17 330 nF zwischen Taster und GND erfüllt hier ihren Zweck.

/*
    Atmega8
    Externer Quarz-Oszillator: 12 MHz
 
    Pollin Funk-AVR-Evaluationsboard v1.1
*/
 
#include <avr/io.h>
#include <util/delay.h>
#include <inttypes.h>
 
// LEDs sind high-active geschaltet
#define LED_AN(LED)	(PORTD |=  (1<<(LED)))
#define LED_AUS(LED)	(PORTD &= ~(1<<(LED)))
#define LED_TOGGLE(LED)	(PORTD ^=  (1<<(LED)))
#define LED1		PD6
#define LED2		PD5
 
// TASTER ist high-active geschaltet 
#define TASTER	PB1
#define TASTER_GEDRUECKT()	(PINB & (1<<TASTER))
#define TASTE_AUF 0
#define TASTE_ZU  1
 
void ausgabe(uint8_t wert)
{
  if (wert & (1<<0)) // Bit 0
    LED_AN(LED1);
  else
    LED_AUS(LED1);
 
  if (wert & (1<<1)) // Bit 1
    LED_AN(LED2);
  else
    LED_AUS(LED2);
}
 
int main(void)
{
  uint8_t alter_tastenzustand = TASTE_AUF;
  uint8_t zaehler = 0;
 
  DDRB &= ~(1<<TASTER);			// Port B: Eingang für Taster
  DDRD |= (1<<LED1) | (1<<LED2);	// Port D: Ausgang für LED1 und LED2
 
  while(1)
  {
    ausgabe(zaehler);
 
    if (TASTER_GEDRUECKT() && (alter_tastenzustand == TASTE_AUF))
    {
      // Wechsel von OFFEN nach GESCHLOSSEN
      zaehler++;
      alter_tastenzustand = TASTE_ZU;
    }
 
    if (!TASTER_GEDRUECKT())
      alter_tastenzustand = TASTE_AUF;
  }
}

[bearbeiten] 8-Bit Zähler mit RS232-Anschluss

Das Beispiel 2-Bit Zähler soll jetzt ausgebaut werden. Im Detail soll der 8-Bit Zählerstand über RS232 auf einen PC ausgegeben werden. Ausserdem soll "ferngesteuert" eine Veränderung des Zählerstands vom PC aus möglich sein.

Auf der PC-Seite wird die Kommunikation mit einem Terminalprogramm z.B. HyperTerm gemacht, so dass hier keine Programmierung notwendig ist. Lediglich die Einstellung der RS232-Schnittstelle (z.B. COM1, 9600/8/N/1) muss passen. Auf der Atmega8-Seite sind zunächst wenige Grundfunktionen für die RS232-Kommunikation zu schreiben.

Die AVR-Grundfunktionen für die RS232-Kommunikation sind eine Funktion für die Initialisierung der µC-eigenen UART-Schnittstelle (Bsp.: UART_init) und eine Funktion für das Senden eines Zeichens (Bsp.: UART_putchar) sowie je eine Funktion für das Warten auf ein Zeichens (Bsp.: UART_getchar) bzw. eine nicht-wartende Funktion um festzustellen, ob ein Zeichen des PCs an der UART-Schnittstelle des µC anliegt (Bsp. UART_peekchar).

Als Programmablauf wurde die UART-Kommunikation mit der technisch relativ einfachen Methode Polling eingerichtet, d.h. das Programm fragt selbst möglichst regelmässig und oft genug für einen sinnvolle Kommunikation die UART Schnittstelle ab, ob Zeichen empfangen wurden oder gesendet werden können. Hier erklärt sich auch der Zweck für die nicht-wartende Funktion UART_peekchar() - wenn kein Zeichen vom PC über RS232 anliegt, soll der µC mit dem bekannten Programmablauf weitermachen, damit die Taster-Eingaben ausgewertet werden. Die technisch meistens vorteilhaftere Alternative wäre die Interruptmethode, d.h. das Hauptprogramm wird automatisch genau dann unterbrochen, wenn ein Zeichen empfangen wurde oder gesendet werden kann und der µC verzweigt in spezielle Grundfunktionen, die sog. Interrupthandler, die sich um die Kommunikation kümmern. Nach dem Abarbeiten der Unterbrechung geht es dann im eigentlichen Hauptprogramm weiter. Ein UART_peekchar-Trick ist bei dieser Programmierweise nicht nötig. Die Programmierweise mit UART-Interrupts könnte Teil eines künftigen Beispiels sein.

Die Grundfunktionen werden von einer allgemeinen Funktion (Bsp.: rs232) verwendet, um den Zählerstand an den PC auszugeben bzw. um Eingaben auf dem PC in einen neuen Zählerstand umzusetzen. Da die Funktion rs232 den Zählerstand ändern soll, wird ihr die Adresse der Zählervariable (&zaehler) übergeben, d.h. es wird hier mit Zeigern (Pointern) gearbeitet.

Per RS232 können komfortabel mehr Informationen ausgegeben werden als über die beiden LEDs. Im Beispiel wird der Zählerstand in drei verschiedenen Zahlensystemen ausgegeben und es wird der Zustand der beiden LEDs übermittelt. Der Clou hinsichtlich Bedienung des Zählers ist, dass per RS232 auch komfortabel mehr Bedienmöglichkeiten eingerichtet werden können als mit dem einfachen Taster: Zusätzlich zum Hochzählen (Plus-Taste) kann z.B. runtergezählt werden (Minus-Taste) oder es kann direkt eine bis zu dreistellige Zahlenfolge eingegeben werden, die zum Setzen des Zählerstands verwendet wird.

Bei der Initialisierung der UART wurden die Einstellungen 9600 Baud, 8 Datenbits, Keine Parity (No Parity) und 1 Stopbit gewählt. 8/N/1 ist ein gängiger Wert für RS232. Bei der Auswahl der Baudrate muss darauf geachtet werden, dass mit der gegebenen Taktrate auf dem Board (hier 12 MHz) nicht jede denkbare RS232-Baudrate gleich gut geeignet ist. Das Register zur Einstellung der Baudrate im µC kann nur mit ganzen Zahlen gefüllt werden, die dann benutzt werden, um durch Teilen die Taktrate der UART aus dem Takt der CPU abzuleiten. Bei einer CPU Taktrate von 12 MHz ergeben folgende Soll-Baudraten (in Baud) die angegebenen Einstellungen des Baudratenregisters (UBRR) und die Ist-Baudraten sowie die Abweichungen zwischen Soll- und Ist-Baudrate in Prozent: Die Formeln zur Berechnung stehen im Atmega8 Datenblatt im Kapitel UART.


Soll-Baudrate UBRR
(bei U2X=0)
Ist-Baudrate
(bei U2X=0)
Abweichung [%]
(bei U2X=0)
UBRR
(bei U2X=1)
Ist-Baudrate
(bei U2X=1)
Abweichung [%]
(bei U2X=1)
300 2499 300 0,0 - - -
1200 624 1200 0,0 1249 1200 0,0
2400 311 2403 0,1 624 2400 0,0
4800 155 4807 0,1 311 4807 0,1
9600 77 9615 0,2 155 9615 0,2
14,4K 51 14423 0,2 103 14423 0,2
19,2K 38 19230 0,2 77 19230 0,2
28,8K 25 28846 0,2 51 28846 0,2
38,4K 18 37473 2,8 38 38461 0,2
57,6K 12 57692 0,2 25 57692 0,2
76,8K 8 83333 8,5 16 78947 2,8
115,2K 5 125000 8,5 12 115384 0,2
230,4K 2 250000 8,5 5 250000 8,5
250K 2 250000 0,0 5 250000 0,0


Bestimmte Baudraten können ohne Abweichung oder mit tolerierbar kleiner Abweichung (max. 0,2%) eingestellt werden, während andere Baudraten immer zu grosse Abweichungen für eine fehlerfreie Kommunikation ergeben. Man kann obige Tabelle (von denen es ähnliche im Datenblatt für andere Taktraten gibt) verwenden oder man kann die UBRR Einstellung im Programmcode berechnen lassen (s. Programmcode unten). Im AVR-GCC-Tutorial ist zusätzlich ganz komfortabel eine Berechnung der Abweichung und eine Warnung bei zu grossen Werten angegeben, so dass man seine Wunschbaudrate in Richtung geringe Abweichung optimieren kann. Im folgenden Code wurde die gängige Baudrate 9600 Baud eingestellt.

Eine letzte grössere Änderung gegenüber dem 2-Bit-Zähler fällt an der Stelle der Taster-Eingabe auf: Jetzt mit der genaueren RS232-Ausgabe wurden Prelleffekte als unregelmässige Sprünge im Zählerstand beobachtet. Deshalb wurde Programmcode für eine einfache Entprellung nach dem Warteschleifenverfahren hinzugefügt. Wenn das Makro ENTPRELLUNG mit 0 definiert ist, entfällt die Entprellung, d.h. das Programm reagiert wie beim 2-Bit Zähler auf den Taster. Um die Entprellung zu aktivieren, wird das Makro ENTPRELLUNG z.B. mit 10, 25 oder 30 definiert und neu kompiliert und geflasht. Die Zahl im Makro gibt eine Wartezeit an, nach der der Zustand des Tasters nochmal geprüft wird. Hier kann man etwas experimentieren, was ein geeigneter Wert wäre.

/*
    Pollin Funk-AVR-Evaluationsboard v1.1
    Atmega8
 
    Externer Quarz-Oszillator: 12 MHz
    Optimierung: -Os
 
    RS232-Einstellungen:
    9600 Baud, 8 Bits, No Parity, 1 Stopbit
*/
 
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
 
// LEDs sind high-active geschaltet
#define LED_AN(LED)     (PORTD |=  (1<<(LED)))
#define LED_AUS(LED)    (PORTD &= ~(1<<(LED)))
#define LED_TOGGLE(LED) (PORTD ^=  (1<<(LED)))
#define LED1            PD6
#define LED2            PD5
 
// TASTER ist high-active geschaltet
#define TASTER             PB1
#define TASTER_GEDRUECKT() (PINB & (1<<TASTER))
#define TASTE_AUF          0
#define TASTE_ZU           1
/* in Millisekunden */
#define ENTPRELLUNG        10
 
void UART_init(void)
{
    // Baudrate setzen
    // 9600 Baud bei F_CPU 12 MHz
    // (Baudratenfehler = +0,2%)
    UBRRH = (uint8_t) ((F_CPU / (16 * 9600L) - 1) >> 8);
    UBRRL = (uint8_t)  (F_CPU / (16 * 9600L) - 1);
 
    //      Empfangen,   Senden erlauben
    UCSRB = (1<<RXEN) | (1<<TXEN);
 
    // Frame Format:              8 Bits,                  No Parity,          1 Stopbit
    UCSRC = (1<<URSEL) | ((1<<UCSZ1) | (1<<UCSZ0)) | ((0<<UPM1) | (0<<UPM0)) | (0<<USBS);
}
 
// Auf Zeichen im UART Eingang prüfen
uint8_t UART_peekchar(void)
{
    // sofort zurückkehren und Zustand melden
    return UCSRA & (1<<RXC);
}
 
// Auf Zeichen im UART Eingang warten
uint8_t UART_getchar(void)
{
    // Warte bis UART Eingang voll
    while ( !(UCSRA & (1<<RXC)) )
        ;
    return UDR;
}
 
// Zeichen in UART Ausgang geben
void UART_putchar(char z)
{
    // Warte bis UART Ausgang frei
    while ( !(UCSRA & (1<<UDRE)) )
        ;
    UDR = z;
}
 
// Zeichenkette (String) in UART Ausgang geben
void UART_puts(char *s)
{
    while ( *s )
    {
        UART_putchar(*s);
        s++;
    }
}
 
#define EINGABE_START 	0
#define EINGABE_ABBRUCH EINGABE_START
#define EINGABE_MAX 		3
#define FORMFEED '\014'
 
 
void rs232(uint8_t *wert)
{
    static int16_t alter_wert = -1;
    uint8_t anzahl_ziffern;
    uint8_t zeichen;
    char puffer[23];
 
    // Eingabe über RS232
 
    // Ohne Warten prüfen, ob Zeichen an UART vorhanden
    if ( UART_peekchar() )
    {
        // Zeichen ist da.
 
        // Aktuellen Wert ausgeben
 
        // Neue Seite im Terminal anfordern.
        // Bewirkt ein Löschen des Bildschirms
 
        UART_putchar(FORMFEED);
 
        // Einleitender Text, was im folgenden ausgegeben wird
        UART_puts("Alter Z\204hler = "); // \204 ist ä in Oktalschreibweise
 
        // Zahl in Ziffernfolge umwandeln. Hier zuerst in Dezimalziffern
        itoa((int) *wert, puffer, 10);
        UART_puts(puffer);
 
        // Dann die gleiche Zahl in Hexadezimalziffern
        UART_puts(" 0x");
        itoa((int) *wert, puffer, 16);
        UART_puts(puffer);
 
        // Dann die gleiche Zahl in Binärziffern
        UART_puts(" 0b");
        itoa((int) *wert, puffer, 2);
        UART_puts(puffer);
 
        // Schliesslich noch der Zustand der LEDs auf dem Board senden
        UART_puts( (*wert & 2) ? " LED2-An " : " LED2-Aus");
        UART_puts( (*wert & 1) ? " LED1-An" : " LED1-Aus");
 
        // Zeilenabschluss zur Formatierung der Anzeige
        UART_puts("\r\n");
 
        // Neuen Wert eingeben
 
        // Initialisierung
        UART_puts("Eingabe> ");
        anzahl_ziffern = EINGABE_START;
 
        // Eingabeschleife
        do
        {
            // 1. bereits Zeichen am UART Eingang abholen
            // in weiteren Schleifen auf neue Zeichen warten
            zeichen = UART_getchar();
 
            // Zeichen auswerten
            if ( (zeichen == '-') && (anzahl_ziffern == EINGABE_START) )
            {
                // MINUS
                UART_puts(" -1");
                UART_puts("\r\n");
                *wert -= 1;
                break;
            }
            else if ( (zeichen == '+') && (anzahl_ziffern == EINGABE_START) )
            {
                // PLUS
                UART_puts(" +1");
                UART_puts("\r\n");
                *wert += 1;
                break;
            }
            else if ( (zeichen == '\n') || (zeichen == '\r') )
            {
                // ENTER oder RETURN: Abschluss der Eingabe
                break;
            }
            else if ( (zeichen >= '0') && (zeichen <= '9') )
            {
                // Eingabe einer Ziffer
                UART_putchar(zeichen); // Echo
                puffer[anzahl_ziffern++] = zeichen;
                puffer[anzahl_ziffern] = 0;
            }
            else
            {
                // Sonstiges Zeichen ist für uns illegal
                anzahl_ziffern = EINGABE_ABBRUCH;
                UART_puts(" Abbruch.");
                UART_puts("\r\n");
                break;
            }
        }
        while ( anzahl_ziffern < EINGABE_MAX );
 
        // Eingabe prüfen
        if ( anzahl_ziffern != EINGABE_ABBRUCH )
        {
            // Ziffern in Zahl umwandeln
            int i = atoi(puffer);
 
            // z.B. erlaubter Wertebereich sei 0 bis 255
            if ( (i >= 0) && (i < 256) )
            {
                UART_puts(" Ok.");
                UART_puts("\r\n");
                *wert = (uint8_t) i;
            }
            else
                UART_puts(" Ung\201ltig.\r\n"); // \201 ist ü in Oktalschreibweise
        }
    }
 
    // Ausgabe auf RS232 (s.oben)
    // bei nur Änderung des Wertes gegenüber der letzten Ausgabe
    //
    // alter_wert wurde mit -1 initialisiert. Das ist ausserhalb
    // des Wertebereichs von *wert, D.h. spätere Abfrage, ob
    // (alter_wert != *wert) ist, ist beim ersten Durchlauf wie
    // gewünscht wahr,
    //
    if (alter_wert != *wert)
    {
        alter_wert = *wert;
        UART_puts("Z\204hler = ");
        itoa((int) *wert, puffer, 10);
        UART_puts(puffer);
        UART_puts(" 0x");
        itoa((int) *wert, puffer, 16);
        UART_puts(puffer);
        UART_puts(" 0b");
        itoa((int) *wert, puffer, 2);
        UART_puts(puffer);
        UART_puts( (*wert & 2) ? " LED2-An " : " LED2-Aus");
        UART_puts( (*wert & 1) ? " LED1-An" : " LED1-Aus");
        UART_puts("\r\n");
    }
}
 
void ausgabe_led(uint8_t wert)
{
    if (wert & (1<<0)) // Bit 0
        LED_AN(LED1);
    else
        LED_AUS(LED1);
 
    if (wert & (1<<1)) // Bit 1
        LED_AN(LED2);
    else
        LED_AUS(LED2);
}
 
int main(void)
{
    uint8_t alter_tastenzustand = TASTE_AUF;
    uint8_t zaehler = 0;
 
    DDRB &= ~(1<<TASTER);           // Port B: Eingang für Taster
    DDRD |= (1<<LED1) | (1<<LED2);  // Port D: Ausgang für LED1 und LED2
 
    UART_init();
    UART_putchar(FORMFEED);
 
    while (1)
    {
        rs232(&zaehler);
 
        ausgabe_led(zaehler);
 
        if ( TASTER_GEDRUECKT() )
        {
#if ENTPRELLUNG
            _delay_ms(ENTPRELLUNG);
            if ( TASTER_GEDRUECKT() )
#endif /* ENTPRELLUNG */
            {
                // Wechsel von OFFEN nach GESCHLOSSEN?
                if ( alter_tastenzustand == TASTE_AUF )
                {
                    zaehler++;
                    alter_tastenzustand = TASTE_ZU;
                }
            }
        }
 
        if ( !TASTER_GEDRUECKT() )
        {
#if ENTPRELLUNG
            _delay_ms(ENTPRELLUNG);
            if ( !TASTER_GEDRUECKT() )
#endif /* ENTPRELLUNG */
                alter_tastenzustand = TASTE_AUF;
        }
    }
}

[bearbeiten] Zähler mit LCD-Anzeige

Als nächstes soll noch eine LCD-Anzeige installiert werden.

In meiner Bastelkiste lag noch ein Text-LCD mit dem LCD-Controller HD44780. Erfreulicherweise gibt es für diesen LCD-Controller jede Menge fertigen Beispielcode zur Ansteuerung im Internet u.a. im AVR-GCC-Tutorial.

Um den Beispielcode in der Form einzubinden, wie er im Tutorial zu finden ist und um das mittlerweise angewachsene Projekt übersichtlicher zu machen, werden zusammenhängende Codeteile in Unterdateien ausgelagert. Im folgenden Bild ist zu sehen, wie die einzelnen Dateien heissen und wie sie im AVR Studio eingebunden werden.


Bild:PFA_Projekt_ZLCD.png


Die Datei zaehler_lcd.c enthält die main-Funktion und die neue Ausgabefunktion ausgabe_lcd und die bereits bekannte Funktion ausgabe_led:

/*
    Pollin Funk-AVR-Evaluationsboard v1.1
    Atmega8
 
    Externer Quarz-Oszillator: 12 MHz
    Optimierung: -Os
 
    RS232-Einstellungen:
    9600 Baud, 8 Bits, No Parity, 1 Stopbit
*/
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
 
#include "funkhw.h"
#include "rs232.h"
#include "lcd-routines.h"
 
/* in Millisekunden */
#define ENTPRELLUNG        10
 
void ausgabe_lcd(uint8_t wert)
{
    static int16_t alter_wert = -1;
    char puffer[23];
 
    if (wert != alter_wert)
    {
        // Ausgabe nur bei Änderungen
        alter_wert = wert;
        itoa((int) wert, puffer, 10);
 
        lcd_clear();
        lcd_string("Zaehler = ");
        lcd_string(puffer);
    }
}
 
void ausgabe_led(uint8_t wert)
{
    if (wert & (1<<0)) // Bit 0
        LED_AN(LED1);
    else
        LED_AUS(LED1);
 
    if (wert & (1<<1)) // Bit 1
        LED_AN(LED2);
    else
        LED_AUS(LED2);
}
 
int main(void)
{
    uint8_t alter_tastenzustand = TASTE_AUF;
    uint8_t zaehler = 0;
 
    DDRB &= ~(1<<TASTER);           // Port B: Eingang für Taster
    DDRD |= (1<<LED1) | (1<<LED2);  // Port D: Ausgang für LED1 und LED2
 
    UART_init();
    UART_putchar(FORMFEED);
 
    lcd_init();
 
    while (1)
    {
        rs232(&zaehler);			// RS232 Eingabe und Ausgabe
 
        ausgabe_lcd(zaehler); // LCD Ausgabe
 
        ausgabe_led(zaehler); // LED Ausgabe
 
        if ( TASTER_GEDRUECKT() )
        {
#if ENTPRELLUNG
            _delay_ms(ENTPRELLUNG);
            if ( TASTER_GEDRUECKT() )
#endif /* ENTPRELLUNG */
            {
                // Wechsel von OFFEN nach GESCHLOSSEN?
                if ( alter_tastenzustand == TASTE_AUF )
                {
                    zaehler++;
                    alter_tastenzustand = TASTE_ZU;
                }
            }
        }
 
        if ( !TASTER_GEDRUECKT() )
        {
#if ENTPRELLUNG
            _delay_ms(ENTPRELLUNG);
            if ( !TASTER_GEDRUECKT() )
#endif /* ENTPRELLUNG */
                alter_tastenzustand = TASTE_AUF;
        }
    }
}

Die Datei lcd_routines.c enthält die Grundfunktionen für die LCD-Ansteuerung aus dem AVR-GCC-Tutorial.

In der Funktion lcd_enable musste das Timing leicht angepasst werden, weil bei meinem Display mit der 1 µs Pause Störungen aufgetreten waren.

// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
//
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar
 
#include <avr/io.h>
#include "lcd-routines.h"
#include <util/delay.h>
 
// sendet ein Datenbyte an das LCD
 
void lcd_data(unsigned char temp1)
{
    unsigned char temp2 = temp1;
 
    LCD_PORT |= (1<<LCD_RS);        // RS auf 1 setzen
 
    temp1 = temp1 >> 4;
    temp1 = temp1 & 0x0F;
    LCD_PORT &= 0xF0;
    LCD_PORT |= temp1;               // setzen
    lcd_enable();
 
    temp2 = temp2 & 0x0F;
    LCD_PORT &= 0xF0;
    LCD_PORT |= temp2;               // setzen
    lcd_enable();
 
    _delay_us(42);
}
 
// sendet einen Befehl an das LCD
 
void lcd_command(unsigned char temp1)
{
    unsigned char temp2 = temp1;
 
    LCD_PORT &= ~(1<<LCD_RS);        // RS auf 0 setzen
 
    temp1 = temp1 >> 4;              // oberes Nibble holen
    temp1 = temp1 & 0x0F;            // maskieren
    LCD_PORT &= 0xF0;
    LCD_PORT |= temp1;               // setzen
    lcd_enable();
 
    temp2 = temp2 & 0x0F;            // unteres Nibble holen und maskieren
    LCD_PORT &= 0xF0;
    LCD_PORT |= temp2;               // setzen
    lcd_enable();
 
    _delay_us(42);
}
 
// erzeugt den Enable-Puls
void lcd_enable(void)
{
    // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen
    // http://www.mikrocontroller.net/topic/81974#685882
    LCD_PORT |= (1<<LCD_EN);
#if 1
    _delay_us(4); // kurze Pause, Original aus dem Tutorial ist bei meinem Display zu kurz!
#else
    _delay_us(1); // kurze Pause
#endif
    // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern
    // http://www.mikrocontroller.net/topic/80900
    LCD_PORT &= ~(1<<LCD_EN);
}
 
// Initialisierung:
// Muss ganz am Anfang des Programms aufgerufen werden.
 
void lcd_init(void)
{
    LCD_DDR = LCD_DDR | 0x0F | (1<<LCD_RS) | (1<<LCD_EN);   // Port auf Ausgang schalten
 
    // muss 3mal hintereinander gesendet werden zur Initialisierung
 
    _delay_ms(15);
    LCD_PORT &= 0xF0;
    LCD_PORT |= 0x03;
    LCD_PORT &= ~(1<<LCD_RS);      // RS auf 0
    lcd_enable();
 
    _delay_ms(5);
    lcd_enable();
 
    _delay_ms(1);
    lcd_enable();
    _delay_ms(1);
 
    // 4 Bit Modus aktivieren
    LCD_PORT &= 0xF0;
    LCD_PORT |= 0x02;
    lcd_enable();
    _delay_ms(1);
 
    // 4Bit / 2 Zeilen / 5x7
    lcd_command(0x28);
 
    // Display ein / Cursor aus / kein Blinken
    lcd_command(0x0C);
 
    // inkrement / kein Scrollen
    lcd_command(0x06);
 
    lcd_clear();
}
 
// Sendet den Befehl zur Löschung des Displays
 
void lcd_clear(void)
{
    lcd_command(CLEAR_DISPLAY);
    _delay_ms(5);
}
 
// Sendet den Befehl: Cursor Home
 
void lcd_home(void)
{
    lcd_command(CURSOR_HOME);
    _delay_ms(5);
}
 
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)
 
void set_cursor(uint8_t x, uint8_t y)
{
    uint8_t tmp;
 
    switch (y)
    {
    case 1:
        tmp=0x80+0x00+x;
        break;    // 1. Zeile
    case 2:
        tmp=0x80+0x40+x;
        break;    // 2. Zeile
    case 3:
        tmp=0x80+0x10+x;
        break;    // 3. Zeile
    case 4:
        tmp=0x80+0x50+x;
        break;    // 4. Zeile
    }
    lcd_command(tmp);
}
 
// Schreibt einen String auf das LCD
 
void lcd_string(char *data)
{
    while (*data)
    {
        lcd_data(*data);
        data++;
    }
}

Die Includedatei lcd_routines.h enthält die Leitungszuordnung für die LCD-Ansteuerung und die Prototypen für die LCD Funktionen.

Hier wurden die Signalleitungen von PORTD auf PORTC geändert. Und die Definition von F_CPU wurde mit #if/#endif auskommentiert, weil dieser Wert bereits mit den Projekt Configuration Options im AVR Studio auf 12 MHz gesetzt ist.

// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
//
void lcd_data(unsigned char temp1);
void lcd_string(char *data);
void lcd_command(unsigned char temp1);
void lcd_enable(void);
void lcd_init(void);
void lcd_home(void);
void lcd_clear(void);
void set_cursor(uint8_t x, uint8_t y);
 
#if 0
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!
#define F_CPU 8000000
#endif
 
// LCD Befehle
 
#define CLEAR_DISPLAY 0x01
#define CURSOR_HOME   0x02
 
// Pinbelegung für das LCD, an verwendete Pins anpassen
 
#if 1
// Änderung des LCD Anschlusses
// Pollin Funk AVR Evaluationsboard v1.1
// DB4 bis DB7 des LCD sind mit PC0 bis PC3 des AVR verbunden
#define LCD_PORT      PORTC
#define LCD_DDR       DDRC
#define LCD_EN        PC5
#define LCD_RS        PC4
#define LCD_DB7       PC3
#define LCD_DB6       PC2
#define LCD_DB5       PC1
#define LCD_DB4       PC0
#else
#define LCD_PORT      PORTD
#define LCD_DDR       DDRD
#define LCD_RS        PD4
#define LCD_EN        PD5
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden
#endif 

Die Includedatei funkhw.h enthält die gemeinsamen, boardspezifischen Port-Definitionen, die in mehreren Unterdateien benötigt werden:

/*
    Pollin Funk-AVR-Evaluationsboard v1.1
    Atmega8
*/
 
// LEDs sind high-active geschaltet
#define LED_AN(LED)     (PORTD |=  (1<<(LED)))
#define LED_AUS(LED)    (PORTD &= ~(1<<(LED)))
#define LED_TOGGLE(LED) (PORTD ^=  (1<<(LED)))
#define LED1            PD6
#define LED2            PD5
 
// TASTER ist high-active geschaltet
#define TASTER             PB1
#define TASTER_GEDRUECKT() (PINB & (1<<TASTER))
#define TASTE_AUF          0
#define TASTE_ZU           1 

Die Includedatei rs232.h enthält die Prototypen für die Funktionen in rs232.c:

#ifndef FORMFEED
#define FORMFEED '\014'
#endif
 
void UART_init(void);
 
// Auf Zeichen im UART Eingang prüfen
uint8_t UART_peekchar(void);
 
// Auf Zeichen im UART Eingang warten
uint8_t UART_getchar(void);
 
// Zeichen in UART Ausgang geben
void UART_putchar(char z);
 
// Zeichenkette (String) in UART Ausgang geben
void UART_puts(char *s);
 
void rs232(uint8_t *wert);

Und zum Schluss noch die Datei rs232.c mit den ungeänderten UART-Grundfunktionen und der rs232-Funktion.

/*
    Pollin Funk-AVR-Evaluationsboard v1.1
    Atmega8
 
    Externer Quarz-Oszillator: 12 MHz
    Optimierung: -Os
 
    RS232-Einstellungen:
    9600 Baud, 8 Bits, No Parity, 1 Stopbit
*/
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
 
#include "funkhw.h"
#include "rs232.h"
 
#define EINGABE_START 	0
#define EINGABE_ABBRUCH EINGABE_START
#define EINGABE_MAX 		3
 
void UART_init(void)
{
    // Bausrate setzen
    // 9600 Baud bei F_CPU 12 MHz
    // (Baudratenfehler = +0,2%)
    UBRRH = (uint8_t) ((F_CPU / (16 * 9600L) - 1) >> 8);
    UBRRL = (uint8_t)  (F_CPU / (16 * 9600L) - 1);
 
    //      Empfangen,   Senden erlauben
    UCSRB = (1<<RXEN) | (1<<TXEN);
 
    // Frame Format:              8 Bits,                  No Parity,          1 Stopbit
    UCSRC = (1<<URSEL) | ((1<<UCSZ1) | (1<<UCSZ0)) | ((0<<UPM1) | (0<<UPM0)) | (0<<USBS);
}
 
// Auf Zeichen im UART Eingang prüfen
uint8_t UART_peekchar(void)
{
    // sofort zurückkehren und Zustand melden
    return UCSRA & (1<<RXC);
}
 
// Auf Zeichen im UART Eingang warten
uint8_t UART_getchar(void)
{
    // Warte bis UART Eingang voll
    while ( !(UCSRA & (1<<RXC)) )
        ;
    return UDR;
}
 
// Zeichen in UART Ausgang geben
void UART_putchar(char z)
{
    // Warte bis UART Ausgang frei
    while ( !(UCSRA & (1<<UDRE)) )
        ;
    UDR = z;
}
 
// Zeichenkette (String) in UART Ausgang geben
void UART_puts(char *s)
{
    while ( *s )
    {
        UART_putchar(*s);
        s++;
    }
}
 
void rs232(uint8_t *wert)
{
    static int16_t alter_wert = -1;
    uint8_t anzahl_ziffern;
    uint8_t zeichen;
    char puffer[23];
 
    // Eingabe über RS232
 
    // Ohne Warten prüfen, ob Zeichen an UART vorhanden
    if ( UART_peekchar() )
    {
        // Zeichen ist da.
 
        // Aktuellen Wert ausgeben
 
        // Neue Seite im Terminal anfordern.
        // Bewirkt ein Löschen des Bildschirms
 
        UART_putchar(FORMFEED);
 
        // Einleitender Text, was im folgenden ausgegeben wird
        UART_puts("Alter Z\204hler = "); // \204 ist ä in Oktalschreibweise
 
        // Zahl in Ziffernfolge umwandeln. Hier zuerst in Dezimalziffern
        itoa((int) *wert, puffer, 10);
        UART_puts(puffer);
 
        // Dann die gleiche Zahl in Hexadezimalziffern
        UART_puts(" 0x");
        itoa((int) *wert, puffer, 16);
        UART_puts(puffer);
 
        // Dann die gleiche Zahl in Binärziffern
        UART_puts(" 0b");
        itoa((int) *wert, puffer, 2);
        UART_puts(puffer);
 
        // Schliesslich noch der Zustand der LEDs auf dem Board senden
        UART_puts( (*wert & 2) ? " LED2-An " : " LED2-Aus");
        UART_puts( (*wert & 1) ? " LED1-An" : " LED1-Aus");
 
        // Zeilenabschluss zur Formatierung der Anzeige
        UART_puts("\r\n");
 
        // Neuen Wert eingeben
 
        // Initialisierung
        UART_puts("Eingabe> ");
        anzahl_ziffern = EINGABE_START;
 
        // Eingabeschleife
        do
        {
            // 1. bereits Zeichen am UART Eingang abholen
            // in weiteren Schleifen auf neue Zeichen warten
            zeichen = UART_getchar();
 
            // Zeichen auswerten
            if ( (zeichen == '-') && (anzahl_ziffern == EINGABE_START) )
            {
                // MINUS
                UART_puts(" -1");
                UART_puts("\r\n");
                *wert -= 1;
                break;
            }
            else if ( (zeichen == '+') && (anzahl_ziffern == EINGABE_START) )
            {
                // PLUS
                UART_puts(" +1");
                UART_puts("\r\n");
                *wert += 1;
                break;
            }
            else if ( (zeichen == '\n') || (zeichen == '\r') )
            {
                // ENTER oder RETURN: Abschluss der Eingabe
                break;
            }
            else if ( (zeichen >= '0') && (zeichen <= '9') )
            {
                // Eingabe einer Ziffer
                UART_putchar(zeichen); // Echo
                puffer[anzahl_ziffern++] = zeichen;
                puffer[anzahl_ziffern] = 0;
            }
            else
            {
                // Sonstiges Zeichen ist für uns illegal
                anzahl_ziffern = EINGABE_ABBRUCH;
                UART_puts(" Abbruch.");
                UART_puts("\r\n");
                break;
            }
        }
        while ( anzahl_ziffern < EINGABE_MAX );
 
        // Eingabe prüfen
        if ( anzahl_ziffern != EINGABE_ABBRUCH )
        {
            // Ziffern in Zahl umwandeln
            int i = atoi(puffer);
 
            // z.B. erlaubter Wertebereich sei 0 bis 255
            if ( (i >= 0) && (i < 256) )
            {
                UART_puts(" Ok.");
                UART_puts("\r\n");
                *wert = (uint8_t) i;
            }
            else
                UART_puts(" Ung\201ltig.\r\n"); // \201 ist ü in Oktalschreibweise
        }
    }
 
    // Ausgabe auf RS232 (s.oben)
    // bei nur Änderung des Wertes gegenüber der letzten Ausgabe
    //
    // alter_wert wurde mit -1 initialisiert. Das ist ausserhalb
    // des Wertebereichs von *wert, D.h. spätere Abfrage, ob
    // (alter_wert != *wert) ist, ist beim ersten Durchlauf wie
    // gewünscht wahr,
    //
    if (alter_wert != *wert)
    {
        alter_wert = *wert;
        UART_puts("Z\204hler = ");
        itoa((int) *wert, puffer, 10);
        UART_puts(puffer);
        UART_puts(" 0x");
        itoa((int) *wert, puffer, 16);
        UART_puts(puffer);
        UART_puts(" 0b");
        itoa((int) *wert, puffer, 2);
        UART_puts(puffer);
        UART_puts( (*wert & 2) ? " LED2-An " : " LED2-Aus");
        UART_puts( (*wert & 1) ? " LED1-An" : " LED1-Aus");
        UART_puts("\r\n");
    }
}

[bearbeiten] Uhr

Nachdem die Eingabe- und Ausgaberoutinen vorhanden sind, sollen jetzt die weiteren Innereien des ATmega8 erkundet werden. Als erstes kommt der Timer und damit die erste Interrupt-Programmierung an die Reihe. Und was liegt bei dem Thema näher, als eine Uhr zu programmieren...

Zunächst der Aufbau des Projektes:

  • PFA_uhr.c - Das Hauptprogramm
  • PFA_funkhw.h - Definitionen für das PFA ;-) Board
  • PFA_rs232.c - Erweiterte rs232-Funktionen
  • PFA_rs232.h - Prototypen zu den rs232-Funktionen
  • PFA_uart.c - UART Grundfunktionen (aus ehemaligem rs232.c herausgezogen)
  • PFA_uart.h - Prototypen zu den UART Grundfunktionen
  • lcd-routines.c - Unverändert s.o.
  • lcd-routines.h - Unverändert s.o.

PFA_uhr.c - Das Hauptprogramm

Drei Funktionen werden für die Uhr benötigt. Selbstverständlich eine Funktion zum Anzeigen der Uhrzeit auf dem LCD (Bsp.: uhrzeit_lcd). Und eine Funktion zum Starten der Uhr (Bsp.: uhr_init).

Sowie eine TickTack-Funktion, die regelmässig die Uhrzeit erhöht und zwar egal was das Programm sonst macht (Tasteneingabe, RS232 Ausgabe...)! Diese letzte Funktion heisst ISR(TIMER0_OVF_vect) und ist eine sog. Interrupt Service Routine für den TIMER0 OVerFlow Interrupt. Die Namen solcher ISR Funktionen sind nicht frei wählbar, sondern den einzelnen Interrupts fest zugeordnet. Wie die ISRs heissen, kann man in der Dokumentation zum jeweiligen AVR nachlesen.

Die Auswahl von TIMER0 ist willkürlich. Der ATmega8 hat insgesamt drei Timer, die unterschiedliche Fähigkeiten haben. TIMER0 ist davon der mit der geringsten Ausstattung, aber selbst die ist noch ausreichend, um damit eine Uhr zu programmieren. So können die anderen Timer für anderes freigehalten werden.

TIMER0 ist ein 8-Bit Timer, d.h. er kann so einstellt werden, dass er ab einem Startwert TCNT0 jedesmal um eins hochzählt, wenn er von seiner Taktquelle angetrieben wird. Die Taktquelle für den TIMER0 kann die Taktquelle des AVR sein oder ein durch den sog. Vorteiler (Prescaler) von