Skip to content

Instantly share code, notes, and snippets.

@andreaseger
Created July 8, 2012 01:48
Show Gist options
  • Save andreaseger/3068951 to your computer and use it in GitHub Desktop.
Save andreaseger/3068951 to your computer and use it in GitHub Desktop.
Embedded Systems SS2012

Fragen

Buildprozess

  • Was gibt es für Sections im generierten Programm?
    • Code: Programm Code
    • RO-data: Read Only Daten
    • RW-data: Read/Write Daten
    • ZI-data: Zero Initialized Daten
  • Welche Daten landen in welcher Section? Lokale Variablen kommen auf den Stack oder in Register, Globale Variablen in den Speicher
  • Gibt es Variablen, die in keiner Section vor kommen? Wird eine lokale Variable in ein Register gespeichert, kommt sie in keiner Section vor.
  • Wie groß sind der Code / Daten?
  • Wo finde ich diese Informationen?
    Map File
    Total RO  Size (Code + RO Data) 888 (   0.87kB)
    Total RW  Size (RW Data + ZI Data) 1288 (   1.26kB)
    Total ROM Size (Code + RO Data + RW Data) 888 (   0.87kB)
  • Woher kommen die 1288 Bytes ZI-data? Die 1288 ZI Bytes kommen von LPC2300.s und sind der STACK Speicher.

Code Analyse

  • Finden Sie heraus wo und wie folgende Komponenten initialisiert werden:
    • Heap
    • User Stack
    • Laufzeit-Bibliotheken
  • Wo werden die Sektionen Initialisiert?
    • ZI
    • RW mit Initialisierungswert

Geträtetreiber

  • Wo befindet sich die Beschreibung der UARTs? => Vertraut machen mit der Peripherie (UART)!
    • Mikrocontroller Handbuch Chapter 16: LPC23XX UART0/2/3 Chapter 17: LPC23XX UART1
  • Worin unterscheidet sich UART1 von den anderen?
    • Modem Interface
  • Kann dieses Zusatzfeature auf dem MCB2300 verwendet werden? (Warum?)
    • Nein!
    • Da elektrisch nicht angeschlossen!

UART

  • Wozu dienen folgende Register?
    • U1RBR
    • U1THR
    • U1DLL + U1DLM
    • U1IER
    • U1IIR
    • U1FCR
    • U1LCR (DLAB-Bit !!)
    • U1MCR
  • Was sind denn die (abstrakten) Parameter einer Seriellen Schnittstelle? D.h. die Parameter die eine API betreffen, NICHT die Register Bit-Muster!
    • Anzahl der Daten-Bits (5..9) [LPC2378: 5..8]
    • Anzahl der Stop-Bits (0, 1.5 oder 2)
    • Baud Rate
    • Parity
  • Welche Schritte braucht man zur Initialisierung der HW?
    • Siehe Anfang des Chapter 17!
    • PCONP
    • PCLK_SEL0
    • PINSELx, PINMODEx
    • UART Konfiguration (UxFDR, UxLCR, UxDLL, UxDLM, UxLCR)
  • Welches Interface abstrahiert eine Serielle Kommunikation?
    • Init
    • Send
    • Receive

Zuordnung der Prioritäten LPC2378

  • Wie sieht die Interrupt Architektur beim LPC2378 aus?
        |WDT . . .           |IS2
  +---------------------------------+
  |  Vector Interrupt Controller    |
  +---------------------------------+
            |FIQ       |IRQ
      +----------------------+
      |      ARM7TDMI-S      |
      +----------------------+
  • Wie sieht die Zuordnung der Prioritäten aus? Ist sie konfigurierbar?
    • Ja sie ist konfigurierbar (siehe Chapter 6: VIC)
  • Wie kann sie konfiguriert werden?
    • Siehe Chapter 6: VIC 5.10 Vector Priority Registers 0-31

Interrupts

  • 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?

  • Was wird automatisch gerettet (ARM7TDMI-S)?

  • Wofür braucht es das __irq?

Gerätetreiber

Philosophie

  • 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.

+---------------+
|  Application  |
+---------------+
| Device Driver |
+---------------+
|    Hardware   |
+---------------+

Implementierungsstrategie

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                                             */
void LED_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                                       */
void LED_On (unsigned int num) {
  FIO2SET = (1 << num);
}

/* Function that turns off requested LED                                      */
void LED_Off (unsigned int num) {
  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 input
PINSEL4       &= ~(3 << 20); //GPIO
IO2_INT_EN_R  |= (1 << 10);  //GPIO Interrupt Enable for Rising edge
VICVectAddr17 = (unsigned long)TASTER_IRQHandler;/* Set Interrupt Vector       */
VICVectCntl17 = 13;                          /* use it for ADC Interrupt    */
VICIntEnable  = (1  << 17);                  /* Enable ADC Interrupt        */

Hardware

Prozessoren (allgemein)

Man unterscheidend folgende Arten von Prozessoren

  • Mikroprozessor
    • General Purpose CPU
    • Kerne von PCs oder Workstation, Servers
  • Mikrocontroller
    • 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)

On-chip FLASH Speicher (512kB)

0x0000 0000 .. 0x0007 FFFF

On-chip SRAM (32kB)

0x4000 0000 .. 0x4000 7FFF

Peripherie SRAM (8kB, 16kB)

USB 0x7FD0 0000 .. 0x7FD0 1FFF
Ethernet 0x7FE0 0000 .. 0x7FE0 3FFF

APB und AHB Peripherie

APB (Advanced Peripheral Bus) 0xE000 0000 .. 0xEFFF FFFF
AHB (Advanced High-performance Bus) 0xF000 0000 .. 0xFFFF FFFF

External Memory Interface (64kB)

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)

  • Direction
  • Masking
  • Set
  • Clear

Interrupts

Basics

  • 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.

+--------------+  +--------------+
| Peripherie A |  | Peripherie B |
+--------------+  +--------------+
        |               |   |
        |INT0       |I1 |I2 |I3
  +---------------------------+
  |      Microcontroller      |
  +---------------------------+

Extern existiert ein Interrupt Controller der über nur eine Interrupt Request Leitung an den Mikrocontroller angebunden ist.

+--------------+  +--------------+
| Peripherie A |  | Peripherie B |
+--------------+  +--------------+
        |               |   |
        |INT0       |I1 |I2 |I3
  +---------------------------+
  |   Interrupt Controller    |
  +---------------------------+
                |INT
  +---------------------------+
  |      Microcontroller      |
  +---------------------------+

Grundfunktion eines 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.

__irq void interruptServiceRoutine(void) {
    uint32_t intStatus;
    /* 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.

int main(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.

Programmiersprachen

C

  • C ist heute die bevorzugte Programmiersprache für ES
  • Vorteile
    • Klein und einfach zu erlernen
    • Compiler sind für fast alle Zielsysteme erhältlich
    • Prozessorunabhängig, Entwickler können sich auf Algorithmen und Applikationen konzentrieren und müssen nicht Prozessor Spezifika kennen
    • Im Vergleich zu anderen Sprachen ist C eine sehr „low-level“ high-level Sprache
    • Große Flexibilität beim direkten Zugriff auf Hardware ohne die Vorteile einer Hochsprache ganz auf zu geben

Assembler

  • Früher sehr stark verbreitet
  • Vorteil
    • hohe Effizienz des Ergebnisses
  • Nachteil
    • Geringe Portabilität
    • Hohe Entwicklungskosten
    • Schwer Entwickler zu finden
  • Heutiger Einsatz beschränkt sich auf
    • Startup Code
    • Code Teile die extrem Effizient / besonders Kompakt sein müssen

C++

  • Objektorientiertes Superset von C
  • Kernkomponenten sind wie bei C
  • Verbesserungen zu C
    • Daten Abstraktion
    • Objektorientierte Programmierung / SW Design
  • Neue Verbesserungen sind sehr hilfreich für den Entwickler, mindern aber die Effizienz der Umsetzung
  • C++ ist weiter verbreitet bei großen Entwicklerteams wo die Vorteile für den Entwickler die Nachteile bei der Programmeffizienz aufwiegen

ADA

  • Ebenfalls eine objektorientierte Programmiersprache
  • Anfänglich wurde ADA vom U.S. Department of Defense (DoD) für die Entwicklung von sicherheitsrelevanten Militär-Software entwickelt
  • Es gibt zwei internationale Standards ADA83 und ADA95
  • Heute nicht weit verbreitet, vorwiegend in Rüstungs-, Luft und Raumfahrt Industrie

Betriebsysteme

Tasks

  • 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.

        +---------+
      ->| running |--
     /  +---------+  \
    /                 v
+-------+           +--------+
| ready | <-------  | wating |
+-------+           +--------+

Scheduling Punkte

Diese Zeitpunkte sind wohl definiert und sind:

  • 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:

  1. Grundbedingung (notwendig aber nicht hinreichend): U ≤ 100%
  2. U ≤ Wn => System ist OK (hinreichende Bedingung)
  3. 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.
  • Die genauen Zeiten hängt stark ab von
    • Verwendete Hardware Architektur (Anzahl Register, …)
    • Toolkette (z.B. Compiler, Assembler, Linker)

Peripherie

Klassifikation von Peripherie Komponenten

  • Intern oder on-chip Peripherie
    • Befinden sich neben Prozessor und Speicher im Mikrocontroller Chip
  • Externe Peripherie
    • Befindet sich außerhalb des Mikrocontroller Chips

Steuer und Status Register

  • Ein eingebetteter Prozessor interagiert mit Peripherie Komponenten mittels Steuer und Status Register.
  • Diese Register sind Teil der Peripherie Hardware. Ihre Adresse, Größe und individuelle Bedeutung sind Charakteristiken der Peripherie.
  • Zum Beispiel sind die Steuer (Control) Register einer Seriellen Schnittstelle sehr unterschiedlich von denen eines Timers.

Anbindung von Peripherie

I/O Bereich

  • erfordert besondere Befehle für den Zugriff
  • Peripherie benötigt keinen Speicherbereich (Vorteil bei begrenztem Adressraum z.B. 8-Bit)
  • Beispiel: 8085

Prozessor Speicher Bereich

  • Memory-Mapped Peripherie
  • Es ist einfacher damit zu arbeiten, da die Steuer und Status Register wie gewöhnliche Variablen im C-Code gehandhabt werden können.
  • Beispiel
#define GPIO_BASE_ADDR 0xE0028000
#define IOPIN0        (*(volatile unsigned long *)(GPIO_BASE_ADDR + 0x00))

Normalerweise werden Peripherie Register als unsigned Typen definiert, da die Interpretation des Inhaltes für gewöhnlich auf Bit-Ebene stattfindet.

Bit Manipulations

set n-th bit

x |= (1 << (n))

clear n-th bit

x &= ~(1 << (n))

test bit

(x & (1 << (y)) ? 1 : 0)

toggle n-th bit

x ^= (1 << (n))

Bit Felder

struct {
    uint8_t bit0 :1;
    uint8_t bit1 :1;
    uint8_t bit2 :1;
    uint8_t bit3 :1;
    uint8_t nibble :4;
} foo;

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.
typedef struct {
    uint16_t count;      /* Offset 0 */
    uint16_t maxCount;   /* Offset 2 */
    uint16_t _reserved1; /* Offset 4 */
    uint16_t control;    /* Offset 6 */
} volatile timer_t
...
timer_t* pTimer = (timer_t *)(0xABCD0123);
...
if(pTimer->control & 0x08) {
    // do something
}

#FatFS

##FAT

File Allocation Table

  • Bootsektor: 0x000 bis 0x1FF
    • Metadaten
    • x86 Code zum anspringen des Boot codes
  • FAT: ab 0x200
    • Tabelle fester Grösse
    • Buchführung der Cluster
    • Einfach verkettete Liste
  • Root Directory
    • Eintrag für jeden Ordner und jede Datei.
    • 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

  1. Eintrag im Root Directory suchen
  2. Startcluster lesen
  3. Über FAT weitere Cluster lesen
  4. 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   |
 +--------------------+

Beispiel:

+-----------------------+
| - Device Class        |
| - Identifications     |
|   (Vendor/Product ID) |
+-----------------------+
            |
            v
+-----------------------+
| - BitMasken auf Pins  |
|   setzen              |
+-----------------------+
            |
            v
 +----- --------------+
 | - Setup Packages   |
 |   senden           |
 +--------------------+

I2C Bus

Protokoll

  • I2C = Inter Integrated Circuit
  • Serieller synchroner Zweidraht-Bus nach dem Master/Slave-Prinzip
  • Von Philips (heute NXP) in den frühen 1980er Jahren entwickelt
  • Kein laufendes Patent mehr → keine Lizenzkosten bei der elektronischen Implementierung
  • Typische Peripheriegeräte: ADC, DAC, EEPROM, Multiplexer, Sensoren...

Bus Anschlaltung

------------------+----+-------------------- Vdd
                 [ ]  [ ] Rp (1k-10k)
----+----------+--+----+--+----------+------ SDA
----|-+--------|-+--------|-+--------|-+---- SCL
    | |        | |        | |        | |
+---------+ +--------+ +--------+ +--------+
| uC      | | ADC    | | DAC    | | uC     |
| Master  | | Slave  | | Slave  | | Slave  |
+---------+ +--------+ +--------+ +--------+

Pegel

LOW:  max 0.3 * Vdd
HIGH: min 0.7 * Vdd

Signalleitungen

SDA (Serial Data)  : Daten
SCL (Serial Clock) : Takt

Taktraten

Modus            | Maximale Taktrate
Standard Mode    | 100 kHz
Fast Mode        | 400 kHz
Fast Mode Plus   | 1 MHz
High Speed Mode  | 3,4 MHz

Clock Stretching Slave zieht die Taktleitung (SCL) aktiv auf LOW um den Master bei der Übertragung zu bremsen.

Addressierung

  • 7-Bit Adressierung
    • 27 – 16 = 112 mögliche Adressen
    • 16 reservierte Addressen
  • 10-Bit Adressierung
    • 210 = 1024 Adressen (+ 112 = 1136)
  • Sub Adressierung

TODO

SPI Serial Peripheral Interface

  • Synchroner, serieller Bus (SPI-Clock)
  • Voll-Duplex Betrieb
  • Master-Slave Betrieb
  • 4 Leitungen
+------------------+       +-----------------+
|             SCLK | ----> | SCLK            |
| SPI Master  MOSI | ----> | MOSI  SPI Slave |
|             MISO | <---- | MISO            |
|               CS | ----> | CS              |
+------------------+       +-----------------+
  • Bei mehreren Slaves sind mehrere CS-Leitungen nötig
  • Slave-MISO hochohmig wenn nicht ausgewählt (wichtig bei mehreren Slaves)

Datenübertragung

  • Bei jedem Clockcycle wird eine Voll-Duplex-Übertragung ausgeführt
    • Master sendet ein Bit auf MOSI
    • Slave sendet ein Bit auf MISO
  • Schieberegister, verbunden als Ring
    SPI Master                  SPI Master
+-------------+       +-----------------+
|  +-------+  | SCLK  |  +-------+      |
|  |Memory |  |------>|  |Memory |      |
|  +-------+  |       |  +-------+      |
|             | MOSI  |                 |
|  0|1|234567-|-------|->  0|1|234567   |
+-------------+       +-----------------+
    A           MISO          |
    +-------------------------+
  • Nachdem alle Daten raus sind haben Master und Slave ihre Registerinhalte getauscht -> Master stoppt Clock
    • Daten verarbeiten (Memory)
    • neue Daten ins Register schreiben
    • neue Übertragung beginnen

Vorteile

  • Vollduplex
  • Höherer Durchsatz als I2C
  • Flexibel (message size)
  • Einfaches HW Interface
  • Höchstens ein Slave-spezifisches Signal (CS), sonst alles geteilt

Nachteile

  • Braucht mehr Pins als I2C
  • Slaves geben kein ACK
  • Keine HW-Flusskontrolle
  • Kein Error-Checking
  • Kein formaler Standard (viele verschiedene Variationen)
  • Nur kurze Distanzen

Ablauf

  1. Clock-Count Register setzen.
  2. Control-Register setzen.
  3. Sende-Daten auf Data-Register schreiben. Startet Transfer.
  4. Warten bis Transfer fertig (Status-Register)
  5. Status-Register lesen.
  6. Data-Register lesen. Setzt Status-Register zurück.
  7. Bei mehreren Transfers ab Schritt 3 wiederholen

Sensor-Handler

  • Abstrahiert den Sensor -> Sensorspezifische Task
  • Nutzt SPI-Treiber für Kommunikation mit Sensor
  • Handler kann mehrere Sensoren des selben Typ bedienen
  • Unterscheidung der Sensoren über Sensor-ID
  • Sensorspezifische Daten im Konfigurationsfile festgelegt

Ablaufdiagramm

siehe folien

AD-Wandler

A/D-Wander wandelt eine analoge Spannung in einen (digitalen) Wert um LPC2378 besitzt

  • 8 x 10Bit ADC
  • 10 Bit Wandlung: 2.44 µs
  • Diese können über Interrupt oder im Burst-Mode betrieben werden
  • Nur 1 Interrupt, sollte auch nur für einen Port verwendet werden
  • Burst-Mode: Die Kanäle werden mit einer Frequenz periodisch gewandelt, im Register steht der letzte aktuelle Wert

Sukzessive Approximation

  • SAR wird Bit für Bit gesetzt
  • DAC wandelt SAR-Wert in analoge Spannung
  • Komparator vergleicht VDAC und VIN
  • Wenn VIN < VDAC → Bit auf 0, ansonsten 1 und nächstes Bit
  • Bei VREF = 3,3V und 10Bit -> Auflösung: 3,2 mV

How?

  • test bits high to low
  • init with 0b00000000
  • test with bit(n)=1
    • if bigger than value => 0
    • else if smaller than value => 1

pseudocode (its actually ruby)

def sukzessive_approximation
  result = 8.times.map{ 0 }
  7.downto(0).map { |n|
    result[n] = 1
    if result.zip(get_value).map{|e| e[0] >= e[1]}.reduce(:&) # result >= value
      result[n] = 0
    end
  }
end

Example with 3 bit, value = 3

  ^ value
  |
7 |
  |
6 |
  |
5 |
  |
4 |   ? // value < 4
  |   |
3 |   |   +---?! // value >=3, last bit => finished
  |   |   |
2 |   +---? // value >= 2
  |
1 |
  |
0 |
  +---+---+---+---+---> iteration
      0   1   2   3

Step:
0:  [?1, 0, 0]
1:  [ 0,?1, 0]
2:  [ 0, 1,?1]     => value == 0b011 == 3

example with 4 bits and value = 11

    ^ value
 15 |
 14 |
 13 |
 12 |   +---? // value < 12
 11 |   |   |   +---?---! // value >= 11, last bit finished
 10 |   |   +---? // value >=10
  9 |   |
  8 |   ? // value >= 8
  7 |
 .. |
  0 |
    +---+---+---+---+---+---> iteration
        0   1   2   3   4

Step:
0:  [?1, 0, 0, 0]
1:  [ 1,?1, 0, 0]
2:  [ 1, 0,?1, 0]
3:  [ 1, 0, 1,?1]     => value == 0b1011 == 11

Implememtierung, Lookuptable, Hashmap

???relevant???

Real-Time-Clock

RTC-Oscillator: 32768Hz

??? Register foo ???

Watchdog

Motivation

  • Lange Laufzeit von Embedded Systems
  • Gefahr: Fehlfunktionen
    • Überlauf des Schleifenzählers
    • Interrupt blockiert Tasks
    • Signal externer Geräte fehlt
  • Folgefehler => Notwendigkeit der Kontrolle

Lösung: Der Watchdog

  • Watchdog hält System im stabilen Zustand
  • Bricht frühzeitig ab um Folgefehler zu mindern

How?

  • Watchdog hat counter
  • WD decrementiert diesen in jedem Tick
  • Programm incementiert/reseted den WD-Counter
  • Wenn Counter == 0 =>Fehler

Bei Fehler:

  • Interrupt
  • WD-Reset

Datenspeicherung

IMHO totaler crap und unrelevant

Powermanagement

relevente Folie ist kaputt

  • Normal
  • Idle
    • Core Clock ist gestoppt bis Reset oder Interrupt
  • Sleep
    • Main Oscillator powered down
    • alle clocks(außer) stopped
  • Power-Down
    • wie sleep, plus
    • flash wird ausgeschalten, erhöht startup time
    • wake via RTC interrupts, reset
  • Deep Power-Down
    • like power down
    • plus internal logic shut of
    • nur rtc läuft noch
    • wake via alarm interrupt, reset

Ethernet

IMO nicht relevant da zu umfangreich

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment