2 12.10.2011 - Verwendung binärer Semaphoren
Theoretischer Teil: RTOS Grundlagen (Fortsetzung Kapitel 2 Salvo-Manual)
- Systemtypen S.14-28
- Binäre Semaphoren S.29-32
- Salvo-Anwendungen Schreiben (Vorgriff auf Kapitel 4) S.63-66
|
Übung
Schreiben Sie ein Programm mit drei Tasks:
- TaskTaster: Inkrementiert Zaehler bei Taster PB0, dekrementiert bei PB1
- TaskRS232: schickt gesteuert über binäre Semaphore nur bei Änderung der Variable "unsigned char zaehler" den Wert an das Hyperterminal, bzw. aktualisiert ihn über PortC
- TaskBlink: Dauerton 500Hz
|
Lexikon
| Englischer Begriff |
Direkte Übersetzung (im Sinne von RTOS) |
Erläuterungen |
| Reentrancy |
Eintrittsinvarianz (von Funktionen) |
Wird eine Funktion während sie läuft an anderer Stelle aufgerufen, korrumpiert das nicht ihre Ausführung. |
| to signal |
melden |
to signal a semaphore: Ereignis, das eine Semaphore in ihrem Zustand ändert. |
| task control block pointer(tcb p) |
Zeiger auf einen Scheduler internen Task-Steuerungs-Block |
...der eine Funktion über einen Zeiger als Task zugeordnet werden kann. |
Tabelle 2-1: Lexikon zentraler Begriffe im Zusammenhang mit RTOS
Verständnisfragen / Themen
- Wie sollten die drei Prozesse der Übungsaufgabe priorisiert werden?
- Lassen sich OS_Yield(), OS_Delay(..), OS_WaitBinSem(...) beliebig mischen? - Was macht Sinn?
- Was bedeutet OSNO_TIMEOUT in OS_WaitBinSem(...) ?
- Welche Rückwirkungen haben Scheduler-Aufrufe auf globale und lokale (innerhalb eines Tasks definiert) Variablen?
|
Lösungsbeispiel
/************************************************************
Programmidee:
Ein schneller Prozess prüft ständig, ob die Taster PB0 oder PB1
gedrückt werden und inkrementiert, bzw. dekrementiert einen
Zähler "zaehler"
Wenn dies geschieht, bekommt eine binäre Semaphore ein Signal.
Auf die Semaphore wartet ein zweiter Prozeß, der
den Zählerstand über die serielle Schnittstelle an das
Hyperterminal schickt.
Ein dritter Prozess erzeugt ständig einen Ton von 500Hz.
************************************************************/
#include <salvo.h>
extern void board_init ( void );
extern void board_enable_interrupt ( void );
#define TASK_TASTER_P OSTCBP(1) // task #1
#define TASK_RS232_P OSTCBP(2) // "" #2
#define TASK_BLINK_P OSTCBP(3) // "" #3
#define PRIO_TASTER 6 // task priorities
#define PRIO_RS232 10 // ""
#define PRIO_BLINK 4 // ""
#define BINSEM_UPDATE_PORT_P OSECBP(1) // binSem #1
//Definition von Taktfrequenz und Bausrate als Konstanten
#define TAKTFREQUENZ 9216000
#define BAUDRATE 115200
volatile unsigned char zaehler;
volatile unsigned char taster;
volatile unsigned char zustand;
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)
{
//Weiterer Durchlauf, nur wenn binäre Semaphore ein Signal erhalten hat:
OS_WaitBinSem(BINSEM_UPDATE_PORT_P, OSNO_TIMEOUT);
akku = zaehler;
//Zahl außerdem wie gewohnt nach Port C schicken:
PORTC = akku;
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';
}
// Context-switch, damit der andere Task laufen kann.
OS_Yield();
}
void TaskBlink(void)
{
//PD7 als Beeper benutzen:
DDRD = 0b10000000;
PORTD = 0b00000000;
while (1)
{
// Toggle PORTD:0
PORTD ^= 0b10000000; //EXOR
// Context-switch für 1 tick.
OS_Delay(1);
}
}
void TaskTaster(void)
{
//Zähler initialiseren.
zaehler = 0;
taster = 0;
zustand = 0;
DDRB &= 0b11111100;
while (1)
{
taster = PINB;
taster &=0b00000011;
if(zustand!=1 && taster==1)
{
zustand = 1;
zaehler++;
OSSignalBinSem(BINSEM_UPDATE_PORT_P);
OS_Delay(5);
}
if(zustand!=2 && taster==2)
{
zustand = 2;
zaehler--;
OSSignalBinSem(BINSEM_UPDATE_PORT_P);
OS_Delay(5);
}
if(taster==0)
{
zustand = 0;
OS_Delay(5);
}
OS_Yield();
}
}
int main (void)
{
//Hardware abhängige Initialisierung.
board_init();
//Salvo initialisieren.
OSInit();
//Erzeugen von Salvo Tasks.
OSCreateTask(TaskTaster, TASK_TASTER_P, PRIO_TASTER);
OSCreateTask(TaskRS232, TASK_RS232_P, PRIO_RS232 );
OSCreateTask(TaskBlink, TASK_BLINK_P, PRIO_BLINK);
//Erzeugen von Salvo Events.
OSCreateBinSem(BINSEM_UPDATE_PORT_P, 0);
//Interrupts aktivieren wg. Scheduler bzw. Timer 1.
board_enable_interrupt();
//Scheduler / Multitasking starten.
while (1)
{
OSSched();
}
return 0;
}
Code 2-1: Lösungsbeispiel zu der Übung mit binärer Semaphore.