Pollin Funk-AVR-Evaluationsboard

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Aufbau

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 lies 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).

Funktionstests

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 bei AVR nicht eingesetzt, MAX232 nicht eingesetzt
  • ca. 22 mA bei AVR nicht eingesetzt, MAX232 nicht eingesetzt, RESET gedrückt
  • ca. 25 mA bei AVR nicht eingesetzt, MAX232 eingesetzt
  • ca. 26 mA bei AVR nicht eingesetzt, MAX232 eingesetzt, RESET gedrückt
  • ca. 30 mA bei ATmega8 eingesetzt, MAX232 eingesetzt

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.

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)

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 RC

@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

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 RC

@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

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.

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

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

Beispielprogramme

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

<c> /*

   Atmega8
   Pollin Funk-AVR-Evaluationsboard v1.1
   Project -> Configuration Options in AVR Studio:
   Frequency:    1000000 bzw. 12000000
   Optimization: -Os
  • /
  1. include <avr/io.h>
  2. include <util/delay.h>

// LEDs sind active-high geschaltet

  1. define LED_AN(LED) (PORTD |= (1<<(LED)))
  2. define LED_AUS(LED) (PORTD &= ~(1<<(LED)))
  3. define LED_TOGGLE(LED) (PORTD ^= (1<<(LED)))
  4. define LED1 PD6
  5. define LED2 PD5
  6. 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);
 }

} </c>

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.

<c>

/*

   Atmega8
   Externer Quarz-Oszillator: 12 MHz
   Pollin Funk-AVR-Evaluationsboard v1.1
  • /
  1. include <avr/io.h>
  2. include <util/delay.h>

// LEDs sind high-active geschaltet

  1. define LED_AN(LED) (PORTD |= (1<<(LED)))
  2. define LED_AUS(LED) (PORTD &= ~(1<<(LED)))
  3. define LED_TOGGLE(LED) (PORTD ^= (1<<(LED)))
  4. define LED1 PD6
  5. define LED2 PD5

// TASTER ist high-active geschaltet

  1. define TASTER PB1
  2. 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);
   }
 }

}

</c>

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.

<c>

/*

   Atmega8
   Externer Quarz-Oszillator: 12 MHz
   Pollin Funk-AVR-Evaluationsboard v1.1
  • /
  1. include <avr/io.h>
  2. include <util/delay.h>
  3. include <inttypes.h>

// LEDs sind high-active geschaltet

  1. define LED_AN(LED) (PORTD |= (1<<(LED)))
  2. define LED_AUS(LED) (PORTD &= ~(1<<(LED)))
  3. define LED_TOGGLE(LED) (PORTD ^= (1<<(LED)))
  4. define LED1 PD6
  5. define LED2 PD5

// TASTER ist high-active geschaltet

  1. define TASTER PB1
  2. define TASTER_GEDRUECKT() (PINB & (1<<TASTER))
  3. define TASTE_AUF 0
  4. 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;
 }

}

</c>

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 sol, 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.

<c>

/*

   Pollin Funk-AVR-Evaluationsboard v1.1
   Atmega8
   Externer Quarz-Oszillator: 12 MHz
   Optimierung: -Os
   RS232-Einstellungen:
   9600 Baud, 8 Bits, No Parity, 1 Stopbit
  • /
  1. include <avr/io.h>
  2. include <util/delay.h>
  3. include <stdlib.h>

// LEDs sind high-active geschaltet

  1. define LED_AN(LED) (PORTD |= (1<<(LED)))
  2. define LED_AUS(LED) (PORTD &= ~(1<<(LED)))
  3. define LED_TOGGLE(LED) (PORTD ^= (1<<(LED)))
  4. define LED1 PD6
  5. define LED2 PD5

// TASTER ist high-active geschaltet

  1. define TASTER PB1
  2. define TASTER_GEDRUECKT() (PINB & (1<<TASTER))
  3. define TASTE_AUF 0
  4. define TASTE_ZU 1

/* in Millisekunden */

  1. 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++;
   }

}

  1. define EINGABE_START 0
  2. define EINGABE_ABBRUCH EINGABE_START
  3. define EINGABE_MAX 3
  4. 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() )
       {
  1. if ENTPRELLUNG
           _delay_ms(ENTPRELLUNG);
           if ( TASTER_GEDRUECKT() )
  1. endif /* ENTPRELLUNG */
           {
               // Wechsel von OFFEN nach GESCHLOSSEN?
               if ( alter_tastenzustand == TASTE_AUF )
               {
                   zaehler++;
                   alter_tastenzustand = TASTE_ZU;
               }
           }
       }
       if ( !TASTER_GEDRUECKT() )
       {
  1. if ENTPRELLUNG
           _delay_ms(ENTPRELLUNG);
           if ( !TASTER_GEDRUECKT() )
  1. endif /* ENTPRELLUNG */
               alter_tastenzustand = TASTE_AUF;
       }
   }

}

</c>

(Wird fortgesetzt)

Forenbeiträge

Weblinks