You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Wie ist das mit unseren Programmen bis jetzt gewesen? (Bitte überprüfen Sie es!)
Vektor Tabelle ist in LPC2300.s definiert.
Default Handler sind Endlosschleifen in Assembler!
Welche Interrupts sind auf dem Board durch Taster auslösbar?
=> RESET + INT0 (EINT0, External Interrupt Input 0)
Kann INT0 vielleicht auch einen anderen Interrupt auslösen? Wenn JA, welchen und wie? (Bitte um kurze Erklärung)
=> INT0 Taster ist an P2.10;
P2.10 kann auch als GPIO Funktion konfiguriert sein;
Verwendung von GPIO Interrupt Feature (Falling Edge IRQ);
Bei einem Interrupt müssen somit die GPIO Peripherie Register geprüft werden um die IRQ Quelle zu ermitteln!
Welche Register müssen zum Retten des Prozessor Kontextes beim LPC2378 gerettet werden?
Beim Entwurf eines Gerätetreibers soll der Fokus immer darauf liegen die Hardware komplett zu kapseln.
Dies ist gleichbedeutend mit einer Abstraktion der Hardware.
Der Gerätetreiber soll das einzige Software Modul sein, das mit der Hardware interagiert. Nur der Treiber soll mit den Steuer und Status Registern der Peripherie
interagieren.
Zudem, sollte die Peripherie auch Interrupts auslösen, so ist die Interrupt Service Routine auch Teil des Gerätetreibers.
Der Gerätetreiber stellt dann der übergeordneten Applikation ein generisches Interface (API) zum Zugriff auf das Gerät und dessen Funktionen zur Verfügung.
Es ist selten möglich dass ein Gerätetreiber die Hardware komplett versteckt. Besonders dann wenn spezielle Funktionen der Peripherie, die inhärent mit der Hardware einhergehen, der Applikation bereitgestellt werden sollen.
Wichtig ist aber dass in solchen Fällen ein Wechsel von einer Peripherie zu einer gleichartigen, keine Auswirkungen auf die Applikation hat, sondern lediglich Änderungen im Hardware Treiber notwendig macht.
Beispiel: Wechsel von einem Flash Speicher zu einem anderen. Es werden sich die Sektoren und die Chip Kommandos ändern, aber die übergeordnete Schnittstelle sollte gleich bleiben.
Die Vorteile eines guten Gerätetreiber Entwurfs
Aufgrund der Modularität ist die Gesamtstruktur der Software einfacher zu verstehen. Diese Strukturierung erlaubt es auch einfacher Änderungen und Erweiterungen in der Software durchzuführen auch in bereits ausgelieferten Systemen.
Weil bei einem solchen Gerätetreiber die Peripherie Interaktion auf den Treiber begrenzt ist, kann der Zustand der Peripherie-Hardware einfacher und genauer verfolgt werden.
Software Änderungen aufgrund von Hardware Anpassungen begrenzen sich auf den Gerätetreiber. Dies macht die Software leichter portierbar.
Jeder dieser Vorteile wird die Anzahl der Fehler im Code reduzieren und die Wiederverwendung erleichtern. Dazu muss aber zu beginn etwas mehr Aufwand investiert werden.
Der Gerätetreiber ist direkt oberhalb der Hardware und beinhaltet das Wissen über den Betrieb eines bestimmten Stücks Peripherie Hardware.
Diese folgenden fünf Schritte beschreiben eine einfache und inkrementelle Implementierungsstrategie von Gerätetreibern:
1. Definition / Erzeugung eines Interfaces zu den Steuer und Status Registern
Bei memory-mapped Peripherie beginnt die Treiberentwicklung mit der Definition und Implementierung der Registerbeschreibung der Peripherie.
Das erfordert für Gewöhnlich dass der Entwickler sich mit der Peripherie vertraut macht indem er die entsprechenden Kapitel im Mikrocontroller Handbuch studiert!
2. Definition von Variablen zum Verfolgen des aktuellen Zustands der physikalischen (und logischen) Geräte
Der zweite Schritt bei der Treiberentwicklung ist die Definition welche Zustandsvariablen notwendig sind um das Gerät zu bedienen. Beispiel: Variablen die eine erfolgreiche Initialisierung speichern.
Manche Gerätetreiber erzeugen mehr als ein logisches Gerät zur Interaktion mit der Hardware. Ein Beispiel ist die Bereitstellung von beliebig vielen Timern welche in SW implementiert werden und auf einen einzigen Hardware Tick Timer zurückgreifen. Solche virtuellen, logischen Geräte brauchen interne Zustandsvariablen die hier definiert werden.
3. Definition von Funktionen zur Initialisierung der Hardware (d.h. in einen definierten Zustand bringen)
Hier wird zum ersten Mal eine Funktion definiert die mit der Hardware interagiert.
Für gewöhnlich startet man mit der Initialisierungs-Routine der Hardware.
4. Definition einer API für Anwender des Gerätetreibers
Der erste Schritt hier wird die Definition der Namen und Funktionen der unterschiedlichen Routinen sein. Dazu gehört auch die Definition der Parameter und Antwortwerte der Funktionen.
Anschließend muss noch ein Test aller API Funktionen implementiert werden.
5. Umsetzung der Interrupt Service Routine
Es ist empfehlenswert alle Treiberroutinen eingehend zu testen bevor man mit der Umsetzung der Interrupt- Steuerung beginnt.
Das Debuggen von Interrupt-Steuerungen ist um einiges komplexer als das Debuggen von gewöhnlichen Ablaufsteuerungen.
Um die Entwicklung zu vereinfachen sollte zuvor im Polling- Modus die grundsätzliche Interaktion mit dem Peripheriegerät überprüft werden, bevor eine Interrupt- basierte Umsetzung aktiviert wird.
UART Treiber
meh, Vorgehen wie oben
siehe Folien, Peripherie p.30-55
##Wichtige Erkenntnisse
Wichtige Punkte beim Interagieren mit Peripherie Registern
Lesen / Schreiben an die selbe Adresse kann zu zugriffen auf unterschiedliche Register der HW führen (RBH und THR)
Durch den Zustand von anderen Registern können sich wiederum das Verhalten bei Zugriffen ändern.
Einige Register „merken“ sich einen Zugriff und Triggern dadurch interne Vorgänge.
Beispiel: Lesen des Receive Buffer Registers (UxRBR) „löscht“ das empfangene Byte aus dem Receive Buffer und ersetzt es mit einem neuen empfangenen Byte.
=> ACHTUNG beim Debuggen und „betrachten“ von solchen Registern können unerwünschte Lücken beim Empfang entstehen!
Weitere Beispiele sind Status Register die sich automatisch zurücksetzen beim Lesen etz.
Example:
###LED driver
/* Function that initializes LEDs */voidLED_Init(void) {
PINSEL10=0; /* Disable ETM interface, enable LEDs */PINSEL4=0; /* Set GPIO function for P2 */FIO2DIR=0x000000FF; /* P2.0..7 defined as Outputs */FIO2MASK=0x00000000;
}
/* Function that turns on requested LED */voidLED_On (unsigned intnum) {
FIO2SET= (1 << num);
}
/* Function that turns off requested LED */voidLED_Off (unsigned intnum) {
FIO2CLR= (1 << num);
}
Misc
/* Enable and setup timer interrupt, start timer */T0MR0=11999; /* 1msec = 12000-1 at 12.0 MHz */T0MCR=3; /* Interrupt and Reset on MR0 */T0TCR=1; /* Timer0 Enable */VICVectAddr4= (unsigned long)T0_IRQHandler;/* Set Interrupt Vector */VICVectCntl4=15; /* use it for Timer0 Interrupt */VICIntEnable= (1 << 4); /* Enable Timer0 Interrupt *//* Power enable, Setup pin, enable and setup AD converter interrupt */PCONP |= (1 << 12); /* Enable power to AD block */PINSEL1=0x4000; /* AD0.0 pin function select */AD0INTEN= (1 << 0); /* CH0 enable interrupt */AD0CR=0x00200301; /* Power up, PCLK/4, sel AD0.0 */VICVectAddr18= (unsigned long)ADC_IRQHandler;/* Set Interrupt Vector */VICVectCntl18=14; /* use it for ADC Interrupt */VICIntEnable= (1 << 18); /* Enable ADC Interrupt *//* Taster */FIO2DIR &= ~(1 << 10); //set p2.10 as inputPINSEL4 &= ~(3 << 20); //GPIOIO2_INT_EN_R |= (1 << 10); //GPIO Interrupt Enable for Rising edgeVICVectAddr17= (unsigned long)TASTER_IRQHandler;/* Set Interrupt Vector */VICVectCntl17=13; /* use it for ADC Interrupt */VICIntEnable= (1 << 17); /* Enable ADC Interrupt */
Mikroprozessor ABER mit internen Speicher (RAM/ROM) und zusätzlicher Peripherie (on-chip)
speziell für Eingebettete Systeme entwickelt
Kostenreduktion aufgrund der Integration in einem Chip
Digitaler Signal Prozessor (DSP)
Optimiert für die Verarbeitung von Diskreten Digitalen Signalen (z.B. digitale Filter, De-Encodierung von Audio / Video)
Heute sind in vielen modernen CPUs Peripheriekomponenten mit DSP Funktionen enthalten
Audio/Video-Codierung
Kryptographie
Memory Map (allgemein)
Die Memory Map beschreibt die Aufteilung des Adressbereiches den der Prozessor ansteuern kann.
Sie ist sehr wichtig für die Programmierung und stellt einen wichtigen Teil der Dokumentation dar.
Kann sich in unterschiedlichen Betriebsmodi des Systems verändern (z.B. Startup, Betrieb, Shutdown)
Bank 0 (CS0) 0x8000 0000 .. 0x8000 FFFF
Bank 1 (CS1) 0x8100 0000 .. 0x8100 FFFF
Polling vs. Interrupt
Polling
ist empfehlenswert wenn der Prozessor besonders schnell auf Ereignisse reagieren soll oder wenn große Daten Mengen zu genau definierten Zeitpunkten übertragen werden sollen.
Interrupts
ist empfehlenswert wenn Effizienz sehr wichtig ist und mehrere parallele Peripherien quasi gleichzeitig bedient werden sollen. Die Software ist hierbei einfacher und klarer strukturiert. Der Code der die jeweilige Peripherie betrifft ist in einer ISR gekapselt.
Startup
Reset Code
gewöhnlich Assembler Code
Reset Code wird als erstes nach einem Reset ausgeführt.
umfasst gewöhnlich nur einige Befehle.
Ziel: so schnell wie möglich in die HW-Initialisierung springen
erste Zeile muss an bestimmter Adresse im Speicher liegen
in HW Reset Vektor ( i.d.R. 0x00000000)
Hardware Initialisierung
gewöhnlich Assembler Code.
In der Hardware Initialisierung wird die erste Mikrocontroller Initialisierung durchgeführt
z.B. System Takt wird konfiguriert
initialisiert Interrupt Controller und andere kritische Peripherie
Weniger kritische Peripherie wird erst später initialisiert (im entsprechenden Treiber)
Weitere Aktionen:
Initialisierung der Memory Management Unit zur Speicherverwaltung (nicht beim LPC2378).
Initialisierung des Speicher Interfaces zum Zugriff auf möglichen externen Speicher (nicht beim MCB2300).
Startup Code zur Ausführung von High-Level Code
gewöhnlich Assembler Code
notwendige Initialisierungen des Frameworks einer High Level Umgebung
Dazu gehören für die C-Programmiersprache
Initialisierung von Standard Bibliotheken
Heap Initialisierung (notwendig für die dynamische Speicherverwaltung)
Stack Initialisierung (notwendig für C Funktionsaufrufe)
nach der Initialisierung des Stacks wird für gewöhnlich MAIN() angesprungen, das erste C-Programm
Heap
Speicherbereich der zur Umsetzung einer dynamische Speicherverwaltung angelegt wird
Dynamische Speicherverwaltung in C
malloc, calloc, free, …
Dynamische Speicherverwaltung in C++
new, …
Stack
RAM Speicher den der Prozessor zur Verwaltung von temporären Variablen zur Laufzeit nutzt
Funktionsaufrufe: lokale Variablen und Rücksprungadressen (Return Adresse)
LIFO Prinzip
Allgemein: Pin Konfiguration von Mikrocontrollern
Mikrocontroller sind Prozessoren, die mit einer Vielzahl an Peripherie Komponenten in einem Chip vereint sind.
Ziel ist es
flexible und kosteneffiziente Lösungen anzubieten
vielseitig einsetzbar
Eine knappe Ressource sind dabei die Pins, d.h. die Anbindung nach außen.
Zur Steigerung der Flexibilität können Pins unterschiedliche Funktionen realisieren, d.h. sie können intern an unterschiedliche Peripherie Komponenten angeschlossen werden.
Diese Anbindung erfolgt über eine Konfiguration des Mikrocontrollers
Chapter 10: LPC23XX General Purpose Input/Output ports (GPIO)
Ein Interrupt ist eine "kurzzeitige" Unterbrechung der gewöhnlichen Programabarbeitung.
Zum Beispiel zur Bearbeitung von Ein/Ausgabe Ereignissen (Events) der Peripherie.
Beispiele von Interrupts
Daten wurden empfangen und stehen zur Weiterverarbeitung bereit (z.B. UART).
Ein Schalter / Taster wurde betätigt und erfordert eine Verarbeitung (z.B. Endschalter eines Linearmotors).
Ein bestimmtes Zeitintervall ist abgelaufen (z.B. Timer).
Interrupts erlauben es zeitkritische Bearbeitungen von dem Hauptprogramm zu trennen und dafür zu sorgen dass diese in einer priorisierten Weise abgearbeitet werden.
Es gibt generell folgende Arten von Unterbrechungen die allgemein definiert werden können
Exception (Ausnahmen): eine Fehlerbedingung wird erkannt und ein sogenannter Software Interrupt wird ausgelöst. Beispiel ist die Division durch 0.
Exceptions werden z.B. auch als SW Breakpoints verwendet. Exceptions sind synchrone Ereignisse.
Interrupt: ein asynchrones elektrisches Signal das von einer Peripherie gesetzt wird um dem Prozessor einen Bearbeitungswunsch mitzuteilen.
Trap (Falle): Unterbrechung der Bearbeitung ausgelöst durch die Prozessor interne Hardware. Traps sind synchrone Ereignisse.
Asynchrone Ereignisse haben keine zeitliche Beziehung zu allen anderen Ereignissen im inneren des Prozessors.
Synchrone Ereignisse geschehen aufgrund von andren Ereignissen im inneren des Prozessors (z.B. Division durch 0).
Da Interrupts asynchrone Ereignisse sind, können sie zu jeder Zeit im Hauptprogramm auftreten.
Die Architektur der Interrupt Hardware ist Prozessor und Mikrocontroller spezifisch.
Nachfolgend werden vorerst allgemein gültige Aspekte betrachtet und dann auf den LPC2378 näher eingegangen.
Anbindung an Peripherie
Der Mikrocontroller wird direkt mit den Interrupt Request Leitungen der Peripherie verbunden. Er besitzt intern einen on-chip Interrupt Controller.
Der Interrupt Controller bündelt mehrere Input Interrupts zu einen Output Interrupt.
Erlaubt die individuelle Kontrolle über die zusammengefassten Interrupt Leitungen
Maskieren (enable / disable)
Priorisieren
Anzeige aktuell aktiven Interrupts
Oftmals werden mehr Interrupts benötigt als zur Verfügung stehen. In solchen Fällen werden einem Interrupt Request mehrere Quellen zugeordnet und die SW hat dann die Aufgabe zu ermitteln wer denn aktuell den Interrupt ausgelöst hat.
Kategorien
Maskierbare (maskeable)
Können per Software ein oder ausgeschalten werden.
Z.B. über die Konfiguration im Interrupt Controller.
Nicht maskierbare (nonmaskable, NMI)
Sind System kritische Interrupts die nicht per Software ausgeschalten werden können.
Beispiele sind: Spannungsversorgungs-Interrupt oder Reset.
Prioritäten
Da Interrupts asynchrone Ereignisse sind, muss es einen Weg geben für den Prozessor zu entscheiden welchen der anliegenden Interrupts er zuerst bearbeitet (für den Fall dass mehrere gleichzeitig aufgetreten sind).
Der Prozessor definiert dafür Interrupt Prioritäten für alle Interrupts und Exceptions die er unterstützt.
Interrupt Prioritäten findet man im Prozessor Handbuch.
ARM Prozessor definiert acht unterschiedliche Interrupt und Exception Typen
Reset (highest)
Data Abort (Prefetch / Data)
FIQ
IRQ
Prefetch Abort
Undefined Instruction
SWI (lowest)
Typischer weise schaltet der Prozessor, beim Auftreten eines Interrupts die aktuelle und alle niederen Prioritäten aus.
Prioritätsbedingte Abarbeitung von Interrupts
Die Prioritätsbedingte Abarbeitung von Interrupts ist Prozessorabhängig kann aber wie folgt aussehen.
Fall 1: Verschachtelung von Interrupts (Interrupt-Nesting)
Tritt ein Interrupt mit hoher Priorität ein wenn gerade einer mit niederer Priorität bearbeitet wird, so wird dieser unterbrochen.
Die Interrupt Service Routine mit höherer Priorität wird bearbeitet.
Anschließend wir die Routine mit niederer Priorität weiterbearbeitet.
Fall 2
Tritt ein Interrupt mit niederer Priorität ein während einer mit hoher Priorität bearbeitet wird.
So wird die Interrupt Anfrage mit niederer Priorität (IRQ) im Interrupt Controller zwischengespeichert.
Erst bei Beendigung der Interrupt Service Routine mit hoher Priorität wird die IRQ mit niederer Priorität bearbeitet.
Fall 3
Tritt bei der Bearbeitung eines Interrupts ein erneute Anforderung mit der selben Priorität auf, so gibt es zwei Möglichkeiten:
Möglichkeit A:
Die aktuelle Interrupt Service Routine wird zu ende bearbeitet.
Im Anschluss wird der neue Interrupt bearbeitet.
Möglichkeit B:
Die aktuelle Interrupt Service Routine wird unterbrochen um mit der anderen Bearbeitung zu benignen.
Dies ist nur möglich wenn die aktuelle Interrupt Service Routine ihre Interrupt Priorität wieder aktiviert. Nur so kann sie von Interrupt Service Anfragen der selben Priorität unterbrochen werden.
ACHTUNG
Beim Verschachteln von Interrupts, muss dafür Sorge getragen werden, dass die jeweiligen Stacks der einzelnen Interrupts entsprechend Speicher für das Ablegen der Register Zustände haben.
Diese werden beim Unterbrechen durch eine Interrupt Service Routine mit höherer Priorität nämlich dort abgelegt.
=> Gefahr eines Stack Überlaufes!!
Zuordnung der Prioritäten
Die Interrupt Prioritäten werden durch die Hardware Schaltung, die Software oder eine Kombination aus beiden zugeordnet.
Nehmen wir an der Prozessor behandelt INT0 als Interrupt mit der höchsten Priorität und alle anderen abfallend bis zu INT3 mit der niedersten Priorität.
Der HW Designer muss in diesem Fall über die Verdrahtung dafür sorgen, dass die Peripherie mit der höchsten Priorität an INT0 angeschlossen wird.
Manche Interrupt Controller erlauben es die Priorität der Interrupts über die Software zu konfigurieren. Dafür hat der Interrupt Controller interne Register mit denen man die Interrupt Priorität der Peripherie definiert.
Pegel oder Flanken Trigger
Pegel Interrupts
Der Mikrocontroller reagiert solange auf den Interrupt solange das Interrupt Signal auf dem definierten Pegel steht.
Diese Interrupts reagieren entweder auf hohen oder auf den niederen Pegel.
Aktivierung
/ \
v v
high +-------+
| |
| |
low ----+ +----
Eigenheiten
Lösen einen Interrupt aus solange der Pegel anliegt.
Viele Peripheriekomponenten setzen ihr Interrupt Signal erst zurück wenn eine Bearbeitung durch den Prozessor quittiert wurde.
Flanken Interrupts
Der Interrupt wird bei einer definierten Signal Transition ausgelöst.
Es wird entweder auf steigende oder fallende Flanken oder beides reagiert.
Der "Peak" muss eine bestimmte Zeit anliegen z.B. 2..3 Prozessor Zyklus Zeiten, dann wird er erst als Flanke erkannt.
Aktivierung
|
V
high +-------+
| |
| |
low ----+ +----
Eigenheiten
können verloren gehen wenn bis zum Zeitpunkt der Bearbeitung des ersten IRQs erneut Interrupts ausgelöst wird.
Interrupt Quittierung
Als Quittierung verseht man wenn der Interrupt Quelle (z.B. Peripherie) mitgeteilt wird,
dass der Prozessor den Interrupt wahrgenommen hat und
dessen Bearbeitung in die Liste aufgenommen wurde.
Wie ein Interrupt Quittiert wird hängt stark von der Quelle ab.
Es kann das einfache Lesen des Interrupt Control Registers sein
oder das Löschen des aktuellen Interrupt Pending Bits.
Wenn der Interrupt quittiert ist wird die Peripherie das Interrupt Signal wieder zurück nehmen.
Manche Prozessoren haben ein Interrupt Acknowledge Signal, das diese Aufgabe automatisch übernimmt.
Aktivieren und Deaktivieren von Interrupts
Es gibt Interrupts die können Maskiert werden, andere nicht.
Auf Prozessor ebene (ARM7TDMI) können Interrupts global ein/ausgeschalten werden.
Weitere Maskierung erfolgt im Interrupt Controller
siehe Fig. 22 in Mikrocontroller Manual (Chapter 6)
Mikrocontroller und die Interrupt Verarbeitung
Bei einem Reset werden alle Interrupts ausgeschalten.
Es ist die Aufgabe des Startup Codes global die Interrupts wieder zu aktivieren, wenn der Prozessor bereit dazu ist.
Ein Teil der Arbeit, zur Sicherstellung dass der Prozessor bereit für Interrupts ist, beinhaltet das „Installieren“ der Software zum Bearbeiten von Interrupt Anforderungen.
D.h. jedem Interrupt ist eine Interrupt Service Routine zugeordnet, die die Basis-Funktion zum Handhaben der zugeordneten Interrupt Quelle beinhaltet.
Damit der Prozessor die richtige ISR ausführt muss eine Zuordnung zwischen Interrupt Quelle und ISR Funktion existieren.
Dieses Mapping wird in einer Interrupt Vektor Tabelle realisiert.
Die Vektor Tabelle ist im Speicher an einer ganz bestimmten Stelle angeordnet, welche der Hardware bekannt ist.
Der Prozessor nutzt die Interrupt Nummer (eine eindeutige Nummer für jeden Interrupt) als Index in der Tabelle.
Bei manchen Prozessoren ist der Eintrag in die Vektor Tabelle die eigentliche Adresse der ISR.
Bei anderen Prozessoren sind es Befehle, die oftmals als „Trampolin“ verwendet werden um in die ISR zu springen.
Beim ARM ist die Interrupt Vektor Tabelle an einer festen Adresse im Speicher.
Interrupt Service Routine
Die Interrupt Service Routine (ISR ) ist eine Funktion die beim Auftreten eines bestimmten Interrupts aufgerufen wird .
Ihre zentrale Funktion ist den Interrupt Request (IRQ) zu bearbeiten und dann die Kontrolle wieder dem Hauptprogramm zurück zu geben.
ISRs bekommen für gewöhnlich keinen Parameter übergeben und können auch keinen Return Wert zurückgeben.
Es ist empfehlenswert ISRs so kurz wie möglich zu halten um ihren Einfluss auf die gesamte Software zu minimieren.
Falls erforderlich sollten längere Bearbeitungen außerhalb der ISR durchgeführt werden.
Kurze ISRs erleichtern auch das Debuggen.
Wenn Bearbeitungen außerhalb einer ISR weitergeführt werden, dann spricht man von Deferred Service Routinen (DSR) oder auch High Level Service Routinen (HSR)
Unabhängig von den spezifischen Dingen die eine ISR ausführen muss ist sie verantwortlich für folgende grundsätzlichen Dinge
Retten des Prozessor Kontexts
Da die ISR und das Hauptprogramm die „selben“ Prozessorregister verwenden muss die ISR diese vor der eigentlichen Verarbeitung retten.
Der Prozessor Kontext besteht aus
Programm Counter (PC)
„alle“ Register
„alle“ Flags
Einige Prozessoren führen diese Aktion eigenständig / automatisch aus.
Interrupt Quittierung
Die ISR muss den IRQ zurücksetzen.
Das wird entweder im
Peripherie Baustein, der den IRQ ausgelöst hat, oder im
Interrupt Controller oder in
beiden gemacht.
Zurückholen des Prozessor Kontexts
Bevor mit dem Hauptprogramm weiter gemacht werden kann muss kurz vor dem Rücksprung noch der zuvor gerettete Kontext wieder hergestellt werden.
Einige Prozessoren erledigen das automatisch.
Bei nur einer IRQ Leitung
Manche Mikrocontroller unterstützen nur einen Interrupt (z.B. Microchip PICs)
Hier muss die ISR alle möglichen Quellen überprüfen.
Tipp:
Die Prüfung sollte nach Auftritts-Häufigkeit sortiert werden.
D.h. IRQ Quellen mit hoher Häufigkeit sollten als erstes geprüft werden.
Software Fluss bei einem Interrupt
Der Prozessor bearbeitet gerade das Main Programm.
Beim Eintreten eines Interrupt Request (IRQ), beendet er den aktuellen Befehl (Assembler Befehl).
Dann prüft der Prozessor welche Interrupt Service Routine (ISR) angesprungen werden soll (aus der Interrupt Vektor Tabelle) und „springt“ dort hin.
In der ISR wird als erstes der Prozessor Kontext auf dem Stack gerettet.
Die ISR bearbeitet dann den IRQ und setzt in der Peripherie die Interrupt Anforderung zurück.
Am Ende der ISR wird der Kontext wieder hergestellt und zurück ins Main Programm gesprungen.
Interrupt Latenzzeit
Ein wichtiger Parameter, besonders bei Echtzeit Systemen ist die Interrupt Latenzzeit.
Sie definiert die maximale Zeit die verstreicht vom Eintreffen einer Interrupt Anforderung bis zur Ausführung der zugeordneten Interrupt Service Routine.
Das Abschalten / Maskieren von Interrupts vergrößert die Interrupt Latenzzeiten, da diese die Zeit zwischen dem Ausschalgen und dem erneuten Einschalten beinhaltet.
Ein IRQ für mehr Interrupt Quellen
Oft ist eine Interrupt Anforderung (IRQ) und somit auch eine Interrupt Service Routine für mehrere Interrupt Quellen verantwortlich.
In solchen Fällen muss die ISR dafür sorgen die richtige Interrupt Quelle zu ermitteln und die geeignete Bearbeitung dafür durch zu führen.
__irqvoidinterruptServiceRoutine(void) {
uint32_tintStatus;
/* get IRQ source */intStatus=*pIntStatusReg;
if(intStatus&INT_SOURCE_01) {
/* do handle source 1 */
}
if(intStatus&INT_SOURCE_02) {
/* do handle source 2 */
}
}
Gemeinsam genutzte Daten und Race Conditions
Race Condition beschreibt eine Situation in der das End-Ergebnis von der genauen Abarbeitungsreihenfolge des Hauptprogramms und der Interrupt Service Routine abhängt.
Eine solche Situation muss auf alle Fälle vermieden werden!
Es ist sehr schwierig Fehler aufgrund von Race Conditions zu finden da
Interrupts asynchrone Ereignisse sind
Sie nicht immer auftreten
Um eine kritische Sektion müssen alle Interrupts gesperrt werden.
intmain(void) {
while(1) {
interruptDisable();
if(gIndex) {
/* process data */gIndex--;
}
interruptEnable();
}
}
Wichtige Punkte bei Interrupts
Interrupts sind essenziell für Eingebetteten Systeme, hier sind einige wichtige Aspekte die Berücksichtigt werden müssen.
Erst mal den ersten IRQ bekommen!
Wenn Sie ein Interrupt einrichten, sollten Sie als erstes versuchen den Interrupt einmal auszulösen bevor Sequenzen dieses Interrupts bearbeitet werden.
Interrupt blockieren / sperren
Interrupts können an verschiedenen Stellen gesperrt sein.
Stellen Sie sicher dass der spezifische Interrupt sowohl im Interrupt Controller als auch in der Peripheriekomponente aktiviert ist.
Stellen Sie sicher dass globale Interrupts im Prozessor freigegeben sind.
###ISR Installation
Überprüfen Sie dass die Interrupt Service Routine an der richtigen Stelle in der Interrupt Vektor Tabelle steht.
Verinnerlichen Sie das Mapping der Interrupts für den Prozessor.
LEDs können hilfreich bei der Analyse von Interrupts sein.
Schutz vor unbehandelten Interrupts
Stellen Sie sicher dass für jeden Interrupt im System eine Interrupt Service Routine existiert.
Es ist empfehlenswert im Startup Code, für jeden Interrupt eine Default ISR zu installieren.
Damit ist gewährleistet dass jeder Interrupt bearbeitet wird und nicht zu sporadischen Fehlverhalten führt.
Prozessor Kontext
Vergewissern Sie sich dass der Prozessor Kontext in der ISR richtig gerettet und wieder hergestellt wird.
Solche Fehler sind sehr schwer zu finden und nach zu vollziehen.
Quittieren / zurücksetzen des Interrupts
Der IRQ muss zurückgesetzt werden, damit ein normaler Programmablauf wieder weiter gehen kann.
Wird dabei ein Fehler gemacht können ISRs mit der selben oder niederen Priorität nicht mehr funktionieren.
Bei Pegel Interrupts wird bei einem Fehler die ISR unendlich laufen.
Vermeiden von Race Conditions
Besonderes wichtig beim Entwurf der SW Architektur ist die Vermeidung von Race Conditions zwischen ISRs und dem Hauptprogramm.
Aktivieren und deaktivieren von Interrupts
Minimieren Sie die Zeitfenster in denen die Interrupts deaktiviert sind.
Das ist besonders wichtig bei Echtzeitsystemen.
Besondere Vorsicht ist geboten bei der Umsetzung von solchen Mechanismen um kritische Sektionen.
Verwenden Sie eine Variable um den aktuellen Stand der Interrupt Maske zu speichern, damit vermieden wird Interrupts frei zu geben an Stellen an denen sie zuvor nicht aktiviert waren.
Eine Task (Threads) entspricht einem Stück Software, das unabhängig vom Rest des Systems ausgeführt werden kann. Die Entkopplung wird durch das Betriebssystem umgesetzt.
Tasks bilden den Schlüssel für die Abstraktion von Software Funktionen und erlauben ein einfacheres Design von eingebetteten Systemen.
Das Ergebnis ist ein Source Code der einfacher verständlich und leichter zu verwalten ist.
Der Programmierer kann sich mehr auf die eigentliche Aufgabenstellung konzentrieren, die einzigartig für das zu entwickelnde System ist.
Kernel
Der Kernel ist ein Teil, der in allen Betriebssystemen (BS, OS) zu finden ist.
Der Kernel der meisten OSs beinhaltet folgende Komponenten
Scheduler, d.h. die Routine welche das Schalten zwischen unterschiedlichen Tasks im System realisiert
Mechanismen zur Kommunikation und Synchronisation zwischen Tasks
Scheduler
Verwaltet die Ressource CPU
Sorgt dafür dass mehrere gleichzeitig ausführbare Tasks auf dem Prozessor serialisiert abgearbeitet werden, aber der Eindruck einer parallelen Abarbeitung entsteht.
Der Scheduler stellt das Herz und die Seele eines jeden OSs dar.
Scheduling Algorithmen
First-In-First-Out (FIFO)
Jede Task wird bis zu ihrem Ende abgearbeitet.
Erst dann findet ein Taskwechsel statt.
Die Abarbeitungsreihenfolge definiert sich durch deren Startzeitpunkt.
Shortest job first
Der Scheduler erlaubt jeder Task entweder bis zum Ende zu laufen oder bis sie sich selbst schlafen legt.
Es wird immer die Task begonnen die am schnellsten wieder zu Ende ist. D.h. die kürzeste Prozessorzeit in Anspruch nimmt.
Priority
Wird üblicher weise in Echtzeit Betriebssystemen verwendet.
Jeder Task wird eine Priorität zugeordnet die genutzt wird um zwischen den Lauffähigen Task diejenige zu wählen die ausgeführt werden soll.
Es kann entweder ein preemptive oder nonpreemptive Ansatz gewählt werden.
Preemtive: jede laufende Task kann durch den Scheduler unterbrochen werden wenn eine höher- priore Task lauffähig wird.
Round Robin
Jede Task läuft für einen vordefinierten Zeitabschnitt.
Nach Ablauf des Zeitintervalls wird die aktuelle Task unterbrochen und eine neue Task ausgeführt.
Erst nachdem alle Tasks einmal zum Zuge gekommen sind, beginnt der Zyklus von vorne.
Echtzeit Scheduling Algorithmus
Bei einem Echtzeit Scheduling Algorithmus wird bei der Auswahl der zu bearbeitenden Task ihre Deadline berücksichtig. Zum Einsatz eines Echtzeit Scheduling Algorithmus müssen die Deadlines der Tasks bekannt sein. Diese werden als Basis für die Entscheidung verwenden, welche der bereiten Tasks abgearbeitet wird.
Real-time executive
Jeder Task wird eine Zeitfenster in einem wiederkehrenden Muster zugeordnet.
Dieses Verfahren ist sehr statisch und nimmt an der Programmierer kennt die Ausführungszeiten jeder Task.
Vergleichbar mit der Zeitplanung bei öffentlichen Verkehrsmitteln.
Funktioniert nicht bei dynamischen Systemen bei dem Tasks kommen und gehen bzw. bei sehr unregelmäßigen Intervallen.
Earliest deadline first (EDF)
Das BS berechnet die nächste Deadline für alle ausführbaren Tasks und zu jedem Scheduling Punkt wird die Task mit der nächsten Deadline zur Ausführung ausgewählt.
Es handelt sich hier um ein prioritätsbasiertes Scheduling Verfahren mit dem Zusatz, dass zu jedem Zeitpunkt die aktuelle Deadline berechnet wird und daraufhin die Prioritäten angepasst werden.
Ein Hauptnachteil dieses Algorithmus ist der Berechnungsoverhead zur Aktualisierung der Deadlines.
Maximum laxity first
Das BS berechnet die nächste Deadline und die noch verbleibende Abarbeitungszeit für alle Tasks.
Zu jedem Scheduling Punkt wird die Task mit der kleinsten laxity gewählt, wobei laxity die Differenz zwischen verfügbarer Zeit zur Deadline und der noch notwendigen Bearbeitungszeit ist.
Resource reservation
Wenn eine Task erzeugt wird, gibt sie ihre Ressourcen Anforderungen, wie z.B. Deadlines, Prozessornutzung, etz. an.
Das Betriebssystem wird die neue Task nur zulassen wenn es garantieren kann, dass im System noch die geforderten Ressourcen verfügbar sind.
Das Ziel eines Echtzeit Scheduling Algorithmus ist, dass kritische Zeitbedingungen (Deadlines oder Reaktionszeiten) eingehalten werden.
Scheduling
Zu bestimmten Zeitpunkten, sogenannten Scheduling Punkten, wird der Scheduler (Stück SW) ausgeführt und entscheidet über den weiteren Verlauf der Abarbeitung von Tasks.
Task Erzeugung
Neue Task wird in die Liste der Tasks aufgenommen.
Task Löschung
Task wird entfernt und daher muss eine andere zur Ausführung kommen.
Clock Tick
Bei jedem Tick könnte eine wartende Task wieder aktiviert werden (z.B. nach dem Sleep Intervall).
Task Blockierung
Wenn eine Task durch ein Systemaufruf blockiert wird.
Task Freigabe.
Wenn eine Task auf ein bestimmtes Ereignis wartet (z.B. Verfügbarkeit von Daten) und dieses eintrifft könnte diese Task zur Ausführung kommen.
Task Kontext
Der Scheduler verwaltet die Tasks indem er für jede Task einen sogenannten Task Kontext anlegt.
Der Kontext einer Task beinhaltet den gesamten Prozessor Zustand, kurz vor dem Wechsel zu einer anderen Task.
Dies beinhaltet für gewöhnlich
Zeiger auf den nächsten auszuführenden Befehl (Befehls Zeiger).
Adresse des aktuellen Stack Endes (Stack Zeiger)
Prozessor Status Register
Alle algemeinen Prozessor Register
Task Control Block
Neben dem Task Kontext benötigt der Scheduler pro Task noch zusätzliche Verwaltungsinformationen, die gemeinsam im sogenannten Task Control Block im
Betriebssystem verwaltet werden.
Der Task Control Block beinhaltet zusätzlich
Zeiger auf den Task Kontext
Aktuelle Zustand in dem sich die Task befindet
Task Priorität
Task Eintrittspunkt Funktion
Alle Task spezifischen Daten (z.B. Parameter, Task Namen, etz.)
Task Prioritäten
Bei der Wahl der Prioritäten für Tasks müssen unterschiedliche Aspekte berücksichtigt werden.
Wie im vorangegangenen Beispiel gesehen können bei ein und dem selben System aufgrund einer ungünstigen Prioritätsvergabe, Fehler auftreten.
Bei einem System mit zyklischen Tasks in dem die Deadline der Task mit dem auftreten der neuen Periode übereinstimmt, gibt es eine optimale Prioritätsvergabe und zwar nach dem Rate Monotinic Algorithm (RMA).
Rate Monotonic Algorithm (RMA)
Die Priorität jeder Task wird abhängig von der Periode zugeordnet. Je kürzer die Periode, desto höher ist die Priorität der Task.
Optimale Prioritätsvergabe bezieht sich dabei auf die Maximierung der abgearbeiteten Task unter Einhaltung aller Deadlines.
D.h. bei einem System mit statischen Prioritäten, bei dem es eine Lösung der Prioritätsvergabe gibt, führt das RMA Verfahren auch zu einer gültigen Lösung.
Der Hauptnachteil von festen-statischen Prioritäten ist, dass der Prozessor nicht zu 100% ausgelastet werden darf, um zu gewährleisten dass alle Deadlines eingehalten werden.
D.h. bei dem RMA Verfahren, mit der optimalen festen- statischen Prioritätsvergabe, ergibt sich für den schlimmsten Fall die maximal zulässige Auslastung (worst-case schedulable bound) mathematisch durch:
W(n) = n(2^(1/n)-1)
Für sehr viele Tasks ergibt sich Wn= ln(2), ca. 69,3%
Es ist somit möglich dass eine Set von zyklischen Tasks lediglich 70% Auslastung erzeugen aber dennoch nicht alle Deadlines einhaltet.
Es gilt für den Nachweis zur Einhaltung aller Deadlines im Falle von zyklischen Task mit der Deadline = Periode:
Grundbedingung (notwendig aber nicht hinreichend): U ≤ 100%
U ≤ Wn => System ist OK (hinreichende Bedingung)
Für den Fall dass die Auslastung höher ist muss eine fallspezifische Analyse durchgeführt werden (z.B. Beispiel von vorhin)
TODO: prio bild
Synchronisation von Tasks
Mutex (mutual exclusion)
Zum Regeln eines exklusiven Zugriff auf geteilte Variablen / Datenstrukturen / Hardware, etz.
Eine Task nimmt den Mutex und gibt ihn nach vollendeter Arbeit wieder zurück
Semaphor
Wird zur Synchronisation zwischen Tasks verwendet.
Es gibt einen Semaphor Verteiler und jemand anderes "konsumiert" den Semaphor.
Message Queue
Wenn nicht nur synchronisiert sondern auch noch Daten dabei ausgetauscht werden sollen, dann werden Message Queues verwendet.
Probleme bei der Synchronisation von Tasks
Deadlock
Aufgrund von verschachtelten Ressourcen Reservierungen kommt es zu einer Blockierung zweier Tasks.
Priority Inversion
Aufgrund von Ressourcen Abhängigkeiten bei zwei Tasks erfolgt eine Blockierung der höher-prioren Task.
Lösung für Priority Inversion:
Priority Inheritance
Die nieder-priore Task erbt die Priorität der höher-prioren Task die an der Ressource wartet.
Priority Ceiling
Einer Ressource wird eine Priorität zugeordnet, welche sich aus der höchste Priorität aller Tasks ergibt die diese Ressource nutzen, + 1.
Beim Einsatz von Synchronisationsmechanismen muss besonders auf solche Effekte geachtet werden.
Interrupt Verarbeitung
###Interrupt Prioritäten
Interrupts haben die höchste Priorität im System. Sie sind höher als die höchste Priorität der Betriebssystem Tasks.
Interrupt Service Routinen (ISR) werden nicht vom Scheduler verwaltet, ihre Verarbeitung ist außerhalb des RTOS Schedulers.
###Maskieren von Interrupts
Betriebssystem blockieren Interrupts um Ihre Datenstrukturen in kritischen Sektions zu schützen. Dies erhöht die Interrupt Latenz Zeit.
Interrupt Stack
Einige Betriebssysteme haben einen separaten Stack für die Interrupt Verarbeitung.
Das ist wichtig da andernfalls die Interrupts auf dem Stack der aktuelle unterbrochenen Task arbeiten, und somit die wort-case Schachteltiefe von Interrupt Funktionen auf jedem Task Stack vorgesehen werden muss.
Signalisierung an Tasks
Da ISR außerhalb des Schedulers ausgeführt werden dürfen sie nur beschränkt OS Funktionen nutzen. So darf eine ISR an keinem Semaphor blockieren, aber sie kann z.B. einen Semaphor setzen.
Empfehlung bei Interrupts mit RTOS
Versuchen Sie die Verweildauer in den ISRs so kurz wie möglich zu gestalten.
Es empfiehlt sich lieber eine zusätzliche hoch-priore Task zu realisieren in der dann alle rechenintensiven Aktivitäten umgesetzt werden.
Dadurch wird die gesamte Interrupt Latenzzeit verbessert und auf Taskebene sind alle RTOS Funktionen verfügbar.
Real-Time Charakteristik
Ein Echtzeit Betriebssystem zeichnet sich durch folgende Eigenschaften aus
deterministisches Verhalten der Systemaufrufe
garantierte worst-case Interrupt Latenzzeiten
garantierte worst-case Kontextwechsel Zeiten
Mit den konkreten Zahlen für die Zeiten und dem aktuellen Design eines System (z.B. Anzahl Tasks, Abhängigkeiten und Prioritäten) kann die worst-case Performanz des System analysiert werden.
Theoretisch können Bits innerhalb eines Bit-Feldes individuell gesetzt, getestet, gelöscht und umgeschaltet werden, ohne die anderen Bits zu beeinflussen.
Theoretisch weil in vielen Fällen solche Operationen nicht Thread-Save sind. Und abhängig von der Compiler Implementierung teilweise sehr ineffizienten Code erzeugen. Des weiteren sind Bitfelder nicht portierbar, die Bits Verteilung ist stark Compiler abhängig und solche Daten können in unterschiedlichen Systemen nicht identisch interpretiert werden. (Bemerkung: ähnlich dem Endianess Problem)
Probleme
nicht Thread-Save
compileranhängig
nicht portierbar
Struktur Überlagerung
In eingebetteten Systemen mit Memory-Mapped I/O Peripherie Komponenten kann es sinnvoll sein eine C Struktur über die Peripherie Steuer und Status Register zu legen.
Damit kann mit einem Pointer auf die Struktur sehr anschaulich auf die Register der Peripherie zugegriffen werden.
Der Code ist sehr gut lesbar.
Der Compiler berechnet die Adress-Offsets beim Bilden automatisch.
mit Filename, Attributen, Erstell- und Änderungs Datum, etc.
Unterverzeichns ist eine Datei, die wiederum ein Root Directory ist.
FAT Cluster
Feste anzahl Cluster
Ein Cluster kann aus einem oder mehreren Sektoren bestehen
FAT Eintrag zum Cluster:
Frei
Defekt
Belegt (Verweis auf nächsten Sektor oder Ende)
FAT lesen
Eintrag im Root Directory suchen
Startcluster lesen
Über FAT weitere Cluster lesen
Beenden wenn 0xFFFFFF in der FAT steht
##FatFS
Windows kompatibles FAT Datei System.
Plattform unabhängig und einfach zu Portieren
Sehr kleiner Footprint
Viele konfigurations optionen
BSD-style license
Abhängigkeiten
Disk I/O
RTC
Sector Alignment
nur komplette Sectoren koennen geladen/geschrieben werden
halbe Sectoren werden im I/O Buffer gecached und erst bei flush, wenn der Buffer voll oder wenn der Teilsector voll ist geschrieben
Critical Section
I/O Buffer wird erst bei schließen des Files geleert, hier kann es zu Race Conditions kommen.
Lösung: wenn relevant dann direkt nach schreiben via .sync() den I/O Buffer schreiben.
SD-Interface
??? Folien IMHO totaler crap ?!?!?
USB Schnittstelle
USB Standard
Host
Steuert den Datenverkehr
Client
Antwortet auf Anfragen des Hosts
On-The-Go (OTG)
Mischung aus Client und Host
Verwendung bei Smartphones
Vorteile
Erheblich schneller als COM
Ersetzt veraltete COM-Schnittstelle
Mehrere Geräte anschließbar (Hub)
Hardware
hier nur Client
Hardware übernimmt sehr viele Funktionen
CRC-Prüfung
Synchronisation
Handshakes zwischen Host und Client
Konfiguration
+---------------+
| Konfiguration |
+---------------+
|
v
+---------------------+
| Hardware aktivieren |
+---------------------+
|
v
+-------------------+
| USB-Verbindung mit |
| Host herstellen |
+--------------------+