Zu den für Anfänger am schnellsten zu verstehenden Echtzeitbetriebssystemen gehört µC/OS von Jean J. Labrosse. Es kann bis zu 63 Applikationstasks mit je unterschiedlichen Prioritäten verwalten und gehört zu den Echtzeitbetriebssystemen mit dem geringsten Speicherbedarf.
Da die Originalversion aus dem Embedded Systems Programming Magazine(1992) FREI ist, habe ich diesen für Interressierte zum Download hinterlegt.
Da die Firma Micrium Ihre Source-Code Distribution Lizenz-Bedingungen auch für die Originalversion aus dem Embedded Systems Programming Magazine(1992) geändert hat, kann ich hier nur noch einen Link zur Download-Seite des Magazins stellen.
Da in der Originalversion Daten durch direkte Übergabe von Zeigern zwischen den Tasks ausgetauscht werden, und somit keine Garantie für die freie Verwendbarkeit des Absender-buffers nach
Übergabe an einen anderen Task besteht und, viel wichtiger, der Empfänger einen Zeiger in das Datenfeld eines anderen Tasks erhält (Pointer-Fehler / Längen-Fehler / Manipulationen u.a.) habe ich die Mechanismen
für Message-Box und Queue entsprechend so geändert, daß nun die Daten über einen Kernel-internen Buffer an den Empfänger übergeben werden. Das bedeutet, daß der Kernel zu übertragende Daten in einen eigenen Buffer kopiert
und bei Übergabe an den Empfänger diese auch wieder selber in den durch den Empfänger bereitgestellten Buffer kopiert. Das bringt zwar einen höheren Speicherbedarf je Queue mit sich, sichert aber dafür die Prozesse weitestgehend (für Real-Mode) voneinander ab.
Aus Sicherheitsgründen wurden außerdem Kernel-Konstanten in den CODE-Area verlegt. Desweiteren habe ich den Kernel um die Diente Pipe, Eventgroup und dyn. Memorymanagement erweitert.
Um diese Änderungen eindeutig zu deklarieren habe ich den Namen, angelehnt an dem immer größer werdenden Original "µC/OS", auf pC/OS wie "pico-C.." geändert.
Special zu: Priority Inversion, das Problem und die Lösungsansätze
Um endgültig den Status des pC/OS Kernel basierend auf dem µC/OS 1.00 klarzustellen,
habe ich die alte "V1.00 is FREE" Bestätigungs-email von Jean Labrosse herausgesucht.
Releases:
1.00a | Messagebox und Queue geändert sowie Pipe, Eventgroup und dyn. Memorymanagement hinzugefügt. |
1.00b | Revision-Info, Tick-counter, Task-Suspension, Task-Delete, History-Table und OS_QueueFrontPost hinzugefügt (Task-Suspension und Task-Delete aus 1.09 importiert). |
1.00c | Beschleunigung der Pipe (bei Taskwechsel auf Grund der Datenübergabe und wenn Pipe leer ist werden die Daten vom Sender-buffer direkt in den Empfänger-buffer kopiert). |
1.00d | korrekte Implemenierung "timeout" in OS_...Post-Funktionen und OS_QueueClear hinzugefügt. |
1.00e | Anpassung der Task-Initialisierung und einiger Parameter zur Nutzung der µC/OS-I Port's. |
1.00f | Bugfix innerhalb der Event-Gruppen. |
1.00g | Dual-Source für MSC-8.00 und Keil-C51 generiert. OS_CFG implementiert. Bugfix in Semaphoren. |
1.00h | MUTEXes hinzugefügt, Eventgruppen richtig implementiert und Code-cleanup. Cleanup der Ports. |
1.40a | Name auf pC/OS geändert und Revisionsnummer überarbeitet, einige C-fixes. |
1.41a | Implementation von OS_STK_TYPE für Prozessor-konformen Stack incl. Update aller bisherigen Ports. |
1.50c | Komplettes Update zu U08..U32 Typen. |
1.52b | Split MIN_PRIO und MAX_TASKS um RAM in der Task-Verwaltung bei wenige Tasks einzusparen. Verwendung "NULL" als pointer. Eindeutige Task-ID hinzugefügt, um anderen Modulen eine eindeutige Identifizierung des Tasks zu ermöglichen (siehe ChangePrio und Mutex) und damit auch dort RAM zu sparen. |
1.60c | temporären reg-holder hinzugefügt um bei Ab- und Anschalten der Interrupte innerhalb einer ISR gegen "neested interrupts" geschützt zu sein, wenn dies unerwünscht ist |
1.61a | der Memory-Manager arbeitet nun immer MPU-spezifisch aligned |
1.65b | Timer-Service implementiert, dessen Timer periodisch oder run-once laufen können. |
1.66c | einige Checks im Timer-Service implementiert, OS_TimerGetState() und OS_TimerGetRemain() hinzugefügt |
1.67a | Minimierung des RAM-Bedarfs der prio-tables der IPC-Resourcen (SEM/EVG/MUX/Queue/Pipe/..) |
1.68a | OS_EvgPendAbbort() hinzugefügt |
1.70b | optionaler Debug Stack-End Check hinzugefügt, Kernel Konfiguration restrukturiert |
1.71a | optionaler Stack-Fill zum Debug Stack-End Check hinzugefügt |
1.80c | einige Fixes in allen ...Abbort() Funktionen, einige zusätzliche Checks für "mutex in use", OS_TaskGetID() und OS_TaskGetStatus() hinzugefügt, mit der neuen Funktion OS_TaskDestroy() können nun Tasks - wartend an einer IPC - auch gelöscht werden, Source-File splitted für besseres Handling |
1.81b | durch eine Erweiterung in der optionalen Funktion OS_TaskDestroy() können nun auch Mutex geschützte Recoursen reinitialisiert werden, die selbst eine User-Verwaltung basierend auf der unique Task-ID beinhalten (wie zB. ein FileSystem) |
1.82a | OS_TaskDestroy() umbenannt zu OS_TaskIdDestroy(), OS_TaskGetPrio(), OS_TimeDelayIdResume(), OS_TaskIdDelete(), OS_TaskIdGetStatus(), OS_TaskIdSyspend() und OS_TaskIdResume() hinzugefügt plus einige zusätzliche Checks für prio und/oder id |
1.83a | kleiner fix für OS_TaskIdDestroy() in OSIntExit(), wenn der aktuell laufende Task durch eine ISR zerstrört wird |
Bei Wartezeiten für "OS_...Post" und "OS_...Pend" -Funktionen ist zu Beachten, daß bei einem Aufruf einer Sender-/Empfängerfunktion aus einer ISR heraus NIE mit einer Wartezeit oder sogar Suspendierung gearbeitet werden darf, da dieses die ISR stoppen würde, und somit nichts mehr läuft. Eine Prüfung dieses Zustandes ist integriert worden.
bekannter Bug:
Wenn ein niederpriorisierter Task auf eine Recource wartet, und ein höherpriorisierter Task diese Recource setzt, so wird der schlafende Task in den Ready-state versetzt. Da der höherpriorisierte Task weiterläuft, darf dieser die selbe Recource nicht wieder auslesen, da ansonsten der niederpriorisierte Task bei Ausführung 'vorzeitig' mit dem Return-Code OS_TIMEOUT zurück kommt.
Kerneldienste des pC/OS Version 1.83a (Kurzform)
Die hier aufgeführte Funktionsübersicht dient einzig als Kurzübersicht.
Für detailierte Informationen sehen Sie bitte in Referenz-Manual zu pC/OS nach.
Task-Control: | |
OS_Init | Initialisierung des Kernels |
OS_Start | Beginn der Kernelservices |
OS_TaskCreate | Anlegen eines Tasks |
OS_ChangePrio | Änderung der Priorität des aktiven Tasks |
OS_TaskChangePrio | Änderung der Priorität eines aktiven/ready Tasks |
OS_TaskDelete | Löschen eines aktiven/ready Tasks |
OS_TaskIdDelete | Löschen eines aktiven/ready Tasks via unique ID |
OS_TaskGetStatus | Gibt den aktuellen Status eines Tasks zurück |
OS_TaskIdGetStatus | Gibt den aktuellen Status eines Tasks via unique ID zurück |
OS_TaskGetID | Gibt die unique ID eines Tasks zurück |
OS_TaskGetPrio | Gibt die Priorität eines Tasks zurück |
OS_TaskIdDestroy | Löschen eines Tasks via unique ID, auch wenn dieser an einer IPC wartet oder eine Mutex inne hat & freigeben aller Memory Allokationen |
OS_TaskSuspend | Suspendiert einen Task |
OS_TaskIdSuspend | Suspendiert einen Task via unique ID |
OS_TaskResume | Wiederaufwecken eines suspendierten Tasks |
OS_TaskIdResume | Wiederaufwecken eines suspendierten Tasks via unique ID |
OS_TimeDly | Legt laufenden Task für bestimmte Zeit schlafen |
OS_TimeDlyResume | Wiederaufwecken eines schlafenden Tasks vor Ablauf der eingestellten Zeit |
OS_TimeDlyIdResume | Wiederaufwecken eines schlafenden Tasks via unique ID vor Ablauf der eingestellten Zeit |
OS_Lock | Unterdrücken des Shedulers (keine Taskwechsel) |
OS_Unlock | Wiederzuschaltung des Shedulers (Taskwechsel bei Ereignis oder Zeit) |
OS_GetRev | Gibt Zeiger auf Kernel-Revision zurück |
Dynamic-Memory: | |
OS_MemoryInit | Erzeugen des Speicherpools |
OS_MemAlloc | Allokieren von Speicher |
OS_MemFree | Freigeben von allokiertem Speicher |
Mailboxes: | |
OS_MboxInit | Anlegen einer Mailbox |
OS_MboxPost | Sendet Daten an höchstpriorisierten Empfänger dieser Mailbox |
OS_MboxPostAbbort | Bricht Warten eines sendenden Tasks (höchste wartende Prio) an einer Mailbox ab |
OS_MboxPend | Wartet auf Daten aus einer Mailbox |
OS_MboxPendAbbort | Bricht Warten eines empfangenden Tasks (höchste wartende Prio) an einer Mailbox ab |
Queues: | |
OS_QueueInit | Anlegen einer Queue |
OS_QueueInfo | Informationen über eine Queue einholen |
OS_QueuePost | Sendet Daten in eine Queue |
OS_QueueFrontPost | Sendet Daten an den Anfang einer Queue |
OS_QueuePostAbbort | Bricht Warten eines sendenden Tasks (höchste wartende Prio) an einer Queue ab |
OS_QueuePend | Wartet auf Daten aus einer Queue |
OS_QueuePendAbbort | Bricht Warten eines empfangenden Tasks (höchste wartende Prio) an einer Queue ab |
OS_QueueClear | Löscht alle Daten in einer Queue |
Pipes: | |
OS_PipeInit | Anlegen einer Pipe |
OS_PipeInfo | Informationen über eine Pipe einholen |
OS_PipePost | Sendet Daten in eine Pipe |
OS_PipeFrontPost | Sendet Daten an den Anfang einer Pipe |
OS_PipePostAbbort | Bricht Warten eines sendenden Tasks (höchste wartende Prio) an einer Pipe ab |
OS_PipePend | Wartet auf Daten aus einer Pipe |
OS_PipePendAbbort | Bricht Warten eines empfangenden Tasks (höchste wartende Prio) an einer Pipe ab |
OS_PipeClear | Löscht alle Daten in einer Pipe |
Semaphores: | |
OS_SemInit | Anlegen einer Semaphore |
OS_SemAccept | wartet auf Ereignis und gibt Anzahl zurück |
OS_SemPost | Freigabe einer belegten Semaphore / setzt Ereignis |
OS_SemPend | Belegt eine Semaphore / wartet auf Ereignis |
OS_SemPendAbbort | Bricht Warten eines Tasks (höchste wartende Prio) an einer Semaphore ab |
OS_SemClear | Löscht Semaphoren-Counter |
Mutexes: | |
OS_MutexCreate | Anlegen einer Mutex |
OS_MutexPost | gibt Mutex wieder frei |
OS_MutexPend | Besetzt die Mutex |
OS_MutexPendAbbort | Bricht Warten eines Tasks (höchste wartende Prio) an einer Mutex ab |
Event-Groups: | |
OS_EvgInit | Anlegen einer Eventgruppe |
OS_EvgPost | Setzt ein/mehrere Events einer Evengruppe |
OS_EvgPend | Wartet auf das Eintreffen eines oder mehrere Events einer Eventgruppe |
OS_EvgPendAbbort | Bricht Warten eines Tasks (höchste wartende Prio) an einer Eventgruppe ab |
Timer-Service: | |
OS_TimerCreate | Anlegen eines Timers |
OS_TimerDelete | Löschen eines angelegten Timers |
OS_TimerStart | (Re-)Starten eines angelegten Timers |
OS_TimerStop | Stoppen eines angelegten Timers |
OS_TimerGetState | gibt den Status eines angelegten Timers zurück |
OS_TimerGetRemain | gibt die verbleibende Zeit eines laufenden Timers zurück |
System-Ticks: | |
OS_TimeSet | Setzt Ticker auf übergebenen Wert |
OS_TimeGet | Gibt aktuellen Ticker-Wert zurück |
Interrupts: | |
OS_IntEnter | Registrierung einer aufgerufenen ISR |
OS_IntExit | Ende einer aufgerufenen ISR |
History: | |
OS_HistoryPost | Schreibt Eintrag in History |
OS_HistoryRead | Gibt ersten History-Eintrag und löscht diesen in der Tabelle |
Error-Codes:
|
Name |
Decimal_Value |
OS_SUCCESS / OS_NO_ERR | 0 |
OS_TIMEOUT | 10 |
OS_MBOX_FULL | 20 |
OS_MBOX_NODATA | 21 |
OS_Q_FULL | 30 |
OS_Q_NODATA | 31 |
OS_Q_CLEAR | 32 |
OS_PRIO_EXIST | 40 |
OS_TASK_NOT_EXIST | 41 |
OS_SEM_ERR | 50 |
OS_SEM_NODATA | 51 |
OS_SEM_OVF | 52 |
OS_MUX_ERR | 55 |
OS_MUX_NOACC | 56 |
OS_MUX_USED | 57 |
OS_P_FULL | 60 |
OS_P_NODATA | 61 |
OS_P_CLEAR | 62 |
OS_P_LEN_ERR | 63 |
OS_MEM_ERR | 70 |
OS_MEM_OVF | 71 |
OS_EVG_ERR | 80 |
OS_EVG_NOE | 81 |
OS_HIS_END | 90 |
OS_SUSPEND_IDLE | 100 |
OS_PRIO_INVALID | 101 |
OS_TIME_NOT_DLY | 102 |
OS_TASK_SUSP_PRIO | 103 |
OS_TASK_NOT_SUSP | 104 |
OS_TASK_NOT_RDY | 105 |
OS_TMR_NO_TIME | 106 |
OS_TMR_NOT_EXIST | 107 |
Tip zu ASM-ISR's:
Um Assembler-ISR's im pC/OS manuell zu integrieren, muß bei Einsprung in die ISR folgender Startcode implementiert werden: (Beispiel x86)
und am Ende der ISR: