kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




05.10.2011 - Einführende Veranstaltung zum Umgang mit RTOS (Real-Time Operating Systems) am Beispiel von Salvo

(EN google-translate)

(PL google-translate)

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 0-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 0-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 0-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 0-2: Änderungen in der Konfigurationsdatei