Multitasking mit AVR RISC-Controllern - Prof. Dr. Ernst Forgber - E-Book

Multitasking mit AVR RISC-Controllern E-Book

Prof. Dr. Ernst Forgber

0,0
39,99 €

oder
-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.
Mehr erfahren.
Beschreibung

Das Buch stellt anhand praktischer Beispiele für die Atmel-8-Bit-Controller mehrere Ansätze für Multitasking-Programme vor. Ausgehend von der Lösung mit einer einfachen Programmschleife werden Multitasking- und Echtzeitkonzepte erläutert - bis hin zur Entwicklung eines echtzeitfähigen, prioritätsgesteuerten, präemptiven Tasking-Systems. Die Ausführungen der verschiedenen Konzepte erfolgen anhand durchgehender Beispiele. Dabei wird zunächst immer die grundlegende Idee vorgestellt, die dann mithilfe der praktischen Beispielen konkretisiert und in einem anschließenden Blick auf die Details vertieft wird. Aufbau des Buchs: Multitasking, Kontext, Real-Time & Co. Begriffliche Erklärungen und grundlegende Mechanismen. Experimentierumgebung Beschreibung der Hard- und Software. AVR-Controller in C programmieren Besonderheiten bei der Programmierung von AVR-Controllern. Multitasking, die Erste: Die Minimalversion Klassischer Ansatz zum Multitasking unter komplettem Verzicht auf einen Scheduler und dem Versuch, alles über Interrupts und eine große Programmschleife zu realisieren. Betrachtung verschiedener Scheduler mit wachsender Komplexität in Theorie und Praxis. Multitasking, die Zweite: Ein Scheduler im Eigenbau Darstellung der Funktionsweise eines einfachen prioritätsgesteuerten Schedulers für zyklische Funktionen. Multitasking, die Dritte: Kooperation ist gefragt Zeigt die Funktionsweise eines kooperativen Schedulers auf der Basis von Dunkels' Protothreads. Multitasking, die Vierte: Präemptives Tasking Vorstellung eines vollständig prioritätsgesteuerten RTOS-Schedulers mit Präemption.

Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:

EPUB

Veröffentlichungsjahr: 2014

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Worum geht es hier?

Mittlerweile gibt es für praktisch alle Mikrocontroller einen leistungsfähigen C-Compiler mit ansprechender Entwicklungsumgebung, mit deren Hilfe man relativ schnell eigene Anwendungen erstellen kann. Aber selbst kleine Programme haben oft mehrere Dinge gleichzeitig zu erledigen: einen Motor steuern, eine Eingangsspannung messen, Taster und Schalter auslesen, LEDs schalten, Ausgaben über die serielle Schnittstelle an einen PC senden und vieles mehr.

Hat ein Programm mehrere Aufgaben praktisch gleichzeitig zu erledigen, lässt sich dieses Multitasking-Problem auf unterschiedliche Arten realisieren. Dieses Buch stellt anhand praktischer Beispiele für die Atmel-8-Bit-Controller mehrere Ansätze für Multitasking-Programme vor. Ausgehend von einer Lösung mit einer einfachen Programmschleife werden Multitasking- und Echtzeitkonzepte erläutert – bis hin zur Entwicklung eines echtzeitfähigen, prioritätsgesteuerten, präemptiven Tasking-Systems.

Aufbau des Buchs

Das Buch ist wie folgt aufgebaut: Im Kapitel »Multitasking, Kontext, Real-Time & Co.« werden einige Begriffe und grundlegende Mechanismen zum Thema Multitasking erklärt. Das Kapitel »Experimentierumgebung« beschreibt Hardware und Software, mit denen die Beispielprogramme entwickelt und getestet wurden.

Einige Besonderheiten der Programmierung speziell auf AVR-Controllern, wie etwa die Verwendung der seriellen Schnittstelle, der Analogwerteingabe und der Hardware-PWM (Pulse-Width Modulation) sowie die Verwendung von Interrupts, werden im Kapitel »AVR-Controller in C programmieren« behandelt.

Das Kapitel »Multitasking, die Erste: Die Minimalversion« beschäftigt sich mit dem »klassischen« Ansatz zum Multitasking, bei dem man auf einen Scheduler komplett verzichtet und alles über Interrupts und eine große Programmschleife zu realisieren versucht. Daran schließt sich die Betrachtung verschiedener Scheduler mit wachsender Komplexität in Theorie und Praxis an.

Ein einfacher prioritätsgesteuerter Scheduler für zyklische Funktionen wird im Kapitel »Multitasking, die Zweite: Ein Scheduler im Eigenbau« vorgestellt. Das Kapitel »Multitasking, die Dritte: Kooperation ist gefragt« beschäftigt sich mit einem kooperativen Scheduler auf der Basis von Dunkels‘ »Protothreads«. Im Kapitel »Multitasking, die Vierte: Präemptives Tasking« wird schließlich ein vollständiger präemptiver Scheduler mit Prioritätssteuerung vorgestellt.

Die Ausführungen zu den verschiedenen Konzepten erfolgen anhand durchgehender Beispiele. Dabei wird zunächst immer die grundlegende Idee vorgestellt, die dann anhand von praktischen Beispielen konkretisiert und in einem anschließenden Blick auf die Details vertieft wird.

Die Beispielprogramme können unter www.buch.cd heruntergeladen werden. Eine Übersicht der verwendeten Softwaremodule und Programme findet sich in Anhang A.3.

Inhaltsverzeichnis

1 Multitasking, Kontext, Real-Time & Co.

1.1      Kontext

1.2      Zustand einer Task

1.3      Scheduler

1.3.1   Kooperatives Scheduling

1.3.2   Round Robin Scheduling

1.3.3   Präemptives Scheduling

1.4      Multitasking und Echtzeitfähigkeit

2 Experimentierumgebung

2.1      Hardware

2.2      Entwicklungsumgebung

2.3      Interface-Schaltung

3 AVR-Controller in C programmieren

3.1      Digitale Ein- und Ausgaben

3.2      Die serielle Schnittstelle nutzen

3.2.1   Die serielle Schnittstelle initialisieren

3.2.2   Daten senden und empfangen

3.2.3   Beispielprogramm: Zeichen vom Terminal lesen

3.3      Analoge Werte messen

3.3.1   Den Analog-Digital-Wandler initialisieren

3.3.2   Analoge Spannungswerte einlesen

3.3.3   Beispielprogramm: Messungen am Spannungsteiler

3.4      PWM

3.4.1   Timer2 als PWM konfigurieren

3.4.2   Beispielprogramm: Motordrehzahl einstellen

3.5      Interrupt

3.5.1   Timer-Interrupt: Systemuhr

3.5.2   Beispielprogramm: Uhrzeit und blinkende LED

3.5.3   Hardware-Interrupt

3.5.4   Beispielprogramm: Drehgeber auswerten

3.6      Datenflussdiagramm

4 Multitasking, die Erste: Die Minimalversion

4.1      Die Grundidee

4.2      Der praktische Einsatz

4.2.1   Drehzahlregler mit Tachogenerator und Hardware-PWM

4.3      Zusammenfassung

5 Multitasking, die Zweite: Ein Scheduler im Eigenbau

5.1      Die Grundidee

5.2      Modul RTC-Scheduler

5.3      Der praktische Einsatz

5.3.1   RTC-Drehzahlregler mit Tachogenerator

5.3.2   RTC-Drehzahlregler mit Drehgeber und Hardware-PWM

5.3.3   RTC-Drehzahlregler mit Drehgeber und Software-PWM

5.3.4   RTC-Drehzahlregler für zwei Motoren mit FIFO und Software-PWM

5.4      Ein Blick auf die Details

5.4.1   Task-Liste

5.4.2   Task-Synchronisation

5.4.3   FIFO

5.4.4   Scheduler

5.4.5   Speicherbedarf

5.5      Zusammenfassung

6 Multitasking, die Dritte: Kooperation ist gefragt

6.1      Die Grundidee: Adam Dunkels‘ Protothreads

6.2      Modul COS-Scheduler

6.3      Der praktische Einsatz

6.3.1   COS: Teste die Möglichkeiten

6.3.2   COS-Drehzahlregler mit Tachogenerator und Hardware-PWM

6.4      COS-Drehzahlregler mit Drehgeber und Hardware-PWM

6.4.1   COS-Drehzahlregler für zwei Motoren mit FIFO und Software-PWM

6.5      Ein Blick auf die Details

6.5.1   Protothreads: Makros machen‘s möglich

6.5.2   Task-Liste

6.5.3   Semaphoren

6.5.4   FIFO

6.5.5   Scheduler

6.5.6   Speicherbedarf

6.6      Zusammenfassung

7 Multitasking, die Vierte: Präemptives Tasking

7.1      Die Grundidee

7.1.1   Task-Kontext

7.1.2   Semaphoren: gemeinsam genutzte Ressourcen schützen

7.1.3   Ablaufinvariante Funktionen: »Reentrant Functions«

7.2      Modul RTOS-Scheduler

7.3      Der praktische Einsatz

7.3.1   RTOS: Teste die Möglichkeiten

7.3.2   RTOS-Drehzahlregler mit Tachogenerator und Hardware-PWM

7.3.3   RTOS-Drehzahlregler mit Drehgeber und Hardware-PWM

7.3.4   RTOS-Drehzahlregler für zwei Motoren mit Software-PWM

7.4      Ein Blick auf die Details

7.4.1   Task-Liste

7.4.2   Task-Kontext

7.4.3   Kontext retten und wiederherstellen

7.4.4   Initialer Kontext einer Task

7.4.5   Kontextwechsel

7.4.6   Semaphoren

7.4.7   Messagebox

7.4.8   Scheduler

7.4.9   Zeitverhalten

7.4.10   Speicherbedarf

7.5      Zusammenfassung

A Anhang

A.1 Interface-Schaltung für zwei Gleichstrommotoren mit Drehgeber

A.2 Pin-Belegung des benutzten Evaluation-Boards

A.3 Softwaremodule und Beispielprogramme

Literaturverzeichnis

Stichwortverzeichnis

1Multitasking, Kontext, Real-Time & Co.

Das Zielsystem in diesem Buch sind Mikrocontroller. Dennoch soll der Einstieg in das Multitasking an einem weitaus mächtigeren Rechnersystem betrachtet werden. Jeder PC-Benutzer kennt bereits mindestens ein Multitasking-System, denn das PC-Betriebssystem erlaubt die Ausführung mehrerer Programme nebeneinander.

Wenn ein Programm gestartet wird, entsteht als Laufzeitumgebung für dieses Programm ein sogenannter Prozess. Der physikalische Speicher wird vom Betriebssystem an die laufenden Prozesse vergeben, jedoch hat jeder Prozess einen eigenen virtuellen Adressraum, in dem das Programm läuft. Die verfügbare Rechenzeit verteilt das Betriebssystem mithilfe des Schedulers an die vorhandenen Prozesse.

Der separate virtuelle Adressraum sorgt dafür, dass jedes Programm isoliert von den anderen laufen kann, ohne dass sich Programme gegenseitig beeinflussen. Innerhalb dieses virtuellen Adressraums werden der Programmcode und alle Variablen des Programms gespeichert.

1.1Kontext

Wenn man sich von einem Programm eine Momentaufnahme vorstellt, erhält man dessen augenblicklichen Kontext mit den Werten aller Variablen, den Inhalten der CPU-Register, dem aktuell bearbeiteten Programmbefehl und der Liste der Rücksprungadressen aus den aufgerufenen Unterprogrammen.

Der Scheduler verteilt die CPU-Zeit auf die vorhandenen Programme. Ein »Kontextwechsel« wird eingeleitet, indem der Scheduler den aktuellen Programmkontext speichert, aus der Liste der lauffähigen Programme eines auswählt und dessen früher gespeicherten Kontext wiederherstellt. Dieser Wechsel verläuft schnell genug, sodass der Nutzer den Eindruck gewinnt, mehrere Programme liefen gleichzeitig, obwohl tatsächlich immer nur eines die CPU belegt. Nur wenn es mehrere CPUs oder mehrere CPU-Kerne gibt, kann es echte Parallelverarbeitung geben.

Das Umschalten des kompletten virtuellen Adressraums ist eine aufwendige Angelegenheit. Daher bietet das Betriebssystem mit den Threads oder Tasks (hier synonym verwendet) kleinere Einheiten an, deren Kontext sich deutlich schneller umschalten lässt. Ein Programm kann dabei mehrere Tasks besitzen, die alle im virtuellen Adressraum des Programms laufen. Dabei ist in einem C-Programm eine Task nichts anderes als eine Funktion, die auf besondere Weise gestartet wird. Da alle Tasks eines Programms im selben virtuellen Adressraum existieren, sind die globalen Vereinbarungen allen Tasks gemeinsam zugänglich. Beispielsweise können die Tasks auf die globalen Variablen des Programms zugreifen. Dadurch lässt sich eine Kommunikation zwischen Tasks verwirklichen.

Den Kontext einer Task bilden die Inhalte der benutzten CPU-Register, die aktuelle Position im Programmcode, die eigenen lokalen Variablen und die Liste der Rücksprungadressen aus den Unterprogrammen, die die Task aufgerufen hat.

Die Rücksprungadressen und die lokalen Variablen liegen auf dem Stack-Speicher, daher braucht jede Task einen eigenen Stack. Wenn eine Task eine C-Funktion aufruft, läuft diese im Kontext der Task, d. h., die lokalen Variablen der aufgerufenen Funktion liegen auf dem Stack der Task, die die Funktion aufgerufen hat. Eine Funktion kann ohne Weiteres von mehreren Tasks gleichzeitig aufgerufen werden, alle lokalen Variablen existieren dann mehrfach auf verschiedenen Stacks, ohne sich zu stören. (Ausnahme: static-Variablen einer Funktion existieren nur einmal, also greifen alle Tasks auf denselben Speicherplatz zu. Das kann spezielle Schutzmaßnahmen erforderlich machen.)

Vor dem Umschalten auf eine andere Task muss der Scheduler den Task-Kontext abspeichern: Alle CPU-Registerinhalte und die Adresse des nächsten auszuführenden Befehls sowie der private Stack der Task werden gerettet, damit später an derselben Stelle nahtlos weitergemacht werden kann. Der Kontextwechsel bei Tasks ist viel schneller durchführbar als bei Prozessen, da viel weniger Daten gesichert werden müssen.

Auf einem Mikrocontroller kann nur ein Programm zur gleichen Zeit laufen, das aber mehrere Tasks starten kann. Auch wird man ohne komplettes Betriebssystem auskommen. Die Verwaltung der Tasks und das Umschalten zwischen ihnen übernimmt ein Scheduler, der in das eigene Programm integriert wird.

Beispiele für verschiedene Arten solcher Scheduler finden sich in den Kapiteln »Multitasking, die Erste: Die Minimalversion« bis »Multitasking, die Vierte: Präemptives Tasking«.

1.2Zustand einer Task

Jede Task befindet sich in einem von mehreren möglichen Zuständen, die in der nachfolgenden Abbildung dargestellt sind. Die Task, deren Kontext gerade geladen ist, befindet sich im Zustand laufend. Existiert lediglich eine CPU mit nur einem Kern, kann auch nur eine Task gleichzeitig in diesem Zustand sein. Andere Tasks sind bereit, d. h., sie könnten zwar laufen, aber der Scheduler hat sie zurzeit nicht aktiviert. Soll ein Task-Wechsel stattfinden, muss der Scheduler die laufende Task deaktivieren, indem er ihren Kontext speichert und ihren Zustand auf bereit setzt. Aus der Menge der bereiten Tasks wählt er eine andere Task aus und aktiviert sie, indem er ihren Kontext lädt und ihren Zustand auf laufend setzt. Im folgenden Abschnitt »Scheduler« wird auf mögliche Kriterien eingegangen, nach denen der Scheduler seine Auswahl trifft.

Bild 1.1: Zustandsdiagramm einer Task.

Eine laufende Task kann blockieren, wenn sie auf ein Ereignis wartet, z. B. dass eine Wartezeit verstreicht. Einer blockierten Task wird der Scheduler sofort die CPU-Zeit entziehen. Ihr Kontext wird gespeichert, und sie geht in den Zustand blockiert über. Erst wenn das erwartete Ereignis eintrifft, z.B. die Wartezeit vorüber ist, wird die Task wieder lauffähig und darf sich in die Liste der bereiten Tasks einreihen. Zu welchem Zeitpunkt sie dann erneut läuft, entscheidet der Scheduler.

Der letzte Zustand, suspendiert, ist nicht zwingend nötig. Viele Systeme verfügen über ein spezielles Kommando suspend(Task X), um Task x vorübergehend vom Scheduling auszuschließen. Erst mit der Ausführung des Kommandos resume(Task X) wird Task x wieder als bereit markiert.

1.3Scheduler

Die Verteilung der verfügbaren Rechenzeit auf die verschiedenen Tasks kann nach ganz unterschiedlichen Kriterien erfolgen. Stellvertretend für alle anderen sollen hier drei einfache Scheduling-Prinzipien dargestellt werden.

1.3.1Kooperatives Scheduling

Bei diesem Verfahren bleibt die gerade laufende Task ohne Zeitbeschränkung aktiv. Ein Task-Wechsel findet nur statt, wenn die laufende Task blockiert, z.B. indem sie sich selbst für eine gewisse Zeit schlafen legt ('sleep(..)'). Damit alle Tasks Rechenzeit erhalten, müssen sich alle kooperativ verhalten und den anderen freiwillig CPU-Zeit einräumen. Der Scheduler entscheidet bei einem Kontextwechsel, welche Task als Nächstes läuft. Er hat aber keinen Einfluss darauf, wie viel Rechenzeit sie bekommen wird. Auf einen Scheduler nach diesem Prinzip wird im Kapitel 6 »Multitasking, die Dritte: Kooperation ist gefragt« im Detail eingegangen.

1.3.2Round Robin Scheduling

Hier wird die verfügbare Rechenzeit gleichmäßig auf alle Tasks verteilt, wie in der nächsten Abbildung dargestellt. Jede der existierenden Tasks (Te, Ta etc.) erhält eine gleich große Zeitscheibe, die Reihenfolge der Aktivierung ist von Anfang an festgelegt. Es gibt keine unterschiedlichen Prioritäten, alle werden gleichbehandelt.

Bild 1.2: »Round Robin Scheduling« – gleiche Zeitscheiben für alle.

Man kann einerseits eine Systemperiode TPer vorgeben, dann bekommt jede der n Tasks eine Zeitscheibe der Größe

Bei wachsendem n wird die Zeitscheibe T immer kürzer, und das System beschäftigt sich zunehmend mit den Kontextwechseln und immer weniger mit den eigentlichen Aufgaben der Tasks. Andererseits kann man die Größe der Zeitscheibe T vorgeben. Bei wachsender Task-Anzahl n dauert es dann immer länger, bis die einzelne Task wieder zum Zuge kommt.

1.3.3Präemptives Scheduling

Bei dieser Art Scheduling kann eine Task jederzeit unterbrochen werden, sobald der gerade laufende Maschinenbefehl abgearbeitet ist. Es gibt keine feste Task-Reihenfolge und keine konstanten Zeitscheiben. Stattdessen erhält jede Task eine Priorität zugeordnet. Aus der Menge der bereiten Tasks wird immer die mit der höchsten Priorität aktiviert. Sobald eine Task lauffähig wird, deren Priorität die der gerade laufenden übersteigt, findet sofort ein Kontextwechsel statt – die laufende Task wird verdrängt. Die Abbildung stellt das Prinzip für drei Tasks Ta, Tb, Tc dar.

Bild 1.3: Präemptives Scheduling: Aus den bereiten Tasks läuft die mit der höchsten Priorität.

Zum Zeitpunkt 0 sind Ta und Tc bereit, Tb ist blockiert. Die Priorität von Ta ist höher als die von Tc, daher aktiviert der Scheduler Ta. Zum Zeitpunkt 1 blockiert Ta, und aus der Menge der bereiten Tasks wird die mit der höchsten Priorität aktiviert, hier also Tc. Zum Zeitpunkt 2 wird Tb bereit und verdrängt aufgrund ihrer Priorität sofort Tc. Sobald Ta zum Zeitpunkt 3 lauffähig ist, verdrängt sie Tb.

1.4Multitasking und Echtzeitfähigkeit

Die bisher angesprochenen Scheduling-Verfahren sagen noch nichts über die Echtzeitfähigkeit eines Systems aus. Ein Multitasking-System verwaltet Tasks und teilt ihnen Rechenzeit zu, aber es wird zunächst keine Angabe über die Verzögerungszeit bei der Aktivierung von Tasks gemacht. Ein Echtzeitsystem muss in der Lage sein, bestimmte Anforderungen innerhalb einer maximalen Zeit zu bearbeiten. Es ist z.B. zu garantieren, dass eine hochpriorisierte Task spätestens 10 ms, nachdem sie lauffähig wurde, auch tatsächlich aktiviert wird.

Das Eintreten eines Ereignisses, z.B. der Tritt auf das Bremspedal eines Fahrzeugs, soll eine Task aktivieren, z.B. einen Teil des Bremsassistenten im Fahrzeug. Die folgende Abbildung zeigt die Latenzzeit zwischen dem Tritt auf das Pedal und der Aktivierung der Task, also dem Beginn des Bremsvorgangs. Ein echtzeitfähiges System kann garantieren, dass die Latenzzeit immer kleiner sein wird als eine bestimmte Maximalzeit. Wie groß diese Maximalzeit sein darf, hängt dabei allein von der Anwendung ab. Ein nicht echtzeitfähiges Multitasking-System wird die Task auch aktivieren, sich aber nicht festlegen, wann die Bremse spätestens aktiv wird.

Neben der Latenzzeit ist auch die Antwortzeit bis zur vollständigen Bearbeitung der Task von großer Bedeutung. Die »Zeitschranke« einer Task ist die maximal tolerierbare Antwortzeit, bestehend aus Latenzzeit plus Bearbeitungszeit. Wenn eine Task ihre Zeitschranke immer einhalten kann, spricht man von Rechtzeitigkeit, halten alle Tasks ihre Zeitschranken ein, erreicht man Gleichzeitigkeit.

Bild 1.4: Ein Echtzeitsystem hält vorgegebene Zeitschranken ein.

Die in diesem Buch in den Kapiteln 4 »Multitasking, die Erste: Die Minimalversion« bis 6 »Multitasking, die Dritte: Kooperation ist gefragt« vorgestellten Scheduler sind nicht echtzeitfähig. Ihre maximalen Latenzzeiten hängen vom Anwenderprogramm ab. Mit diesen Schedulern lassen sich Echtzeitanforderungen nur begrenzt über Hardware-Interrupts realisieren. Im Kapitel 7 »Multitasking, die Vierte: Präemptives Tasking« kommt dagegen ein echtzeitfähiger Scheduler zum Einsatz, dessen Latenzzeit durch die Systemuhr vorgegeben werden kann.

2Experimentierumgebung

Für die Bearbeitung der angeführten Beispiele werden ein AVR-Experimentierboard und eine Entwicklungsumgebung für die Programmiersprache C benötigt. Als Prozessor wird durchgehend der ATmega8 gewählt. Diese CPU ist im DIL-Gehäuse verfügbar und daher für eigene Hardwareentwürfe einfach zu handhaben. Ein Umstieg auf leistungsstärkere AVRs ist ohne Probleme möglich. Um den Einstieg zu erleichtern, kann man auf eines der zahlreich verfügbaren Experimentierboards zurückgreifen, etwa auf das bewährte STK500 und seine Nachfolger oder auf einen Bausatz wie etwa das Atmel Evaluation-Board der Firma Pollin.

2.1Hardware

Die Programmbeispiele in diesem Buch wurden auf einem »Atmel Evaluation-Board Version 2.0.1« der Firma Pollin entwickelt und getestet (siehe Abbildung). Das Board kann mit den Prozessoren ATtiny2323, ATmega8, ATmega16, ATmega32, ATmega8535 oder ATmega644 bestückt werden. Alle Programme wurden mit einem ATmega8 mit 16 MHz Takt entwickelt. Diese Wahl soll zeigen, dass auch eine relativ kleine CPU für Multitasking-Programme tauglich ist. Darüber hinaus ist es leichter, eine Software auf einen leistungsstärkeren Prozessor zu portieren, als umgekehrt.

Bild 2.1: Atmel Evaluation-Board Version 2.0.1 von Pollin.

Das Experimentierboard verfügt über eine RS232-Schnittstelle, über die man mit einem anderen Rechner kommunizieren kann. Die Programmierung des Prozessors erfolgt über die ISP-Schnittstelle. Die ISP-Schnittstelle kann über die einfache Bit-Banging-Schaltung des Experimentierboards an eine serielle Schnittstelle des PCs angeschlossen werden. Man sollte aber lieber ein externes USB-Programmiergerät benutzen, wie z.B. mySmartUSB MK3. Diese Programmierschnittstelle funktioniert schneller und zuverlässiger.

Bild 2.2: AVR Highspeed-USB-Programmer.

2.2Entwicklungsumgebung

Für die Softwareentwicklung steht der freie C-Compiler AVR-GCC (GNU Compiler Collection) zur Verfügung – WinAVR (http://sourceforge.net/projects/winavr). Dieser Compiler kann in die kostenlos von Atmel bereitgestellte Entwicklungsumgebung AVR-Studio integriert werden. Er arbeitet aber auch sehr gut mit anderen freien Tools zusammen, wie z.B. CodeBlocks, das vom Autor genutzt wird (http://www.codeblocks.org).

Das fertige Programm lässt sich dann über eine Programmiersoftware auf den ATmega8 laden. Benutzt man den Bit-Banging-Programmer des Pollin-Boards, kann man dazu das freie Tool PonyProg verwenden. Das USB-Programmiergerät wird mit eigener Programmiersoftware geliefert.

2.3Interface-Schaltung

In diesem Buch werden Programme vorgestellt, die externe Hardwarekomponenten verwenden. Dazu benötigt man eine Interface-Schaltung (siehe Anhang, Abschnitt »Interface-Schaltung für zwei Gleichstrommotoren mit Drehgeber«), die z.B. Steuersignale für Gleichstrommotoren und Lichtschranken ausgeben sowie verschiedene Analogspannungen einlesen kann. Die jeweils benötigten Teile dieser Schaltung werden dort angegeben, wo sie gebraucht werden. Im eigenen Projekt werden die verwendeten Sensoren und Aktoren vermutlich ohnehin anders sein als in den Beispielen, sodass man nicht die komplette Interface-Schaltung nachbauen muss.

3AVR-Controller in C programmieren

Bei der Programmierung der AVR-Controller in C sind einige Besonderheiten gegenüber einem PC-Programm zu beachten. Zum einen gibt es in der Regel keine Standardeingabe und -ausgabe, d.h., Funktionen wie printf() und scanf() entfallen. Zum anderen will man die digitalen Ein-/Ausgabesignale (Ports) des Prozessors nutzen, um externe Hardware anzusteuern und auszulesen.

3.1Digitale Ein- und Ausgaben

Die AVR-Controller besitzen verschiedene 8 Bit breite I/O-Register (Ports), auf denen digitale Signale ausgegeben und eingelesen werden können. Die Port-Pins lassen sich unabhängig voneinander benutzen. Dazu muss man zuvor für jeden Pin die Datenrichtung angeben, indem man das zugeordnete Data Direction Register entsprechend setzt.

Direkt nach dem Einschalten der Betriebsspannung sind alle Register des Prozessors gelöscht, d.h., sie enthalten den Wert 0. Wird ein Bit im Data Direction Register auf 1 gesetzt, ist damit der entsprechende Port-Pin als Ausgang konfiguriert.

Der ATmega8 besitzt die Ports B, C und D, deren Pins meist mehrere Funktionen haben. Im folgenden Beispielprogramm wird ein Taster ausgelesen, der mit Port D, Pin 4 (kurz: pd4) verbunden ist. Bei gedrücktem Taster wird eine LED eingeschaltet, die an Port D, Pin 5 (pd5) angeschlossen ist. Die restlichen I/O-Pins bleiben ungenutzt, es ist also nur das Data Direction Register für Port D, Pin 5 auf 1 (DDRD |= (1<<5)) zu setzen (siehe Funktion _initPortDirections()).

Listing Anfang

/**************************************************

Beispiel ATmega8 Port I/O:

Taster an pd4 lesen, LED an pd5 ansteuern

***************************************************/

#include <avr/io.h>

#define AN      1

#define AUS    0

#define set_bit(sfr,bit)    sfr |= (1<<(bit))

#define clear_bit(sfr,bit)  sfr &= ~(1<<(bit))

/*--------------------------------------------------*/

// function prototypes

/*--------------------------------------------------*/

void    _initPortDirections(void);

uint8_t _Taster_T3_pressed(void);

void    _setLED1(uint8_t val);

/*--------------------------------------------------*/

int main(void)

{   _initPortDirections();

    while (1)  // Endlosschleife, Atmega kehrt nie zurueck...

{   if(_Taster_T3_pressed())

              _setLED1(AN);

            else

              _setLED1(AUS);

}

    return 0;

}

/*!

 **************************************************************

 * @par Beschreibung:

 *   PORT-Richtungen einstellen

 *************************************************************/

void _initPortDirections(void)

{   DDRB = 0; /* Port B: alle Pins auf INPUT */

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!

Lesen Sie weiter in der vollständigen Ausgabe!