Home: www.rowalt.de
Keine Angst vor Mikrokontrollern!
Der einfache Einstieg in die Welt der AVRs (3)
Roland Walter, DL7UNO

Die aktuellen Qelltexte und Compilate zu den Beiträgen

Nachdem das Hard- und Software-Drumherum in der letzten Folge endlich abgeschlossen werden konnte, geht es nun endlich konkret zur Sache. Vorweg will ich aber noch kurz auf Fragen eingehen, die ich per E-Mail erhalten habe.

FAQ-Teil

Speaker: Ich hatte den Innenwiderstand des Speakers mit >250 Ohm angegeben. Gemeint war der minimale Gleichstrom-Widerstand (nicht die Impedanz), weil ein AVR-Ausgang bis zu 20mA treiben kann.
MAX232: Stattdes MAX232A kann auch der "normale" (billigere) MAX232 verwendet werden. Dann aber müssen alle 4 Kondensatoren durch (teuere) 1uF-Typen ersetzt werden. Bitte prüfen, ob die Kondensatoren aufs Board passen, denn der Platz wurde für 100nF-Keramikkondensatoren berechnet. Datenblätter zum MAX232 gibt es auf www.maxim-ic.com


Die I/O-Pins

Das erste Programm

Wie im letzten Teil angekündigt, starten wir mit den I/O-Pins in ihrer Urfunktion, also zunächst ohne die mögliche Umnutzung für Timer, externe Interrupts u.s.w.

Bitte verbinden sie die rote LED mit dem AVR-Pin PD3, die gelbe LED mit PD4 und die grüne LED mit PD5. Das zugehörige Programm ist im folgenden Kasten (übrigens war hier monatelang ein falsches Listing im Kasten - danke an Stefan Breunig für den Hinweis).

Die Steckverbindungen zum ersten Programm

'0001.BAS: Rote LED an Pin PD3, gelbe an Pin PD4, grüne an PD5
'--------------------------------------------------------------
$Regfile = "2313def.dat"  'AT90S2313
$Crystal = 3686400        'Quarz: 3.6864 MHz
DDRD     = &B0001000      'Pin PD3 als Ausgang konfigurieren
PORTD    = &B11111111     'PD0...PD7 auf High setzen
Do
Loop
End

Nach dem Brennen des Programms in den AVR muß die rote LED (und nur diese) aufleuchten. Falls das Brennen an sich erfolgreich war, aber die LED nicht leuchtet, dann schalten Sie bitte kurz die Betriebsspannung aus und wieder ein. Nach dem Brennen eines neuen Programms ist eine Power-Off-Sequenz vorgeschrieben. TwinAVR macht das, aber es könnte sein, daß es ein anderer Programmer nicht macht.

Doch kommen wir zum Programm: Was sich selbst erklärt, werde ich in der Mehrzahl Ihrer eigenen Überlegung überlassen. Bascom hat übrigens eine kontextsensitive Hilfe, d.h. sie setzen den Kursor in einen Befehl, zu dem sie Hilfe benötigen und drücken dann F1. Meist wird dann die korrekte Hilfeseite geöffnet.

Bascom beginnt die Ausführung des Programms in der ersten Zeile. Lachen sie nicht, denn bei vielen Programmiersprachen ist das anders.

Das Statement $Regfile gibt die Include-Datei an, welche die Deklarationen für den verwendeten Prozessors enthält. Dort stehen die Adressen der vorhandenen Register, die Namen der einzelnen Bits der Register und die Größe des Flash- und EEPROM-Speichers.

Das Statement $Crystal gibt die Quarzfrequenz in KHz an. Dieses Statement werden wir in jedem Listing wiederfinden, da viele Funktionen frequenzabhängig sind.

In der nächsten Zeile wird ein Wert ins DataDirectionRegisterD geschrieben. In diesem AVR-Register wird festgelegt, welche Datenrichtung jeder Pin von PortD haben soll. Jedes Bit in diesem Register korrespondiert mit einem I/O-Pin, sodaß hier die Binärdarstellung mit &B... am anschaulichsten ist (es gibt auch &H für die Hexadezimal- und &O für die Oktaldarstellung einer Zahl).

Der AT90S2313 verfügt über PortB mit 8 Pins und PortD mit 7 Pins, folgerichtig gibt es für die Festlegung der Datenrichtung die Register DDRB und DDRD. Die Beschreibung dieser Register gehört logisch nicht in die Hilfe von Bascom, sondern ins Atmel-Datasheet zum AT90S2313. Schauen Sie also dort mal rein. Wenn ein Pin auf Ausgang gesetzt werden soll, muß das korrespondierende Bit im Register DDRx auf 1 gesetzt werden. Soll er Eingang sein, muß das korrespondierende Bit auf 0 gesetzt werden. Registerwerte für nicht vorhandene Pins werden ignoriert. In unserem Beispiel ist nur Pin PD3 auf Ausgang gesetzt. Alle anderen Pins von PortD sind Eingänge.

Nun folgt eine Zeile, in der alle 8 Bits des PORTD-Registers auf 1 gesetzt werden. Das ist der eigentliche Befehl, mit dem wir die rote LED einschalten, denn eine 1 im PORTx-Register setzt den korrespondierenden Pin auf High (+5V). Die gelbe und die grüne LED bleiben dunkel, weil wir zuvor die betreffenden AVR-Pins nicht auf Ausgang gesetzt hatten. Mit der Anweisung DDRD = &B0011100 würden alle 3 LEDs aufleuchten.

Das war schon die grundlegende Erklärung, wie man die AVR-Pins als normale Daten-Ein/Ausgänge nutzen kann. Aber etwas wichtiges fehlt noch.

Das zweite Programm

Das Listing im folgenden Kasten enthält das neue Register PIND. Dies ist ein Nur-Lese-Register, aus dem man den augenblicklichen Zustand des jeweiligen Input-Pins lesen kann. Aber ein Daten-Input ist keine so saubere Sache wie ein softwareseitig gesetztes Bit. Ein Digital-Eingang darf man nie in der Luft hängen lassen, weil er sonst wie eine Antenne wirkt und sich Störungen aus der Umgebung einfängt. Testen Sie bitte das folgende Programm (die PORTD-Zeile bitte noch auskommentiert lassen). Sie werden wahrscheinlich feststellen, daß die LED ihren Zustand ändert, wenn Sie mit der Hand in die Nähe des Tasters oder des Verbindungsdrahtes kommen.


'002.BAS:   Auf Tastendruck LED einschalten
'Hardware:  Taster an PD2, LED an PD3
'---------------------------------------------------
$Regfile = "2313def.dat"  'AT90S2313-Deklarationen
$Crystal = 3686400    'Quarz: 3.6864 MHz
DDRD = &B0001000      'Pin PD3 Ausgang, Rest Eingang
'PORTD = &B0000100    'PullUp von Pin PD2 aktivieren

Do
  If PIND.2 = 0 Then  'Wenn Taster (Pin PD2) gedrückt
    PORTD.3 = 1       'Rote LED (Pin PD3) an
  Else                'Wenn Taster nicht gedrückt
    PORTD.3 = 0       'Rote LED aus
  End If
Loop
End

Wenn Sie jetzt den Kommentarstrich der PORTD-Zeile entfernen und das Programm widerum testen, leuchtet die LED wie erwartet erst nach Tastendruck auf. Was wurde mit PORTD gemacht? - Wenn man einen Pin im Register DDRx auf Eingang setzt, würde es normalerweise keinen Sinn ergeben, den Pin im PORTx-Register auf 1 zu setzen, denn zum Lesen eines Input-Pins wird ja das Register PIND benutzt. Die Auflösung steht schon im Kommentar des Listings: Dieses logisch unlogische Bit aktiviert einen internen PullUp-Widerstand, der den Eingang sauber auf +5V zieht. Aus diesem Grund sind die Taster auf dem AVR-Board auch auf der Masse-Seite.

Das Setzen der Datenrichtung könnte in Bascom übrigens auch mit der Basic-Anweisung Config PORTx erfolgen. Dies hätte exakt die selbe Wirkung, würde Sie aber vom AVR-Datasheet fernhalten, was nicht zum systematischen Verständnis des AVRs beitragen würde.


Die Serielle Schnittstelle

Allgemein: Die meisten AVR-Typen haben eine Hardware-UART eingebaut. Das Wort UART sagt zunächst einmal nur aus, daß es sich um einen universellen Sende-Empfänger handelt, der asynchron Daten überträgt. Die asynchrone Datenübertragung erfolgt im Gegensatz zur synchronen ohne separates Taktsignal. Deshalb ist nur eine Sende- und eine Empfangsleitung sowie eine Masseleitung erforderlich. Ein UART-Empfänger muß das eintreffende Signal mit einer mehrfachen Taktferquenz abtasten und so das Taktsignal rückgewinnen. Das setzt ein akkurates Timing beim UART-Sender voraus. Man kann eine UART durchaus als reine Software-Lösung realisieren und Bascom bietet diese Möglichkeit auch. Aber das kostet sehr viel Rechenzeit (und Programmspeicher), bringt Fehler in der Datenübertragungsrate mit sich und läßt logischerweise keine hohen Übertragungsraten zu. Bitte achten sie in der Bascom-Hilfe genau darauf, ob sich ein bestimmter Befehl auf die Hard- oder Software-UART bezieht und bringen Sie beides nicht durcheinander.

Datenwort: Die gebräuchlichste Übertragungsart für ein Datenwort ist 8N1, also 8 Datenbits, kein (N) Paritätsbit und 1 Stopbit. Die Hardware-UART des AVR bietet mehr Möglichkeiten (z.B. auch 9 Datenbits), aber das will ich hier nicht behandeln. Bascom verwendet standardmäßig 8N1.

Baudrate: Die Baudrate sollte bei einer asynchronen Schnittstelle wirklich korrekt sein. Die Baudrate wird im AVR nur als Baudratencode gespeichert, der dann in Abhängigkeit von der Quarzfrequenz die Baudrate ergibt. Der Baudratencode ist nur ein Byte groß und da ein Byte nur 256 Werte zuläßt, kann natürlich nicht jede beliebige Baudrate generiert werden. Will man mit der UART arbeiten, dann diktiert die gewünschte Baudrate den einsetzbaren Quarz! - Bei der Auswahl des richtigen Quarzes kann Ihnen das kleine Tool AvrBaud helfen, das ich in der letzten Folge vorgestellt hatte. Die generierte Baudrate sollte niemals mehr als 3% von der gewünschten abweichen, weil es sonst zu Erkennungsfehlern kommen kann. Für eine sichere Übertragung sollte die erzeugte Baudrate eine Abweichung von höchstens 2% gegenüber der gewünschten haben. DAs folgende Bild verdeutlicht am (extrem großen) Baudratenfehler von 30%, wie Bits falsch interpretiert werden können.

Der UART-Empfänger des AVR tastet das eintreffende Signal um den Faktor 16 höher als die eingestellte Baudrate ab und führt anschließend auch noch eine Fehlererkennung der 16 Samples jedes Bits durch. Das ist eine ordentliche Implementation. Aber verlassen Sie sich nicht darauf, daß das Gerät auf der Gegenseite genauso gut ist.

Sowohl beim sende-, als auch beim empfangsseitig besteht der UART-Puffer beim AVR nur aus einem einzigen Byte. Die festzulegende Baudrate darf deshalb nicht auf der durchschnittlichen zu erwartenden Datenrate beruhen, sondern auf der maximal zu erwartenden Datenrate. Ebenso kann eine geradezu unsinnig hohe Baudrate Sinn machen - schlicht und einfach deshalb, um zu sendende Bytes möglichst schnell loszuwerden und im eigentlichen Programm weitermachen zu können. Bascom bietet zwar die Einrichtung von Software-Warteschlangen für die UART an (Config Serialin und Config Serialout), aber dafür ist laut Hilfe der Anschluß eines externen SRAM-ICs erforderlich. Ich selbst habe damit noch nicht gearbeitet.

UART-Typ: Die Hardware-UART des AVR gibt lediglich TTL-Pegel aus. Das heißt, sie können zwar z.B. zwei AVRs direkt miteinander verbinden (TxD- und RxD-Leitungen gekreuzt!), aber ein direkter Anschluß an den PC ist so nicht möglich. Per TTL-Signal können schlicht und einfach keine längeren Wege überbrückt werden, weshalb die Verbindung verschiedener Geräte untereinander eine Pegel- oder Stromwandlung erforderlich macht. Bei der seriellen Schnittstelle des PCs wird z.B. die RS232-Norm (anderer Name: V.24) verwendet. Das eingesetzte Verfahren ist eigentlich veraltet und bestimmt nicht das beste, aber es hat sich eben durchgesetzt. Wie auf TTL-Ebene erfolgt die Kodierung durch verschiedene Spannungspegel. Eine logische 1 (Mark) entspricht einer Spannung von -3...-15 Volt und eine logische 0 entspricht einer Spannung von +3...+15 Volt. Der benötigte Pegelwandler muß also nicht nur höhere Spannungen bereitstellen, sondern das Signal auch invertieren. Der bekannteste IC für diesen Zweck ist der MAX232. Der Vorteil dieses ICs ist, daß er nur die Betriebsspannung von +5V benötigt und daraus die RS232-Spannungen erzeugen kann. Mehr als diese Spannungswandlung macht der MAX232 nicht!

Neben RS232 gibt es aber noch eine Reihe von anderen Verfahren, die vor allem in der Industrie Verwendung finden. So wird z.B. bei der RS485 keine Spannungs- sondern eine Stromkodierung verwendet. RS485 ermöglicht viel größere Entfernungen und erlaubt darüber hinaus den Anschluß von bis zu 32 Sendern/Empfängern an die selbe Leitung. Auch hier gibt es preiswerte ICs, die unkompliziert an die AVR-UART angeschlossen werden können. Ein Beispiel dafür ist der LTC485.

Und schließlich soll nicht vergessen werden, daß man den AVR mit Hilfe eines geeigneten ICs auch mit der USB-Schnittstelle verbinden kann. Das setzt allerdings einen intelligenten Peripherie-IC voraus und ist ein eigenes Thema. Ein sehr schöner USB-IC ist der FT8U232AM von Future Technology Devices.

Wir bleiben aber bei der gewöhnlichen RS232, für die das Experimentierboard den MAX232 enthält und verwenden nur das Standard-Datenwort 8N1. Alles andere wäre im Moment viel zu verwirrend.

UART-Sender

Fangen wir ganz einfach mit einem reinen Sender an. Diese Funktion wird Ihnen beim Debuggen noch sehr nützlich werden. Verbinden Sie bitte das AVR-Board per Nullmodemkabel mit dem PC und starten Sie AVRterm.

Im folgenden Kasten steht das zugehörige Bascom-Programm.


'0003.BAS:   UART-Sender
'Hardware:  MAX232 an PD0/PD1, Nullmodemkabel zum PC
'---------------------------------------------------
$Regfile = "2313def.dat"  'AT90S2313-Deklarationen
$Crystal = 3686400    'Quarz: 3.6864 MHz
$Baud    = 9600       'Baudrate der UART: 9600 Baud

Dim i As Byte
Dim wTest As Word

i = 1
wTest = 35000

Do
  Printbin i ; wTest
  Print "Hallo AVR: " ; i ; wTest
  Print "<--->" ;
Loop
End

Der gesendete Text ist im folgenden Bild zu sehen.

Zuerst fällt das neue Basic-Statement $Baud auf. Der Compiler errechnet aufgrund der hier angegebenen Baudrate und der Quarzfrequenz den AVR-internen Baudratencode, der dann ins Baudratenregister (UBRR) des AVRs gelangt. Noch einmal zur Erinnerung: Es kann nicht jede beliebige Baudrate verwendet werden. Bei unserem 3,6864MHz-Quarz lassen sich die Standard-Baudraten fehlerfrei generieren, z.B.: 9600, 14400, 19200, 28800, 38400, 57600, 76800 und 115200.

Anschließend deklarieren wir zwei Variablen, deren Inhalt wir später über die serielle Schnittstelle ausgeben wollen und geben ihnen einen Wert.

In der Hauptschleife werden dann zwei verschiedene Bascom-Print-Befehle zur Ausgabe an die serielle Schnittstelle verwendet. Es mag beim erstenmal verwirrend sein, daß der Print-Befehl in Bascom für die Ausgabe auf der seriellen Schnittstelle verwendet wird. Aber andererseits ist es auch wieder logisch beim monitorlosen Mikrocontroller. Print ein sehr komfortabler Befehl, dessen Syntax an das altbekannte Quickbasic angelehnt ist. Wie bei Quickbasic werden numerische Variablen intern in Strings umgewandelt und sind deshalb im Terminalprogramm als Klartext zu sehen. Der Befehl Print hat aber seinen Preis, denn er frißt sehr viel Programmspeicher. Der Befehl Printbin gibt den Inhalt der Variablen ohne Wandlung aus, im Textmodus des Terminalprogramms erscheinen die Variablen i und wTest deshalb nur als Sonderzeichen (i als ein Byte und wTest als zwei Bytes).

Das war schon die Haupsache zur UART-Ausgabe. Hier nur noch eine Ergänzung: Mit dem Befehl Baud (ohne $-Zeichen am Anfang) kann die Baudrate zu einem späteren Zeitpunkt verändert werden - es ist aber immer erforderlich, die initiale Baudrate mit dem Compiler-Statement $Baud zu setzen.

Der Datenempfang wird in der nächsten Folge behandelt. Wir starten mit der leicht zu verstehenden Pollmethode und werden dann die Nutzung der Interrupts behandeln, die nicht nur die Arbeit mit der seriellen Schnittstelle effektiver machen kann.