Basics of Embedded μC/OS-II System

Wu Jianying Microcontroller Development Board Address

Shop:Wu Jianying’s Shop

Address:https://item.taobao.com/item.htm?_u=ukgdp5a7629&id=524088004171

Basics of Embedded μC/OS-II System

μC/OS-II maintains all information of an event control block through the OS_EVENT data structure defined in uCOS_II.H

[Program Listing L6.1], which is the event control block ECB mentioned at the beginning of this chapter. This structure not only includes the definition of the event itself, such as the counter for the semaphore, a pointer to the mailbox, and an array of pointers pointing to the message queue, but also defines a list of all tasks waiting for that event.

typedef struct {

void *OSEventPtr; /* Pointer to the message or message queue */

INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* Waiting task list */

INT16U OSEventCnt; /* Counter (when the event is a semaphore) */

INT8U OSEventType; /* Event type */

INT8U OSEventGrp; /* Group of waiting tasks */

} OS_EVENT;

.OSEventPtr pointer is used only when the defined event is a mailbox or message queue. When the defined event is a mailbox, it points to a message, and when the defined event is a message queue, it points to a data structure. For details, see Section 6.06 Message Mailbox and Section 6.07 Message Queue.

.OSEventTbl[] and .OSEventGrp are similar to the previously mentioned OSRdyTbl[] and OSRdyGrp, except that the former includes the tasks waiting for a certain event, while the latter includes the tasks that are in a ready state in the system. (See Section 3.04 Ready Table)

.OSEventCnt, when the event is a semaphore, .OSEventCnt is a counter for the semaphore. (See Section 6.05 Semaphore).

e .OSEventType defines the specific type of the event. It can be one of a semaphore (OS_EVENT_SEM), a mailbox (OS_EVENT_TYPE_MBOX), or a message queue (OS_EVENT_TYPE_Q). The user must call the corresponding system function based on the specific value of this field to ensure the correctness of the operations performed on it.

The following code inserts a task into the waiting task list of the event.

Program Listing L6.2 – Insert a task into the waiting task list of the event

pevent->OSEventGrp |= OSMapTbl[prio >> 3];

pevent->OSEventTbl[prio >> 3] |= OSMapTbl[prio & 0x07];

Program Listing L6.3 – Remove a task from the waiting task list

if ((pevent->OSEventTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) {

pevent->OSEventGrp &= ~OSMapTbl[prio >> 3];

}

Program Listing L6.4 – Find the highest priority task in the waiting task list

y = OSUnMapTbl[pevent->OSEventGrp];

x = OSUnMapTbl[pevent->OSEventTbl[y]];

prio = (y << 3) + x;

void OSEventWaitListInit (OS_EVENT *pevent)

{

INT8U i;

pevent->OSEventGrp = 0x00;

for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {

pevent->OSEventTbl[i] = 0x00;

}

}

Program Listing L6.6 – Make a task ready

void OSEventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)

{

OS_TCB *ptcb;

INT8U x;

INT8U y;

INT8U bitx;

INT8U bity;

INT8U prio;

y = OSUnMapTbl[pevent->OSEventGrp]; (1)

bity = OSMapTbl[y]; (2)

x = OSUnMapTbl[pevent->OSEventTbl[y]]; (3)

bitx = OSMapTbl[x]; (4)

prio = (INT8U)((y << 3) + x); (5)

if ((pevent->OSEventTbl[y] &= ~bitx) == 0) { (6)

pevent->OSEventGrp &= ~bity;

}

ptcb = OSTCBPrioTbl[prio]; (7)

ptcb->OSTCBDly = 0; (8)

ptcb->OSTCBEventPtr = (OS_EVENT *)0; (9)

#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN

ptcb->OSTCBMsg = msg; (10)

#else

msg = msg;

#endif

ptcb->OSTCBStat &= ~msk; (11)

if (ptcb->OSTCBStat == OS_STAT_RDY) { (12)

OSRdyGrp |= bity; (13)

OSRdyTbl[y] |= bitx;

}

}

Program Listing L6.7 – Make a task enter the waiting state

void OSEventTaskWait (OS_EVENT *pevent)

{

OSTCBCur->OSTCBEventPtr = pevent; (1)

if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { (2)

OSRdyGrp &= ~OSTCBCur->OSTCBBitY;

}

pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; (3)

pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;

}

Program Listing L6.8 – Make a task ready due to timeout

void OSEventTO (OS_EVENT *pevent)

{

if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0)

{ (1)

pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY;

}

OSTCBCur->OSTCBStat = OS_STAT_RDY; (2)

OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; (3)

}

Semaphore

The semaphore in μC/OS-II consists of two parts: one is the count value of the semaphore, which is a 16-bit unsigned integer (between 0 and 65,535); the other is a waiting task table composed of tasks waiting for the semaphore. The user needs to set the OS_SEM_EN switch constant to 1 in OS_CFG.H so that μC/OS-II can support semaphores. The semaphore in μC/OS-II consists of two parts: one is the count value of the semaphore, which is a 16-bit unsigned integer (between 0 and 65,535); the other is a waiting task table composed of tasks waiting for the semaphore. The user needs to set the OS_SEM_EN switch constant to 1 in OS_CFG.H so that μC/OS-II can support semaphores.

Program Listing L6.9 – Create a semaphore

OS_EVENT *OSSemCreate (INT16U cnt)

{

OS_EVENT *pevent;

OS_ENTER_CRITICAL();

pevent = OSEventFreeList; (1)

if (OSEventFreeList != (OS_EVENT *)0) { (2)

OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;

}

OS_EXIT_CRITICAL();

if (pevent != (OS_EVENT *)0) { (3)

pevent->OSEventType = OS_EVENT_TYPE_SEM; (4)

pevent->OSEventCnt = cnt; (5)

OSEventWaitListInit(pevent); (6)

}

return (pevent); (7)

}

Program Listing L6.10 – Wait for a semaphore

void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)

{

OS_ENTER_CRITICAL();

if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1)

OS_EXIT_CRITICAL();

*err = OS_ERR_EVENT_TYPE;

}

if (pevent->OSEventCnt > 0) { (2)

pevent->OSEventCnt–; (3)

OS_EXIT_CRITICAL();

*err = OS_NO_ERR;

6-15

} else if (OSIntNesting > 0) { (4)

OS_EXIT_CRITICAL();

*err = OS_ERR_PEND_ISR;

} else {

OSTCBCur->OSTCBStat |= OS_STAT_SEM; (5)

OSTCBCur->OSTCBDly = timeout; (6)

OSEventTaskWait(pevent); (7)

OS_EXIT_CRITICAL();

OSSched(); (8)

OS_ENTER_CRITICAL();

if (OSTCBCur->OSTCBStat & OS_STAT_SEM) { (9)

OSEventTO(pevent); (10)

OS_EXIT_CRITICAL();

*err = OS_TIMEOUT;

} else {

OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; (11)

OS_EXIT_CRITICAL();

*err = OS_NO_ERR;

}

}

Program Listing L6.11 – Post a semaphore

INT8U OSSemPost (OS_EVENT *pevent)

{

OS_ENTER_CRITICAL();

if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1)

OS_EXIT_CRITICAL();

return (OS_ERR_EVENT_TYPE);

}

if (pevent->OSEventGrp) { (2)

OSEventTaskRdy(pevent, (void *)0, OS_STAT_SEM); (3)

OS_EXIT_CRITICAL();

OSSched(); (4)

return (OS_NO_ERR);

} else {

if (pevent->OSEventCnt < 65535) {

pevent->OSEventCnt++; (5)

OS_EXIT_CRITICAL();

return (OS_NO_ERR);

} else {

OS_EXIT_CRITICAL();

return (OS_SEM_OVF);

}

}

}

Program Listing L6.12 – Wait for a semaphore without blocking

INT16U OSSemAccept (OS_EVENT *pevent)

{

INT16U cnt;

OS_ENTER_CRITICAL();

if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1)

OS_EXIT_CRITICAL();

return (0);

}

cnt = pevent->OSEventCnt; (2)

if (cnt > 0) { (3)

pevent->OSEventCnt–; (4)

}

OS_EXIT_CRITICAL();

return (cnt); (5)

}

Program Listing L6.13 – Query the status of a semaphore

INT8U OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata)

{

INT8U i;

INT8U *psrc;

INT8U *pdest;

OS_ENTER_CRITICAL();

if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { (1)

OS_EXIT_CRITICAL();

return (OS_ERR_EVENT_TYPE);

}

pdata->OSEventGrp = pevent->OSEventGrp; (2)

psrc = &pevent->OSEventTbl[0];

pdest = &pdata->OSEventTbl[0];

for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {

*pdest++ = *psrc++;

}

pdata->OSCnt = pevent->OSEventCnt; (3)

OS_EXIT_CRITICAL();

return (OS_NO_ERR);

}

Program Listing L6.14 – Create a mailbox

OS_EVENT *OSMboxCreate (void *msg)

{

OS_EVENT *pevent;

OS_ENTER_CRITICAL();

pevent = OSEventFreeList;

if (OSEventFreeList != (OS_EVENT *)0) {

OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;

}

OS_EXIT_CRITICAL();

if (pevent != (OS_EVENT *)0) {

pevent->OSEventType = OS_EVENT_TYPE_MBOX; (1)

pevent->OSEventPtr = msg; (2)

OSEventWaitListInit(pevent);

}

return (pevent); (3)

}

Program Listing L6.15 – Wait for a message in a mailbox

void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)

{

void *msg;

OS_ENTER_CRITICAL();

if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1)

OS_EXIT_CRITICAL();

*err = OS_ERR_EVENT_TYPE;

return ((void *)0);

}

msg = pevent->OSEventPtr;

if (msg != (void *)0) { (2)

pevent->OSEventPtr = (void *)0; (3)

OS_EXIT_CRITICAL();

*err = OS_NO_ERR;

} else if (OSIntNesting > 0) { (4)

OS_EXIT_CRITICAL();

*err = OS_ERR_PEND_ISR;

} else {

OSTCBCur->OSTCBStat |= OS_STAT_MBOX; (5)

OSTCBCur->OSTCBDly = timeout;

OSEventTaskWait(pevent);

OS_EXIT_CRITICAL();

OSSched();

OS_ENTER_CRITICAL();

if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) { (6)

OSTCBCur->OSTCBMsg = (void *)0;

OSTCBCur->OSTCBStat = OS_STAT_RDY;

OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;

OS_EXIT_CRITICAL();

*err = OS_NO_ERR;

} else if (OSTCBCur->OSTCBStat & OS_STAT_MBOX) { (7)

OSEventTO(pevent); (8)

OS_EXIT_CRITICAL();

msg = (void *)0; (9)

*err = OS_TIMEOUT;

} else {

msg = pevent->OSEventPtr; (10)

pevent->OSEventPtr = (void *)0;

(11)

OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; (12)

OS_EXIT_CRITICAL();

*err = OS_NO_ERR;

}

}

return (msg);

}

Program Listing L6.16 – Send a message to a mailbox

INT8U OSMboxPost (OS_EVENT *pevent, void *msg)

{

OS_ENTER_CRITICAL();

if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1)

OS_EXIT_CRITICAL();

return (OS_ERR_EVENT_TYPE);

}

if (pevent->OSEventGrp) { (2)

OSEventTaskRdy(pevent, msg, OS_STAT_MBOX); (3)

OS_EXIT_CRITICAL();

OSSched(); (4)

return (OS_NO_ERR);

} else {

if (pevent->OSEventPtr != (void *)0) { (5)

OS_EXIT_CRITICAL();

return (OS_MBOX_FULL);

} else {

pevent->OSEventPtr = msg; (6)

OS_EXIT_CRITICAL();

return (OS_NO_ERR);

}

}

}

Program Listing L6.16 – Send a message to a mailbox

INT8U OSMboxPost (OS_EVENT *pevent, void *msg)

{

OS_ENTER_CRITICAL();

if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1)

OS_EXIT_CRITICAL();

return (OS_ERR_EVENT_TYPE);

}

if (pevent->OSEventGrp) { (2)

OSEventTaskRdy(pevent, msg, OS_STAT_MBOX); (3)

OS_EXIT_CRITICAL();

OSSched(); (4)

return (OS_NO_ERR);

} else {

if (pevent->OSEventPtr != (void *)0) { (5)

OS_EXIT_CRITICAL();

return (OS_MBOX_FULL);

} else {

pevent->OSEventPtr = msg; (6)

OS_EXIT_CRITICAL();

return (OS_NO_ERR);

}

}

}

Program Listing L6.17 – Non-blocking request for a message from a mailbox

void *OSMboxAccept (OS_EVENT *pevent)

{

void *msg;

OS_ENTER_CRITICAL();

if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1)

OS_EXIT_CRITICAL();

return ((void *)0);

}

msg = pevent->OSEventPtr; (2)

if (msg != (void *)0) { (3)

pevent->OSEventPtr = (void *)0; (4)

}

OS_EXIT_CRITICAL();

return (msg); (5)

}

Program Listing L6.18 – Query the status of a mailbox

INT8U OSMboxQuery (OS_EVENT *pevent, OS_MBOX_DATA *pdata)

{

INT8U i;

6-25

INT8U *psrc;

INT8U *pdest;

OS_ENTER_CRITICAL();

if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { (1)

OS_EXIT_CRITICAL();

return (OS_ERR_EVENT_TYPE);

}

pdata->OSEventGrp = pevent->OSEventGrp; (2)

psrc = &pevent->OSEventTbl[0];

pdest = &pdata->OSEventTbl[0];

for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {

*pdest++ = *psrc++;

}

pdata->OSMsg = pevent->OSEventPtr; (3)

OS_EXIT_CRITICAL();

return (OS_NO_ERR);

}

Program Listing L6.19 – Use a mailbox as a binary semaphore

OS_EVENT *MboxSem;

void Task1 (void *pdata)

{

INT8U err;

for (;;) {

OSMboxPend(MboxSem, 0, &err); /* Obtain access to the resource */

6-26

.

/* Task has obtained the semaphore and is accessing the resource */

.

OSMboxPost(MboxSem, (void*)1); /* Release access to the resource */

}

}

Program Listing L6.21 – Create a message queue

OS_EVENT *OSQCreate (void **start, INT16U size)

{

OS_EVENT *pevent;

OS_Q *pq;

OS_ENTER_CRITICAL();

pevent = OSEventFreeList; (1)

if (OSEventFreeList != (OS_EVENT *)0) {

OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; (2)

}

OS_EXIT_CRITICAL();

if (pevent != (OS_EVENT *)0) {

OS_ENTER_CRITICAL();

pq = OSQFreeList; (3)

if (OSQFreeList != (OS_Q *)0) {

OSQFreeList = OSQFreeList->OSQPtr;

}

OS_EXIT_CRITICAL();

if (pq != (OS_Q *)0) {

pq->OSQStart = start; (4)

pq->OSQEnd = &start[size];

pq->OSQIn = start;

pq->OSQOut = start;

pq->OSQSize = size;

pq->OSQEntries = 0;

pevent->OSEventType = OS_EVENT_TYPE_Q; (5)

pevent->OSEventPtr = pq; (6)

OSEventWaitListInit(pevent); (7)

} else {

OS_ENTER_CRITICAL();

pevent->OSEventPtr = (void *)OSEventFreeList; (8)

OSEventFreeList = pevent;

OS_EXIT_CRITICAL();

pevent = (OS_EVENT *)0;

}

}

return (pevent); (9)

}

9.03 OS_CPU.H File

The OS_CPU.H file contains the definitions of constants, macros, and structures related to the processor. Program Listing L9.2 is the content of the OS_CPU.H file written for 80×86.

9.04 OS_CPU_A.ASM

The porting of μC/OS-II requires users to rewrite four functions in OS_CPU_A.ASM:

OSStartHighRdy()

OSCtxSw()

OSIntCtxSw()

OSTickISR()

9.04.01 OSStartHighRdy()

This function is called by the OSStart() function and is responsible for running the highest priority ready task. Before calling OSStart(), the user must first call OSInit() and has created at least one task (refer to the functions OSTaskCreate() and OSTaskCreateExt()). The OSStartHighRdy() function defaults the pointer OSTCBHighRdy to point to the task control block (OS_TCB) of the highest priority ready task (before this, OSTCBHighRdy has been set by OSStart()). Figure F9.3 shows the stack structure of the task created by the function OSTaskCreate() or OSTaskCreateExt(). It is clear that OSTCBHighRdy->OSTCBStkPtr points to the top of the task stack.

Program Listing L9.3 OSStartHighRdy().

_OSStartHighRdy PROC FAR

MOV AX, SEG _OSTCBHighRdy ; Load DS

MOV DS, AX ;

LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr

(1)

MOV SS, ES:[BX+2] ;

MOV SP, ES:[BX+0] ;

;

POP DS ; Restore task environment (2)

POP ES ; (3)

POPA ; (4)

;

IRET ; Run task (5)

_OSStartHighRdy ENDP

9.05 OS_CPU_C.C

The porting of μC/OS-II requires users to rewrite six functions in OS_CPU_C.C:

OSTaskStkInit()

OSTaskCreateHook()

OSTaskDelHook()

OSTaskSwHook()

OSTaskStatHook()

OSTimeTickHook()

The return value of OSTaskResume() may be one of the following:

? OS_NO_ERR: Function call succeeded.

? OS_TASK_RESUME_PRIO: The task to be awakened does not exist.

? OS_TASK_NOT_SUSPENDED: The task to be awakened is not in a suspended state.

? OS_PRIO_INVALID: The priority specified by the parameter is greater than or equal to OS_LOWEST_PRIO.

OS_MAX_EVENTS defines the maximum number of event control blocks in the system. Each message mailbox, message queue, and semaphore in the system requires an event control block. For example, if the system has 10 message mailboxes, 5 message queues, and 3 semaphores, then OS_MAX_EVENTS should be at least 18. As long as the program uses message mailboxes, message queues, or semaphores, OS_MAX_EVENTS should be set to a minimum of 2.

If you like this article, please give it a thumbs up

Basics of Embedded μC/OS-II System

Basics of Embedded μC/OS-II System

Technology Comes from Accumulation, Success Comes from Persistence

——Microcontroller Detailed Explanation by Wu Jianying

Leave a Comment