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.

Embedded Systems Programming Magazine(1992) - code area


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.



pCOS_183a.zip
download pC/OS 1.83a (as ZIP)


Releases:

1.00a

Messagebox und Queue geändert sowie Pipe, Eventgroup und dyn. Memorymanagement hinzugefügt.
* Port für Am188ES erstellt

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).
Einige BugFixes aus 1.01 bis 1.09 übernommen.

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).
Vorab-Implementation einer Wartezeit für Sender (OS_.....Post).
Nicht-warten für Sender und Empfänger mittels OS_NO_SUSP implementiert.
Anlegen von Queue und Pipe (speziell: Zeiger auf Kernel-buffer) überarbeitet.

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.
OS_QueueInfo und OS_PipeInfo hinzugefügt.

1.00f

Bugfix innerhalb der Event-Gruppen.
OS_SemAccept hinzugefügt.

1.00g

Dual-Source für MSC-8.00 und Keil-C51 generiert. OS_CFG implementiert. Bugfix in Semaphoren.
* Port für 8051 hinzugefügt

1.00h

MUTEXes hinzugefügt, Eventgruppen richtig implementiert und Code-cleanup. Cleanup der Ports.
* Port für Rabbit 2000/3000 hinzugefügt
* Port für Philips-XA hinzugefügt

1.40a

Name auf pC/OS geändert und Revisionsnummer überarbeitet, einige C-fixes.
* Port für ATMega128 hinzugefügt
* Port für AT91SAM7Sxx hinzugefügt

1.41a

Implementation von OS_STK_TYPE für Prozessor-konformen Stack incl. Update aller bisherigen Ports.
Keil-Variante des AT91SAM7Sxx-Ports (instabil) hinzugefügt

1.50c

Komplettes Update zu U08..U32 Typen.
Implementation von OS_PACKED, OS_PACKED_ATTR und align(x)-Korrekturen.
Kleine Korrektur in OS_TaskSuspend().
* Port für LPC214x hinzugefügt
* Port für LPC236x hinzugefügt
* Port für STR71x hinzugefügt

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.
* Port für Cortex-M3 LM3S811 hinzugefügt

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
* Port für Cortex-M3 STM32 hinzugefügt

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.
Einige neue API-Funktionen hinzugefügt

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/..)
* Port für PIC32 hinzugefügt
* Port für MSP430 hinzugefügt

1.68a

OS_EvgPendAbbort() hinzugefügt
* Port für AVR32 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
* Port für ATMega32 hinzugefügt

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
* Port für Cortex-M0 LPC11xx hinzugefügt
* Port für Cortex-M3 LPC13xx hinzugefügt
* Port für Cortex-M3 LPC17xx hinzugefügt
* Port für Cortex-M3 AT91SAM3xxx hinzugefügt



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)

EXTRN C OSIntEnter : NEAR EXTRN C OSIntExit : NEAR ... ISR: pusha ; save all used register push es call OSIntEnter ; C-function to register INT-level ...

und am Ende der ISR:

... call OSIntExit ; C-function to unregister INT-Level ; and perform a context-switch is needed pop es ; restore all used register popa iret ; return