1 05.10.2011 - Einführende Veranstaltung zum Umgang mit RTOS (Real-Time Operating Systems) am Beispiel von Salvo
Theoretischer Teil: RTOS Grundlagen (vergl. Kapitel 2 Salvo-Manual)
Möglichkeiten Tasks zu verwalten und deren Vor- und Nachteile
| Englischer Begriff |
Direkte Übersetzung |
Bedeutung |
| foreground / background (or superloop)-system |
Vordergrund / Hintergrund (oder auch Superschleife)-Systeme |
Abarbeitung der Tasks läuft durch Aufruf von Funktionen aus einer Main-Endlos-Schleife heraus. |
| Event driven Multitasking |
Ereignisbasierter Multiprogrammbetrieb |
Aufruf von Funktionen über Interrupts |
| RTOS with preemptive context switching |
RTOS mit bevorrechtigter Kontext-Umschaltung |
Unterbrechung von Tasks an beliebiger Stelle und Retten und Wiederherstellen aller vom Task verwendeten Register-Inhalte |
| RTOS with cooperative context switching |
RTOS mit kooperativer Kontext-Umschaltung |
Im Quelltext sind bei den einzelnen Task-Funktionen Stellen markiert, an denen der Scheduler Gelegenheit erhält, einen anderen Prozeß zu starten. |
Tabelle 1-1: Systeme zur Verwaltung von Tasks.
Lexikon
| Englischer Begriff |
Direkte Übersetzung (im Sinne von RTOS) |
| eligible |
wählbar |
| context switching |
(Register-)Kontext Umschaltung |
| Latency |
Latenzzeit (Dauer, bis z.B. eine Interrupt-Funktion nach Auftritt eines Events abgearbeitet wird) |
| maintain |
aufrechterhalten / unterhalten (z.B. einen Task in einem bestimmten Zustand) |
| provide |
bereitstellen |
| reliable |
betriebssicher / zuverlässig |
| scheduler |
Zeitplanungsprogramm |
| state machine |
Zustandsautomat |
| stack |
Kellerspeicher |
| system tick |
Kleinste Zykluszeit eines Maschinenbefehls auf einem System |
| voluntarily relinquish control |
freiwillig die Kontrolle preisgeben |
| yield |
(Betriebssystem-)Einschub |
| to yield |
(etwas) abgeben |
Tabelle 1-2: Lexikon zentraler Begriffe im Zusammenhang mit RTOS
Verständnisfragen / Themen
- Gründe, ein RTOS einzusetzen, statt superloop-systems.
- Vor- und Nachteile von preemptive und cooperative context switching.
- Ursachen für Latenzzeiten beim Start von Tasks.
- RTOS-Typ von Salvo.
|
Praktischer Teil: Anwendung von Salvo als RTOS für einen ATmega32
Hinweise zum herausgegebenen Material
- Pumpkin hat ein AVR-Studio-Beispiel-Projekt (Tut5) für einen ATmega16 herausgegeben.
- Siehe in C:\Pumpkin\Salvo\Example\AVR\ATmega16\AVR_Studio\Tut.
- 001_Anzeige_muecA, 002_Test_muecB, 003_RS232_test: gewöhnliche ATmega32-Projekte aus "Mikrocontrollertechnik"
- fhb_rtos_002_vereinfacht: aus Tut5 abgeleitetes vereinfachtes Projekt mit einem Rechteckgenerator-Task und einem RS232-Task.
|
- Dateien des Beispielprojektes:
- tut5.c: Hauptprogramm (liegt unter /media/sda1/Pumpkin/Salvo/Example/AVR/AT90S8515/AVR_Studio/Tut/Tut5 !)
- tut5_board.c: Konfigurationsdatei, in der beispielsweise auch die Taktrate des Schedulers eingestellt wird.
- salvocfg.h: Konfigurationsfile gibt Auskunft über verfügbare Events (1 bei kostensloser Lite-Version) und Multitasks (3 bei kostenloser Lite-Version).
|
- Auswahl des Devices über: Project->Configuration Options->Device
|
Übung
Vereinfachen Sie das Projekt Tut5 so, dass ein Rechtecksignal mit 500Hz auf PD7 ausgegeben wird (TaskBlink).
Als weiterer Task soll eine Datenübertragung über RS232 laufen (TaskRS232):
Ein mit jedem Schleifendurchlauf in TaskBlink inkrementierter unsigned-char-Wert "zaehler" soll über die serielle Verbindung an das Hyperterminal des PCs geschickt werden.
#include <salvo.h>
extern void board_init ( void );
extern void board_enable_interrupt ( void );
#define TASK_RS232_P OSTCBP(1) // task #1
#define TASK_BLINK_P OSTCBP(2) // task #2
#define PRIO_RS232 10 // niedrige Priorität
#define PRIO_BLINK 2 // hohe Priorität
//------------------------------------------
//Definition von Taktfrequenz und Bausrate als Konstanten
#define TAKTFREQUENZ 9216000
#define BAUDRATE 115200
volatile unsigned char zaehler;
void TaskRS232(void)
{
unsigned char l,m,r; //Variablen für Anzeige links, mitte, rechts
unsigned char akku=0;
DDRC = 255;
//Merken des in UBRR zu speichernden Wertes.
unsigned int baudregister = TAKTFREQUENZ/8/BAUDRATE-1;
//setzen der Baudrate
UBRRH = (unsigned char) (baudregister>>8); //Setzen des HIGH-Bytes des Baudraten-Registers
UBRRL = (unsigned char) baudregister; //Setzen des LOW -Bytes des Baudraten-Registers
//Einschalten des Senders und des Empfängers
UCSRB = (1<<TXEN) | (1<<RXEN);
//Setzen des Nachrichtenformats: 8 Datenbits, 1 Stopbits
UCSRC = (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);
UCSRA = (1<<U2X);
while (1)
{
OS_Yield();
akku = zaehler;
OS_Yield();
l= akku / 100; //links = Inhalt von akku dividiert mit 100
OS_Yield();
m= (akku % 100) / 10; //mitte = Inhalt von akku modulo mit 100 , anschließend dividiert mit 10
OS_Yield();
r= akku % 10; //rechts = Inhalt von akku modulo mit 10
//Die drei Ziffern der aktuellen Zahl werden in ASCII-Zeichen umgewandelt
//und mit UDR = ... seriell gesendet.
//Dann muß gewartet werden, bis das Senden beendet ist.
//Es folgen ein Zeilenumbruch und ein Zurücksetzen des Cursers auf den
//Zeilenanfang:
while( !(UCSRA & (1<<UDRE)) ) //Warten bis der Uebertragungspuffer leer ist
OS_Yield();
UDR = l+48; //Daten in den Puffer schreiben und übertragen
while( !(UCSRA & (1<<UDRE)) )
OS_Yield();
UDR = m+48;
while( !(UCSRA & (1<<UDRE)) )
OS_Yield();
UDR = r+48;
while( !(UCSRA & (1<<UDRE)) )
OS_Yield();
UDR = '\n';
while( !(UCSRA & (1<<UDRE)) )
OS_Yield();
UDR = '\r';
//Zahl außerdem wie gewohnt nach Port C schicken:
PORTC = akku;
}
// Context-switch, damit der andere Task laufen kann.
OS_Yield();
}
void TaskBlink(void)
{
//PD7 als Beeper benutzen:
zaehler=0;
DDRD = 0b10000000;
PORTD = 0b00000000;
while (1)
{
// Toggle PORTC:0
PORTD ^= 0b10000000; //EXOR
zaehler++;
// Context-switch für 1 tick.
OS_Delay(1);
}
}
int main (void)
{
board_init();
OSInit();
// Erzeuge Salvo tasks.
OSCreateTask(TaskRS232, TASK_RS232_P, PRIO_RS232 );
OSCreateTask(TaskBlink, TASK_BLINK_P, PRIO_BLINK );
// Benötigt: Timer1 ISR ruft den OSTimer auf.
board_enable_interrupt();
// Endlosschleife für Scheduler.
while (1)
{
OSSched();
}
}
Code 1-1: Hauptprogramm
...
void board_init ( void )
{
TCCR1B = 0x00; // Stoppe Timer1
TCNT1H = 0x00; // Lösche Timer1
//FHB_RTOS anpassen:
OCR1AH = 0x00; // Setze Compare A auf 39
OCR1AL = 0x23; // ((9.216MHz/256)/36) = 1ms
//FHB_RTOS anpassen statt Vorteiler 1024, Vorteiler 64:
TIMSK = _BV(OCIE1A);
TCCR1B = _BV(WGM12)|_BV(CS12);
}
...
Code 1-2: Änderungen in der Konfigurationsdatei