Bauen und Programmieren von AVR-Mikrocontrollern am Beispiel von ATMEL ATTiny 2313 und ATMega 8
Es gibt SMD-LEDs mit drei einzelnen LED-Zellen auf einem Träger, die so nah aneinander liegen, dass das Auge sie aus einiger Entfernung nicht mehr getrannt wahrnimmt. Damit kann man gut die additive Farbmischung zeigen. Mit drei einfachen Schaltern kann man bereits mit den drei LEDs und drei angepassten Vorwiderständen sieben Farben und Schwarz (=kein Licht) mischen. In der Tabelle bedeutet die Beschriftung, dass die jeweilige LED an ist. Die Hintergrundfarbe zeigt das Ergebnis an.
rote LED | grüne LED | blaue LED |
rote LED | ||
grüne LED | ||
blaue LED | ||
rote LED | grüne LED | |
grüne LED | blaue LED | |
rote LED | blaue LED | |
rote LED | grüne LED | blaue LED |
Die Idee
war nun, das Schalten einem AVR zu überlassen und zwar in der Weise, dass die Einschaltdauer jeder LED in ganz kleinen Stufen geändert werden konnte. Diese Idee konnt durch den Einsatz der Pulsweitenmodulation (PWM) gelöst werden. Innerhalb eines festen Zeitintervalls von ca. 0,25 ms (= Schaltfrequenz ca. 3,9 kHz) wird der Anteil der Einschaltzeit (= die Pulsweite) variiert. Dadurch gibt die LED proportional zur Einschaltzeit unterschiedlich viel Lichtenergie ab, was sich in der wahrgenommenen Helligkeit äußert, denn die einzelnen Pulse folgen so schnell aufeinander, dass das Auge sie nicht mehr getrennt registrieren kann.
Die PWM-Unit
Für genau diesen Zweck stellt ATMEL in den meisten seiner Controller über die "Output Compare Unit" pro Timer/Counter ein bis zwei PWM-Steuerungen zur Verfügung. Im Fall der vorliegenden Aufgabe reicht ein TINY 2313 mit seinen beiden Countern, von denen jeder zwei Output compare Einheiten versorgen kann. Die entsprechenden Signalausgänge sind an den Pins PORTB.2 (Pin 14) = OC0A, PORTD.5 (Pin 9) = OC0B und PORTB.3 (Pin 15) = OC1A herausgeführt. Das vierte Signal OC1B wird nur intern zur Zeitsteuerung benutzt, also nicht nach außen durchgeschaltet, es wäre auch kein weiterer Pin verfügbar.
Die Bedienung
Die Steuerung des AVR kann wahlweise (auch gemischt) über die RS232 oder drei Tasten erfolgen. Im letzeren Fall schaltet Taste T1 die Farbkanäle durch, während T2 den Wert für die Pulsweite hochzählt, verringert ihn T3. Wird beim Einschalten bereits die Taste T1 gedrückt, läuft ein Automatikprogramm in wiederholter Folge ab. Die Steuerung via RS232 erlaubt das Hoch- und Herunterzählen der RGB-Werte unabhängig von einer Kanal wahl - r, g, b verringern die Werte, R, G, B erhöhen sie. Zusätzlich wird durch "n" die geringste Leuchtstärke und durch "v" die größte hervorgerufen. "a" startet das Automatikprogramm.
Die Schaltung
Die Tasten T1 bis T3 sind an den Pins PORTD.2 (Pin 6) bis PORTD.4 (Pin 8) angeschlossen, die daher als Eingänge zu konfigurieren sind. Am Pin 9 (OC0B) liegt über einen 470 Ohm-Widerstand die blaue LED gegen Vcc. An Pin 14 (OC0A) liegt die rote LED über 220 Ohm an Vcc und an Pin 14 (OC1A) über ebenfalls 220 Ohm die grüne LED. Eine Betriebsanzeige, die nur kurz aufblinkt und damit Batterieenergie spart, kann über 220 Ohm an Pin 11 (PORTD.6) angeschlossen werden. Der RS232-Eingang RXD liegt wie üblich an Pin 2 und der Ausgang an Pin 3 (TXD). Ein 2-zeiliges LCD mit 16 Zeichen pro Zeile ist wie folgt angeschlossen:
LCD D7 | LCD D6 | LCD D5 | LCD D4 | LCD E | LCD RS |
PORTB.7 | PORTB.6 | PORTB.5 | PORTB.4 | PORTB.1 | PORTB.0 |
Die RW-Leitung des LCD ist direkt an Masse gelegt.
Der dargestellte Bestückungsplan ist für einen SMD-Typ des TINY 2313 gezeichnet und zeigt die Platine von deren Unterseite (Kupferseite) her. Die Tasten werden an die linken Anschlüsse der 10k SMD-Widerstände gegen Masse gelegt.
Das Layout ist gespiegelt und für die Bügelmethode vorbereitet. Dazu muss zuvor aber mit dem Faktor 0,339 verkleinert werden, denn die Datei zeigt eine Vergrößerung (295%). Nach dem Ausdruck kommt die Druckseite direkt auf das Kupfer der Platine. Im Layout sind die drei Tasten zur Steuerung bereits vorgesehen, nur die Verbindungen zu den Widerständen müssen noch hergestellt werden.
Auf eine extra Schaltskizze wird wegen der detailierten Beschreibung und den Abbildungen verzichtet. Natürlich kann die Schaltung auch mit der Experimentierplatine für den TINY2313 realisiert werden. Die hier abgebildete Platine wurde von mir in einem Einzelgerät eingesetzt, das für die oben genannten Demonstrationszwecke benutzt wird, deshalb wurde auf Miniaturisierung geachtet. In einem Gehäuse von Reichelt (GEH KSB 01B) mit 101 x 60 x 26 mm sind die Platine selbst, das LCD, der Spannungskonstanter, die RS232 und natürlich ein 9V-Block zur Energieversorgung untergebracht.
Die Software
Die Firmware greift im Wesentlichen auf bereits vorhandene Bibliotheksmodule zurück, die zusammen mit der Hauptdatei "rgb_dimmer.asm" im gleichen Projektverzeichnis liegen. Das betrifft die Header-Dateien
"arithmetik_header.asm", "lcd-routines-bib_header.asm", "rs232inc_header.asm",
sowie die Codedateien
"arithmetik.asm", "lcd-routines-bib.asm", "rs232inc.asm"
Der LCD-Anschluss an die oberen 4 Bits des Ports B erforderte die Anpassung des Headers für die LCD-Steuerung sowie des dazugehörigen Codes. Die Tastenabfrage und -Entprellung wurde vom Projekt "Datenlogger" übernommen und geringfügig angepasst. Verbessert wurde auch die Koordinierung der RS232- und Tastenabfrage. Die Änderungen sind eher geringfügig und finden hier keine nähere Erwähnung, sie können den Quelldateien der Bibliotheken entnommen werden.
Projektspezifisch dagegen ist in der Firmware der PWM-Teil, der nun besprochen werden soll.
Die Portleitungszuweisung für LEDs und Taster kennen wir bereits. Die RGB-LEDs sind lowaktiv, das heißt, dass sie leuchten, wenn der LED-spezifische Anschluss auf 0V = GND liegt. Das geht deshalb, weil alle Anodenanschlüsse der LEDs direkt an Vcc = +5V liegen. Wenn der AVR-Pin die Kathoden der LEDs also auf GND-Potential zieht, dann liegt die Betriebsspannung über den Begrenzungswiderstand an der LED und sie leuchtet. Auch die Tasten sind lowaktiv, denn am AVR-Eingang liegt im Ruhezustand (offener Schalter) über den 10k - Ohm - Widerstand die Bertiebsspannung Vcc = +5V. Wird der Taster gedrückt, schließt der Schalter und der Eingang wird auf 0V = GND gelegt.
Die Portleitungen für die LEDs werden auf Ausgang und zunächst high (=logisch 1 = +5V) gesetzt. An Anode und Kathode liegt nun das gleiche Spannungspotential, es kann kein Strom fließen, die LEDs sind aus.
Die Werte für die Ausgangs-Vergleichsregister (Output Compare Register = OCR) der Timer werden auf die Startwerte gesetzt. Zur Funktion dieser Vergleiche muss man nur wissen, dass erst einmal die Timer beim Stand 0 aufwärts zu zählen beginnen. Wird ein Zählerstand erreicht, der mit dem Wert in einem Vergleichsregister übereinstimmt, dann wird ein Gleichstandsereignis ausgelöst. Dieses führt, je nach Konfiguration der Einheit dazu, dass ein internes Bit gesetzt oder rückgesetzt wird. Bei entsprechender Programmierung wird dieser Zustand nach außen zum OC-Pin durchgeschaltet, das daraufhin auch seinen Spannungspegel ändert. Gleichzeitig kann auch noch ein IRQ ausgelöst werden, der ist im vorliegenden Fall aber nur für die Zeitsteuerung relevant. Für die Vergleichsregister A und B von Timer0 (8-Bit) wird kein IRQ ausgelöst, sehr wohl dagegen das entsprechende Ausgangspin geschaltet. Gleiches gilt für das Vergleichsregister A von Timer1, der - obwohl 16-Bit-fähig - aus Kompatibilitätsgründen nur als 8-Bit-Timer verwendet wird. In diesem Projekt wird nun der Ausgang des OC-Pins jedes Mal auf GND gelegt (LED schaltet ein), wenn der Zähler überläuft (255 nach 256 = 0). Im Moment des Gleichstands von TimerXregister und VergleichsregisterXY wird der Ausgang auf 1 = high = +5V gelegt, die LED erlischt und bleibt dunkel, bis der Zähler den höchsten Stand von 255 erreicht hat. Dann beginnt der Zyklus von Neuem. Hier wird auch auf das Diagramm in den Datenblättern des Tiny 2313 auf Seite 72 verwiesen.
Wichtig ist die korrekte Initialisierung der Timersteuerregister, die hier dargestellt wird. Die COMXY-Einstellungen in TCCRXA regeln das Schaltverhalten des Ausgangs und sind in den Datenblättern ab Seite 77 beschrieben. Die WGMnm-Bits stellen den Zählbetrieb auf "Fast-PWM"-Betrieb ein, wie er oben beschrieben wurde. Die CSnm-Bits legen den Vorteiler 8 für alle Timer fest. Da der Systemtakt durch den Quarz auf 8 MHz festgelegt ist, beträgt der Timereingangstakt 1 MHz. Im "TIMSK"-Register wird festgelegt, welches Ereignis einen IRQ auslösen darf, nachdem IRQs global zugelassen wurden. So wird ein IRQ ausgelöst, wenn der Timer1 den eingestellten Höchststand von 255 erreicht hat (TOIE1 = 1). Gleiches gilt für den Überlauf von Timer0 (TOIE0 = 1) Beim Überlauf der Timer muss nämlich für den nächsten Durchgang das Vergleichsregister neu gesetzt werden, falls der zuständige Wert in der Zwischenzeit geändert wurde. Das erledigen die entsprechenden ISR. Wenn Timer1 mit dem Vergleichsregister B übereinstimmt, wird zwar kein Ausgang umgeschaltet, aber der zugehörige IRQ bewirkt in der ISR das Auslesen der Tasten T1 bis T3 und deren Entprellung. Ferner wird auf Tastenbetätigung mit Kanaleinstellung der LEDs und dem Hoch- oder Herunterzählern des PWM-Werts reagiert. Abschließend wird in der zugeordneten ISR die LCD-Anzeige upgedatet.
Nach der globalen Freigabe der IRQs wird beim Programmstart die Taste T1 abgefragt. Falls sie gedrückt ist, startet der Controller die Programmautomatik.
Die Hauptschleife ist seh kurz, weil die Tasten auf der AVR-Platine durch die ISR bedient werden. Die Abfrage von Tasten auf dem PC, die per RS232 übermittelt werden, erledigt die UP-Routine "tastenabfrage", die jedoch übersprungen wird, wenn kein Tastencode gesendet wurde. In diesem Fall ist das Bit im Register UCSRA.RXC = 0. Andernfalls wird der Tastencode ernmittelt und der entsprechende Befehl ausgeführt.
Die Steuerinformationen zwischen Hauptprogramm und ISR werden wieder über Nachrichten (Messages) ausgetauscht. Träger der Nachrichten sind Flags in bestimmten Registern. Wurde durch eine Tastenbetätigung zum Beispiel das Erhöhen des Blauwerts angefordert, dann setzt das Hauptprogramm das Flag "fblau" im Register "flag". Das kann zu einem beliebigen Zeitpunkt geschehen. Die regelmäßig aufgerufene ISR für den Überlauf des Timer0 fragt nun ab, ob dieses Flag "fblau" gesetzt ist. Wenn das der Fall ist, wird nicht sofort zum Test des Rot-Flags gesprungen, sondern der neue Blauwert, den das Hauptprogramm haben möchte (steht in der SRAM-Speicherzelle "blau_wert") genau zum richtigen Zeitpunkt gesetzt. Zum Schluss der Sequenz wird das Flag "fblau" durch die ISR zurückgesetzt. Anschließend wird der Rot-Wert auf ähnliche Weise behandelt. Diese etwas aufwendige Prozedur ist nötig, weil ein neuer Wert für das Vergleichsregister am besten genau in dem Moment gesetzt werden soll, wenn der Timer auf 0 zurückgesetzt wird. Dies ist genau dann der Fall, wenn der Höchststand erreicht ist (=Überlauf).
Natürlich müssen beeinträchtigte Register beim Eintritt in die ISR gesichert und am Ende restauriert werden.
Das gleiche Verhalten das wir eben für rot und blau kennengelernt haben, gilt auch für die grüne LED.
Alle anderen Funktionen, wie Tastenabfrage und Zeitsteuerung habe ich schon im Kapitel "Datenlogger" vorgestellt. Die geringfügigen Anpassungen an das aktuelle Projekt kann man den Quelldateien, vor allem der Datei "rgb_dimmer.asm" leicht entnehmen.
Als Steuerung der AVR-Anwendung vom PC aus lässt sich übrigens sehr gut das systemeigene Programm "Hyperterminal" einsetzen.
Die Realisierung dieses Projekts bedeutet einen Rückschritt vom RGB-Projekt. Ist es hier doch nur nötig, einen einzigen Kanal nach oben oder unten zu steuern. Ich habe mich bei der Umsetzung für den Rot-Kanal des Vorgängerprojekts entschieden. Diese Entscheidung war rein willkürlich. Unter anderen Gesichtspunkten wäre vielleicht die Entscheidung für den grünen Kanal besser gewesen, weil dadurch ein ganzer Timer (Timer0) frei geworden wäre. Natürlich könnte man unter den gegebenen Voraussetzungen auch die Zeitsteuerung auf Timer0 übertragen, wodurch Timer1 frei wird. Wie auch immer.
Die Kunst der Reduktion besteht einfach darin, alle Grün- oder Blaukanal bezogenen Programmteile und die Referenzen darauf aus dem Programm zu entfernen. Ein Grobdurchgang erledigt den ersten Aspekt, die Reaktion auf Fehlermeldungen des Assemblers beim neuerlichen Übersetzen den zweiten.
Danach kann die Firmware in den Tiny2313 geflasht werden und sollte dann über einen Transistor als Motortreiber oder über eine Stufe eines ULN2803/04 einen kleinen DC-Motor betreiben können. Der Motor ist folgerichtig am Pin 14 (PORTB.2) = OC0A angeschlossen. Die Treiberstufe entspricht im Aufbau genau der für ein Relais. Essentiell für das Überleben der schaltenden Halbleiter ist es, dass durch eine Diode, parallel zur Spule des Relais oder des Motors dafür gesorgt ist, dass sich am Halbleiter (Transistor oder direkt am Port des AVR) zu hohe Spannungen aufbauen können. Die besagte Begrenzungsdiode muss dafür in Sperrrichtung gepolt werden, das heißt Kathode an Vcc und Anode am kälteren Ende (gegen GND).
Ausblick - Motor-Drehzahl-Regler
Soll das Projekt zum Motordrehzahlregler ausgebaut werden, dann muss man die tatsächliche Drehzahl ermitteln. Das kann durch Zählen von Impulsen erfolgen, welche durch die Rotation der Motorachse hervorgerufen werden, oder durch das Messen einer Spannung, die z. B. ein Tachogenerator abgibt, der mit der Motorachse gekoppelt ist. Letzteres ist auch beim Tiny2313 möglich, wenn man die Messung der analogen Spannung über einen ADC realisiert, der durch Software über eine vom AVR vorgegebene Spannung (via PWM-Ausgang und Siebkondensator) und den Analogkomparator modelliert wurde.
Das festgestellte "IST"-Signal muss dann mit einem vorgegebenen "SOLL"-Signal verglichen werden. Abweichungen nach oben oder unten müssen eine Stellgröße verändern, die ihrerseits das PWM-Signal für den Motor beeinflusst.