Download presentation
Presentation is loading. Please wait.
1
Introduction to uC/OS-II
2
uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 06:10 /27
3
Real-Time Systems Concepts
Multitasking Kernel Scheduling Mutual Exclusion Message Passing Interrupt 06:10 /27
4
Foreground/Background Systems
Interrupt Level Task Level 06:10 /27
5
Multitasking Multitasking Related issues
A process of scheduling and switching CPU between several tasks. Related issues Context Switch (Task Switch) Resource Sharing Critical Section 06:10 /27
6
Multitasking 06:10 /27
7
Task A task, or called a thread, a simple program that thinks it has the CPU all to itself. Each task is assigned a priority, its own set of CPU registers, and its own stack area . Each task typically is an infinite loop that can be in any one of five states. Ready, Running, Waiting, ISR, Dormant 06:10 /27
8
Task States 06:10 /27
9
Non-Preemptive Kernel
Kernel is the part of a multitasking system responsible for the management of tasks. Context switching is the fundamental service of a kernel. Non-Preemptive Kernel v.s. Preemptive Kernel 06:10 /27
10
Non-Preemptive Kernel
Cooperative multitasking A non-preemptive kernel allows each task to run until it voluntarily gives up control of the CPU. An ISR can make a higher priority task ready to run, but the ISR always returns to the interrupted task. Linux 2.4 is non-preemptive. Linux 2.6 is preemptive. 06:10 /27
11
Non-Preemptive Kernel
06:10 /27
12
Preemptive Kernel A preemptive kernel always executes the highest priority task that is ready to run. µC/OS-II and most commercial real-time kernels are preemptive. Much better response time. Should not use non-reentrant functions, unless the functions are mutual exclusive. 06:10 /27
13
Preemptive Kernel 06:10 /27
14
Function Reentrancy A reentrant function is a function that can be used by more than one task without fear of data corruption. Reentrant functions either use local variables or protected global variables. OS_ENTER_CRITICAL() OS_EXIT_CRITICAL() 06:10 /27
15
Function Reentrancy Non-Reentrant Function Reentrant Function
static int Temp; void swap(int *x, int *y) { Temp = *x; *x = *y; *y = Temp; } Reentrant Function void strcpy(char *dest, char *src) { while (*dest++ = *src++) { ; } *dest = NULL; } 06:10 /27
16
Scheduling Round-Robin Scheduling Task Priority Assignment
Tasks executed sequentially Task Priority Assignment Static priority Rate Monotonic (RM) Dynamic priority Earliest-Deadline First (EDF) Time-Derived Scheduling Pinwheel 06:10 /27
17
Round-Robin Kernel gives control to next task if the current task
has no work to do completes reaches the end of time-slice Not supported in uC/OS-II O(1) priority preemptive scheduling 06:10 /27
18
Priority Inversion Problem
06:10 /27
19
Priority Inheritance 06:10 /27
20
Mutual Exclusion Protected shared data of processes.
Exclusive access implementation Disabling and enabling interrupts Test-and-Set Disabling and enabling scheduler Semaphores Simple Semaphore Counting Semaphore Deadlock – set timeout for a semaphore 06:10 /27
21
Using Semaphore 06:10 /27
22
Synchronization Synchronization mechanism is used between tasks or task to ISR. Unilateral rendezvous Bilateral rendezvous 06:10 /27
23
Unilateral rendezvous
06:10 /27
24
Bilateral rendezvous 06:10 /27
25
Event Flags 06:10 /27
26
Message Passing Intertask Communication
Using global variables or sending messages Only communicate to ISR through global variables Tasks are not aware when the global variables is changed unless task polls the content. 06:10 /27
27
Message Mailboxes A Message Mailbox, also called a message exchange, is typically a pointer size variable. Operations Initial POST PEND necessary to wait for the message being deposited ACCEPT Acknowledgement 06:10 /27
28
Message Queues A message queue is used to send one or more messages to a task. A message queue is an array of message mailboxes. Generally, FIFO is used. µC/OS-II allows a task to get messages Last-In-First-Out (LIFO). 06:10 /27
29
Interrupt An interrupt is a hardware mechanism used to inform the CPU that an asynchronous event has occurred. Interrupt Latency Interrupt Response Interrupt Recovery 06:10 /27
30
Interrupt Nesting 06:10 /27
31
Interrupt Latency Disabling interrupts affects interrupt latency.
All real-time systems disable interrupts to manipulate critical sections of code. Re-enable interrupts when the critical section has executed. Interrupt latency = Maximum time to disable interrupts + Time to start the first instruction in ISR. 06:10 /27
32
Interrupt Response Interrupt Response means time between the reception of the interrupt and the start of the user code which will handle the interrupt. Interrupt response = Interrupt latency + Time to save CPU context The worst case for interrupt response is adopted. 06:10 /27
33
Interrupt Recovery Interrupt recovery is defined as the time required for the processor to return to the interrupted code. Interrupt recovery = Time to determine if a high priority task is ready + Time to restore the CPU context of the highest priority task + time of executing return-from-interrupt. 06:10 /27
34
Non-preemptive Kernel
06:10 /27
35
Preemptive Kernel 06:10 /27
36
Non-Maskable Interrupts (NMIs)
Service the most important time-critical ISR Can not be disabled Interrupt latency = Time to execution the longest instruction + Time to start execution the NMI ISR Interrupt response = Interrupt latency + Time to save CPU context Interrupt recovery = Time to restore CPU context + time of executing return-from-interrupt 06:10 /27
37
Clock Tick A periodical interrupt Be viewed as heartbeat
Application specific and is generally between 10ms and 200ms Faster timer causes higher overhead Delay problem should be considered in real-time kernel. 06:10 /27
38
Delaying a Task for 1 Tick
Tick Interrupt Tick ISR All higher priority tasks Delayed Task t1 t2 t3 20 mS (6 mS) (19 mS) (27 mS) Call to delay 1 tick (20 mS) 06:10 /27
39
Clock Tick Solve the problem
Increase the clock rate of your microprocessor. Increase the time between tick interrupts. Rearrange task priorities. Avoid using floating-point math. Get a compiler that performs better code optimization. Write time-critical code in assembly language. If possible, upgrade to a faster microprocessor in the same family. 06:10 /27
40
Memory Requirement Be careful to avoid large RAM requirement
Large local arrays Nested/Recursive function Interrupt nesting Stack used by libraries Too many function arguments 06:10 /27
41
Real-Time Kernels Real-time OS allows real-time application to be designed and expanded easily. The use of an RTOS simplifies the design process by splitting the application into separate tasks. Real-time kernel requires more ROM/RAM and 2 to 4 percent overhead. 06:10 /27
42
uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 06:10 /27
43
Kernel Structure Task Control Blocks Ready List Task Scheduling
Interrupt under uC/OS-II 06:10 /27
44
Critical Sections Archive this by disabling interrupt OS_CPU.H
OS_ENTER_CRITICAL() OS_EXIT_CRITICAL() 06:10 /27
45
Tasks Up to 64 tasks Two tasks for system use(idle and statistic)
Priorities 0, 1, 2, 3, OS_LOWEST_PRIO-3, OS_LOWEST_PRIO-2, OS_LOWEST_PRIO-1, OS_LOWEST_PRIO for future use The lower the priority number, the higher the priority of the task. The task priority is also the task identifier. 06:10 /27
46
Task States 06:10 /27
47
Task Control Blocks 06:10 /27 typedef struct os_tcb {
OS_STK *OSTCBStkPtr; #if OS_TASK_CREATE_EXT_EN void *OSTCBExtPtr; OS_STK *OSTCBStkBottom; INT32U OSTCBStkSize; INT16U OSTCBOpt; INT16U OSTCBId; #endif struct os_tcb *OSTCBNext; struct os_tcb *OSTCBPrev; #if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN OS_EVENT *OSTCBEventPtr; #if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN void *OSTCBMsg; INT16U OSTCBDly; INT8U OSTCBStat; INT8U OSTCBPrio; INT8U OSTCBX; INT8U OSTCBY; INT8U OSTCBBitX; INT8U OSTCBBitY; #if OS_TASK_DEL_EN BOOLEAN OSTCBDelReq; } OS_TCB; 06:10 /27
48
OS_TCB Lists TCBs store in OSTCBTbl[]
All TCBs are initialized and linked when uC/OS-II is initialized 06:10 /27
49
Ready List 06:10 /27
50
OSRdyGrp and OSRdyTbl[]
Bit 0 in OSRdyGrp is 1 when any bit in OSRdyTbl[0] is 1. Bit 1 in OSRdyGrp is 1 when any bit in OSRdyTbl[1] is 1. Bit 2 in OSRdyGrp is 1 when any bit in OSRdyTbl[2] is 1. Bit 3 in OSRdyGrp is 1 when any bit in OSRdyTbl[3] is 1. Bit 4 in OSRdyGrp is 1 when any bit in OSRdyTbl[4] is 1. Bit 5 in OSRdyGrp is 1 when any bit in OSRdyTbl[5] is 1. Bit 6 in OSRdyGrp is 1 when any bit in OSRdyTbl[6] is 1. Bit 7 in OSRdyGrp is 1 when any bit in OSRdyTbl[7] is 1. 06:10 /27
51
Making a Task Ready to Run
OSRdyGrp |= OSMapTbl[prio >> 3]; OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07]; Index Bit mask (Binary) 1 2 3 4 5 6 7 Task’s Priority Y X 06:10 /27
52
Removing a Task from the Ready List
if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) OSRdyGrp &= ~OSMapTbl[prio >> 3]; 06:10 /27
53
Finding the Highest Priority Task
OSUnMapTbl[] … y = OSUnMapTbl[OSRdyGrp]; x = OSUnMapTbl[OSRdyTbl[y]]; prio = (y << 3) + x; INT8U const OSUnMapTbl[] = { 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; 06:10 /27
54
Task Scheduler void OSSched (void) { INT8U y; OS_ENTER_CRITICAL();
if ((OSLockNesting | OSIntNesting) == 0) { (1) y = OSUnMapTbl[OSRdyGrp]; (2) OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); (2) if (OSPrioHighRdy != OSPrioCur) { (3) OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; (4) OSCtxSwCtr++; (5) OS_TASK_SW(); (6) } OS_EXIT_CRITICAL(); 06:10 /27
55
Locking The Scheduler void OSSchedLock (void) { if (OSRunning == TRUE) { OS_ENTER_CRITICAL(); OSLockNesting++; OS_EXIT_CRITICAL(); } } 06:10 /27
56
Unlocking The Scheduler
void OSSchedUnlock (void) { if (OSRunning == TRUE) { OS_ENTER_CRITICAL(); if (OSLockNesting > 0) { OSLockNesting--; if ((OSLockNesting | OSIntNesting) == 0) { (1) OS_EXIT_CRITICAL(); OSSched(); (2) } else { OS_EXIT_CRITICAL(); } } else { OS_EXIT_CRITICAL(); } } } 06:10 /27
57
Idle Task OSTaskIdle() Lowest priority, OS_LOWEST_PRIO
void OSTaskIdle (void *pdata) { pdata = pdata; for (;;) { OS_ENTER_CRITICAL(); OSIdleCtr++; OS_EXIT_CRITICAL(); } } 06:10 /27
58
Statistics Task 06:10 /27
59
Initializing the Statistic Task
void main (void) { OSInit(); /* Initialize uC/OS-II (1)*/ /* Install uC/OS-II's context switch vector */ /* Create your startup task (for sake of discussion, TaskStart()) (2)*/ OSStart(); /* Start multitasking (3)*/ } void TaskStart (void *pdata) { /* Install and initialize µC/OS-II’s ticker (4)*/ OSStatInit(); /* Initialize statistics task (5)*/ /* Create your application task(s) */ for (;;) { /* Code for TaskStart() goes here! */ } } 06:10 /27
60
Initializing the Statistic Task
void OSStatInit (void) { OSTimeDly(2); OS_ENTER_CRITICAL(); OSIdleCtr = 0L; OS_EXIT_CRITICAL(); OSTimeDly(OS_TICKS_PER_SEC); OSIdleCtrMax = OSIdleCtr; OSStatRdy = TRUE; } 06:10 /27
61
Statistics Task 06:10 /27 void OSTaskStat (void *pdata) { INT32U run;
INT8S usage; pdata = pdata; while (OSStatRdy == FALSE) { (1) OSTimeDly(2 * OS_TICKS_PER_SEC); } for (;;) { OS_ENTER_CRITICAL(); OSIdleCtrRun = OSIdleCtr; run = OSIdleCtr; OSIdleCtr = 0L; OS_EXIT_CRITICAL(); if (OSIdleCtrMax > 0L) { usage = (INT8S)(100L - 100L * run / OSIdleCtrMax); (2) if (usage > 100) { OSCPUUsage = 100; } else if (usage < 0) { OSCPUUsage = 0; } else { OSCPUUsage = usage; OSTaskStatHook(); (3) OSTimeDly(OS_TICKS_PER_SEC); 06:10 /27
62
Interrupts under uC/OS-II
Be written in assembly language or C code with in-line assembly. YourISR: Save all CPU registers; (1) Call OSIntEnter() or, increment OSIntNesting directly; (2) Execute user code to service ISR; (3) Call OSIntExit(); (4) Restore all CPU registers; (5) Execute a return from interrupt instruction; 06:10 /27
63
Servicing an Interrupt
06:10 /27
64
Beginning an ISR void OSIntEnter (void) { OS_ENTER_CRITICAL(); OSIntNesting++; OS_EXIT_CRITICAL(); } 06:10 /27
65
Leaving an ISR void OSIntExit (void) { OS_ENTER_CRITICAL(); (1)
if ((--OSIntNesting | OSLockNesting) == 0) { (2) OSIntExitY = OSUnMapTbl[OSRdyGrp]; (3) OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]); if (OSPrioHighRdy != OSPrioCur) { OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; OSCtxSwCtr++; OSIntCtxSw(); (4) } OS_EXIT_CRITICAL(); 06:10 /27
66
Pseudo Code for Tick ISR
void OSTickISR(void) { Save processor registers; Call OSIntEnter() or increment OSIntNesting; Call OSTimeTick(); Call OSIntExit(); Restore processor registers; Execute a return from interrupt instruction; } 06:10 /27
67
Service a Tick void OSTimeTick (void) { OS_TCB *ptcb; OSTimeTickHook(); (1) ptcb = OSTCBList; (2) while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { (3) OS_ENTER_CRITICAL(); if (ptcb->OSTCBDly != 0) { if (--ptcb->OSTCBDly == 0) { if (!(ptcb->OSTCBStat & OS_STAT_SUSPEND)) { (5) OSRdyGrp |= ptcb->OSTCBBitY; (4) OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; } else { ptcb->OSTCBDly = 1; } } } ptcb = ptcb->OSTCBNext; OS_EXIT_CRITICAL(); } OS_ENTER_CRITICAL(); (7) OSTime++; (6) OS_EXIT_CRITICAL(); } 06:10 /27
68
uC/OS-II Initialization
Initialize variables and data structures Create the idle task OSTaskIdle() Create the statistic task OSTaskStat() 06:10 /27
69
After calling OSInit()
06:10 /27
70
Starting uC/OS-II void main (void) { OSInit(); /* Initialize uC/OS-II */ Create at least 1 task using either OSTaskCreate() or OSTaskCreateExt(); OSStart(); /* Start multitasking! OSStart() will not return */ } void OSStart (void) { INT8U y; INT8U x; if (OSRunning == FALSE) { y = OSUnMapTbl[OSRdyGrp]; x = OSUnMapTbl[OSRdyTbl[y]]; OSPrioHighRdy = (INT8U)((y << 3) + x); OSPrioCur = OSPrioHighRdy; OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; (1) OSTCBCur = OSTCBHighRdy; OSStartHighRdy(); (2) } } 06:10 /27
71
uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 06:10 /27
72
Task Management Task prototype Create a task Task stacks
Stack checking Delete a task Change a task’s priority Suspend a task Resume a task Query task information 06:10 /27
73
Task Prototype void YourTask (void *pdata) { for (;;) { /* USER CODE */ Call one of uC/OS-II’s services: /* USER CODE */ } } void YourTask (void *pdata) { /* USER CODE */ OSTaskDel(OS_PRIO_SELF); } 06:10 /27
74
OSTaskCreate() Check that the task priority is valid
Check that the task is unique Set up the task stack OSTaskStkInit() Obtain and initialize OS_TCB from TCB pool OSTCBInit() Call OSTaskCreateHook() to extend the functionality Call OSSched() when OSTaskCreate() is called from a task 06:10 /27
75
INT8U OSTaskCreate (void (. task)(void. pd), void. pdata, OS_STK
INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio) { void *psp; INT8U err; if (prio > OS_LOWEST_PRIO) { (1) return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { (2) OSTCBPrioTbl[prio] = (OS_TCB *)1; (3) OS_EXIT_CRITICAL(); (4) psp = (void *)OSTaskStkInit(task, pdata, ptos, 0); (5) err = OSTCBInit(prio, psp, (void *)0, 0, 0, (void *)0, 0); (6) if (err == OS_NO_ERR) { (7) OSTaskCtr++; (8) OSTaskCreateHook(OSTCBPrioTbl[prio]); (9) OS_EXIT_CRITICAL(); if (OSRunning) { (10) OSSched(); (11) } else { OSTCBPrioTbl[prio] = (OS_TCB *)0; (12) return (err); return (OS_PRIO_EXIST); OSTaskCreate() 06:10 /27
76
OSTaskCreateExt() More flexibility, at the expense of overhead
The first four arguments are the same as OSTaskCreate() id, assign a unique identifier for the task pbos, a pointer that can perform stack checking stk_size, specifies the size of the stack pext, a pointer to extend the OS_TCB opt, specifies whether stack checking is performed 06:10 /27
77
OSTaskCreateExt() INT8U OSTaskCreateExt (void (*task)(void *pd),
void *pdata, OS_STK *ptos, INT8U prio, INT16U id, OS_STK *pbos, INT32U stk_size, void *pext, INT16U opt) { void *psp; INT8U err; INT16U i; OS_STK *pfill; if (prio > OS_LOWEST_PRIO) { (1) return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { (2) OSTCBPrioTbl[prio] = (OS_TCB *)1; (3) OS_EXIT_CRITICAL(); (4) … return (err); } else { OS_EXIT_CRITICAL(); return (OS_PRIO_EXIST); OSTaskCreateExt() 06:10 /27
78
Task Stacks or Using malloc() to allocate stack space
Static OS_STK MyTaskStack[stack_size]; or OS_STK MyTaskStack[stack_size]; Using malloc() to allocate stack space OS_STK *pstk; pstk = (OS_STK *)malloc(stack_size); if (pstk != (OS_STK *)0) { /* Make sure malloc() had enough space */ Create the task; } 06:10 /27
79
Fragmentation 06:10 /27
80
OSTaskStkChk() Computes the amount of free stack space by “walking” from the bottom of the stack until a nonzero value is found 06:10 /27
81
Stack Checking 06:10 /27
82
OSTaskDel() Return to the DORMANT state
The code for the task will not be deleted Procedures Prevent from deleting and idle task Prevent from deleting a task from within an ISR Verify that the task to be deleted does exist Remove the OS_TCB Remove the task from ready list or other lists Set .OSTCBStat to OS_STAT_RDY Call OSDummy() Call OSTaskDelHook() Remove the OS_TCB from priority table Call OSSched() 06:10 /27
83
OSTaskDelReq() Tell the task that owns memory buffers or semaphore to delete itself Procedures Check the task’s priority If the task’s priority is OS_PRIO_SELF Return the flag of OSTCBDelReq Otherwise Set OSTCBDelReq of the task to OS_TASK_DEL_REQ 06:10 /27
84
OSTaskDelReq() Example
void RequestorTask (void *pdata) { for (;;) { /* Application code */ if (‘TaskToBeDeleted()’ needs to be deleted) { while (OSTaskDelReq(TASK_TO_DEL_PRIO) != OS_TASK_NOT_EXIST) { OSTimeDly(1); } } /* Application code */ } } void TaskToBeDeleted (void *pdata) { for (;;) { /* Application code */ if (OSTaskDelReq(OS_PRIO_SELF) == OS_TASK_DEL_REQ) { Release any owned resources; De-allocate any dynamic memory; OSTaskDel(OS_PRIO_SELF); } else { /* Application code */ } } } 06:10 /27
85
OSTaskChangePrio() Cannot change the priority of any idle task
Procedures Reserve the new priority by OSTCBPrioTbl[newprio] = (OS_TCB *) 1; Remove the task from the priority table Insert the task into new location of the priority table Change the OS_TCB of the task Call OSSched() 06:10 /27
86
OSTaskSuspend() Procedures Check the input priority
Remove the task from the ready list Set the OS_STAT_SUSPEND flag in OS_TCB Call OSSched() 06:10 /27
87
OSTaskResume() Procedures Check the input priority
Clear the OS_STAT_SUSPEND bit in the OSTCBStat field Set OSTCBDly to 0 Call OSSched() 06:10 /27
88
OSTaskQuery() INT8U OSTaskQuery (INT8U prio, OS_TCB *pdata) {
OS_TCB *ptcb; if (prio > OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { (1) return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); if (prio == OS_PRIO_SELF) { (2) prio = OSTCBCur->OSTCBPrio; if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) { (3) OS_EXIT_CRITICAL(); return (OS_PRIO_ERR); *pdata = *ptcb; (4) return (OS_NO_ERR); 06:10 /27
89
uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 06:10 /27
90
Time Management OSTimeDly() OSTimeDlyHMSM() OSTimeDlyResume()
OSTimeGet() OSTimeSet() 06:10 /27
91
OSTimeDly() void OSTimeDly (INT16U ticks) { if (ticks > 0) {
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { OSRdyGrp &= ~OSTCBCur->OSTCBBitY; } OSTCBCur->OSTCBDly = ticks; OSSched(); } } 06:10 /27
92
OSTimeDlyHMSM() INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U milli) { { INT32U ticks; INT16U loops; if (hours > 0 || minutes > 0 || seconds > 0 || milli > 0) { if (minutes > 59) { return (OS_TIME_INVALID_MINUTES); } if (seconds > 59) { return (OS_TIME_INVALID_SECONDS); if (milli > 999) { return (OS_TIME_INVALID_MILLI); ticks = (INT32U)hours * 3600L * OS_TICKS_PER_SEC + (INT32U)minutes * 60L * OS_TICKS_PER_SEC + (INT32U)seconds) * OS_TICKS_PER_SEC + OS_TICKS_PER_SEC * ((INT32U)milli + 500L / OS_TICKS_PER_SEC) / 1000L; loops = ticks / 65536L; ticks = ticks % 65536L; OSTimeDly(ticks); while (loops > 0) { OSTimeDly(32768); loops--; } return (OS_NO_ERR); } else { return (OS_TIME_ZERO_DLY); } 06:10 /27
93
OSTimeDlyResume() INT8U OSTimeDlyResume (INT8U prio) {
ptcb = (OS_TCB *)OSTCBPrioTbl[prio]; if (ptcb != (OS_TCB *)0) { if (ptcb->OSTCBDly != 0) { ptcb->OSTCBDly = 0; if (!(ptcb->OSTCBStat & OS_STAT_SUSPEND)) { OSRdyGrp |= ptcb->OSTCBBitY; OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; OSSched(); } return (OS_NO_ERR); } else { return (OS_TIME_NOT_DLY); } } else { return (OS_TASK_NOT_EXIST); } } 06:10 /27
94
OSTimeGet() & OSTimeSet()
INT32U OSTimeGet (void) { ticks = OSTime; return (ticks); } void OSTimeSet (INT32U ticks) { OSTime = ticks; 06:10 /27
95
uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 06:10 /27
96
Intertask Communication & Synchronization
OS_ENTER_CRITICAL() OS_EXIT_CRITICAL() OSSchedLock() OSSchedUnlock() Semaphore Message mailbox Message queue 06:10 /27
97
OS_ENTER_CRITICAL() & OS_EXIT_CRITICAL()
#define OS_CRITICAL_METHOD 2 #if OS_CRITICAL_METHOD == 1 #define OS_ENTER_CRITICAL() asm CLI #define OS_EXIT_CRITICAL() asm STI #endif #if OS_CRITICAL_METHOD == 2 #define OS_ENTER_CRITICAL() asm {PUSHF; CLI} #define OS_EXIT_CRITICAL() asm POPF CLI … STI 06:10 /27
98
Linux Example /* include/asm-i386/system.h */
/* interrupt control.. */ #define __save_flags(x) __asm__ __volatile__("pushfl ; popl %0":"=g" (x):) #define __restore_flags(x) __asm__ __volatile__("pushl %0; popfl": /* no output */ :"g" (x):"memory", "cc") #define __cli() __asm__ __volatile__("cli": : :"memory") #define __sti() __asm__ __volatile__("sti": : :"memory") /* For spinlocks etc */ #define local_irq_save(x) __asm__ __volatile__("pushfl; popl %0; cli":"=g" (x): :"memory") #define local_irq_restore(x) __restore_flags(x) #define local_irq_disable() __cli() #define local_irq_enable() __sti() 06:10 /27
99
Locking and Unlocking the Scheduler
void OSSchedLock (void) { if (OSRunning == TRUE) { OSLockNesting++; } } void OSSchedUnlock (void) { if (OSLockNesting > 0) { OSLockNesting--; if ((OSLockNesting | OSIntNesting) == 0) { OSSched(); } 06:10 /27
100
ECB (Event Control Block)
06:10 /27
101
Event Control Block typedef struct {
void *OSEventPtr; /* Ptr to message or queue structure */ INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* Wait list for event to occur */ INT16U OSEventCnt; /* Count (when event is a semaphore) */ INT8U OSEventType; /* Event type */ INT8U OSEventGrp; /* Group for wait list */ } OS_EVENT; 06:10 /27
102
List of Free ECBs 06:10 /27
103
Wait Queue Functions void OSEventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk) { y = OSUnMapTbl[pevent->OSEventGrp]; bity = OSMapTbl[y]; x = OSUnMapTbl[pevent->OSEventTbl[y]]; bitx = OSMapTbl[x]; prio = (INT8U)((y << 3) + x); if ((pevent->OSEventTbl[y] &= ~bitx) == 0) { pevent->OSEventGrp &= ~bity; } ptcb = OSTCBPrioTbl[prio]; ptcb->OSTCBDly = 0; ptcb->OSTCBEventPtr = (OS_EVENT *)0; ptcb->OSTCBMsg = msg; ptcb->OSTCBStat &= ~msk; if (ptcb->OSTCBStat == OS_STAT_RDY) { OSRdyGrp |= bity; OSRdyTbl[y] |= bitx; } } 06:10 /27
104
Wait Queue Functions void OSEventTaskWait (OS_EVENT *pevent) {
OSTCBCur->OSTCBEventPtr = pevent; if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { OSRdyGrp &= ~OSTCBCur->OSTCBBitY; } pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; pevent->OSEventGrp |= OSTCBCur->OSTCBBitY; } void OSEventTO (OS_EVENT *pevent) { if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY; } OSTCBCur->OSTCBStat = OS_STAT_RDY; OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; 06:10 /27
105
Semaphore 06:10 /27
106
Creating a Semaphore OS_EVENT *OSSemCreate (INT16U cnt) {
pevent = OSEventFreeList; if (OSEventFreeList != (OS_EVENT *)0) { OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } if (pevent != (OS_EVENT *)0) { pevent->OSEventType = OS_EVENT_TYPE_SEM; pevent->OSEventCnt = cnt; OSEventWaitListInit(pevent); } return (pevent); } 06:10 /27
107
Waiting for a Semaphore
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) { if (pevent->OSEventCnt > 0) { pevent->OSEventCnt--; } else if (OSIntNesting > 0) { *err = OS_ERR_PEND_ISR; } else { OSTCBCur->OSTCBStat |= OS_STAT_SEM; OSTCBCur->OSTCBDly = timeout; OSEventTaskWait(pevent); OSSched(); if (OSTCBCur->OSTCBStat & OS_STAT_SEM) { OSEventTO(pevent); OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; } } 06:10 /27
108
Signaling a Semaphore INT8U OSSemPost (OS_EVENT *pevent) {
if (pevent->OSEventGrp) { OSEventTaskRdy(pevent, (void *)0, OS_STAT_SEM); OSSched(); return (OS_NO_ERR); } else { if (pevent->OSEventCnt < 65535) { pevent->OSEventCnt++; return (OS_SEM_OVF); } } 06:10 /27
109
Getting a Semaphore without Waiting
INT16U OSSemAccept (OS_EVENT *pevent) { cnt = pevent->OSEventCnt; if (cnt > 0) { pevent->OSEventCnt--; } return (cnt); 06:10 /27
110
Obtaining the Status of a Semaphore
INT8U OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata) { pdata->OSEventGrp = pevent->OSEventGrp; psrc = &pevent->OSEventTbl[0]; pdest = &pdata->OSEventTbl[0]; for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } pdata->OSCnt = pevent->OSEventCnt; return (OS_NO_ERR); } 06:10 /27
111
Message Mailbox 06:10 /27
112
Creating a Mailbox OS_EVENT *OSMboxCreate (void *msg) {
pevent = OSEventFreeList; if (OSEventFreeList != (OS_EVENT *)0) { OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } if (pevent != (OS_EVENT *)0) { pevent->OSEventType = OS_EVENT_TYPE_MBOX; pevent->OSEventPtr = msg; OSEventWaitListInit(pevent); return (pevent); 06:10 /27
113
Waiting for a Message to Arrive at a Mailbox
void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) { msg = pevent->OSEventPtr; if (msg != (void *)0) { pevent->OSEventPtr = (void *)0; } else if (OSIntNesting > 0) { *err = OS_ERR_PEND_ISR; } else { OSTCBCur->OSTCBStat |= OS_STAT_MBOX; OSTCBCur->OSTCBDly = timeout; OSEventTaskWait(pevent); OSSched(); if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) { OSTCBCur->OSTCBMsg = (void *)0; OSTCBCur->OSTCBStat = OS_STAT_RDY; OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; } else if (OSTCBCur->OSTCBStat & OS_STAT_MBOX) { OSEventTO(pevent); } return (msg); 06:10 /27
114
Depositing a Message in a Mailbox
INT8U OSMboxPost (OS_EVENT *pevent, void *msg) { if (pevent->OSEventGrp) { OSEventTaskRdy(pevent, msg, OS_STAT_MBOX); OSSched(); return (OS_NO_ERR); } else { if (pevent->OSEventPtr != (void *)0) { return (OS_MBOX_FULL); pevent->OSEventPtr = msg; return (OS_NO_ERR); } } 06:10 /27
115
Getting a Message without Waiting
void *OSMboxAccept (OS_EVENT *pevent) { msg = pevent->OSEventPtr; if (msg != (void *)0) { pevent->OSEventPtr = (void *)0; } return (msg); } 06:10 /27
116
Obtaining the Status of a Mailbox
INT8U OSMboxQuery (OS_EVENT *pevent, OS_MBOX_DATA *pdata) { pdata->OSEventGrp = pevent->OSEventGrp; psrc = &pevent->OSEventTbl[0]; pdest = &pdata->OSEventTbl[0]; for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } pdata->OSMsg = pevent->OSEventPtr; return (OS_NO_ERR); } 06:10 /27
117
Using a Mailbox as a Binary Semaphore
void Task1 (void *pdata) { for (;;) { OSMboxPend(MboxSem, 0, &err); /* Obtain access to resource(s) */ . . /* Task has semaphore, access resource(s) */ OSMboxPost(MboxSem, (void )1); /* Release access to resource(s) */ } 06:10 /27
118
Using a Mailbox as a Time Delay
void Task1 (void *pdata) { for (;;) { OSMboxPend(MboxTimeDly, TIMEOUT, &err); /* Delay task */ . . /* Code executed after time delay */ } void Task2 (void *pdata) { OSMboxPost(MboxTimeDly, (void *)1); /* Cancel delay for Task1 */ 06:10 /27
119
Message Queues OSQCreate() OSQPend() OSQPost() OSQPostFront()
OSQAccept() OSQFlush() OSQQuery() 06:10 /27
120
Message Queues A message queue
Allows a task or an ISR to send pointer size variables to another task. Each pointer points a specific data structure containing a ‘message’. Looks like a mailbox with multiple entries. Is like an array of mailboxes except that there is only one wait list. 06:10 /27
121
Message Queues (Cont.) 06:10 /27
122
Data Structures in a Message Queue
06:10 /27
123
Queue Control Block A queue control block contains following fields
OSQPtr OSQStart OSQEnd OSQIn OSQOut OSQSize OSQEntries 06:10 /27
124
List of Free Queue Control Blocks
06:10 /27
125
Message Queue Is a Circular Buffer
06:10 /27
126
Creating a Queue Specify the number of entries.
OSQCreate() requires that you allocate an array of pointers that hold the message. The array must be declared as an array of pointers to void. Once a message queue has been created, it cannot be deleted. 06:10 /27
127
OSQCreate() OS_EVENT *OSQCreate (void **start, INT16U size) {
pevent = OSEventFreeList; if (OSEventFreeList != (OS_EVENT *)0) { OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } if (pevent != (OS_EVENT *)0) { pq = OSQFreeList; if (OSQFreeList != (OS_Q *)0) { OSQFreeList = OSQFreeList->OSQPtr; if (pq != (OS_Q *)0) { pevent->OSEventType = OS_EVENT_TYPE_Q; pevent->OSEventPtr = pq; OSEventWaitListInit(pevent); } else { pevent->OSEventPtr = (void *)OSEventFreeList; OSEventFreeList = pevent; pevent = (OS_EVENT *)0; return (pevent); 06:10 /27
128
OSQPend() (1) void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) { pq = pevent->OSEventPtr; if (pq->OSQEntries != 0) { msg = *pq->OSQOut++; pq->OSQEntries--; if (pq->OSQOut == pq->OSQEnd) { pq->OSQOut = pq->OSQStart; } *err = OS_NO_ERR; } else if (OSIntNesting > 0) { *err = OS_ERR_PEND_ISR; } else { OSTCBCur->OSTCBStat |= OS_STAT_Q; OSTCBCur->OSTCBDly = timeout; OSEventTaskWait(pevent); OSSched(); if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) { OSTCBCur->OSTCBMsg = (void *)0; OSTCBCur->OSTCBStat = OS_STAT_RDY; OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; *err = OS_NO_ERR; 06:10 /27
129
OSQPend() (2) } else if (OSTCBCur->OSTCBStat & OS_STAT_Q) {
OSEventTO(pevent); msg = (void *)0; *err = OS_TIMEOUT; } else { msg = *pq->OSQOut++; pq->OSQEntries--; if (pq->OSQOut == pq->OSQEnd) { pq->OSQOut = pq->OSQStart; } OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; *err = OS_NO_ERR; return (msg); 06:10 /27
130
OSQPost() INT8U OSQPost (OS_EVENT *pevent, void *msg) {
if (pevent->OSEventGrp) { OSEventTaskRdy(pevent, msg, OS_STAT_Q); OSSched(); return (OS_NO_ERR); } else { pq = pevent->OSEventPtr; if (pq->OSQEntries >= pq->OSQSize) return (OS_Q_FULL); else { *pq->OSQIn++ = msg; pq->OSQEntries++; if (pq->OSQIn == pq->OSQEnd) { pq->OSQIn = pq->OSQStart; } 06:10 /27
131
OSQPostFront() OSQPostFront() Is basically identical to OSQPost().
Uses OSQOut instead of OSQIn as the pointer to the next entry. 06:10 /27
132
OSQPostFront() (Cont.)
INT8U OSQPostFront (OS_EVENT *pevent, void *msg){ if (pevent->OSEventGrp) { OSEventTaskRdy(pevent, msg, OS_STAT_Q); OSSched(); return (OS_NO_ERR); } else { pq = pevent->OSEventPtr; if (pq->OSQEntries >= pq->OSQSize) return (OS_Q_FULL); else { if (pq->OSQOut == pq->OSQStart) { pq->OSQOut = pq->OSQEnd; } pq->OSQOut--; *pq->OSQOut = msg; pq->OSQEntries++; 06:10 /27
133
OSQAccept() void *OSQAccept (OS_EVENT *pevent) {
pq = pevent->OSEventPtr; if (pq->OSQEntries != 0) { msg = *pq->OSQOut++; pq->OSQEntries--; if (pq->OSQOut == pq->OSQEnd) { pq->OSQOut = pq->OSQStart; } } else { msg = (void *)0; return (msg); 06:10 /27
134
OSQFlush() INT8U OSQFlush (OS_EVENT *pevent) {
pq = pevent->OSEventPtr; pq->OSQIn = pq->OSQStart; pq->OSQOut = pq->OSQStart; pq->OSQEntries = 0; return (OS_NO_ERR); } 06:10 /27
135
OSQQuery() Pass a OS_Q_DATA structure to query.
OS_Q_DATA contains following fields OSMsg OSNMsgs OSQSize OSEventTbl[] OSEventGrp 06:10 /27
136
OSQQuery() (Cont.) INT8U OSQQuery (OS_EVENT *pevent, OS_Q_DATA *pdata){ pdata->OSEventGrp = pevent->OSEventGrp; psrc = &pevent->OSEventTbl[0]; pdest = &pdata->OSEventTbl[0]; for (i = 0; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } pq = (OS_Q *)pevent->OSEventPtr; if (pq->OSQEntries > 0) { pdata->OSMsg = pq->OSQOut; } else { pdata->OSMsg = (void *)0; pdata->OSNMsgs = pq->OSQEntries; pdata->OSQSize = pq->OSQSize; return (OS_NO_ERR); 06:10 /27
137
Using a Message Queue When Reading Analog Inputs
06:10 /27
138
Using a Queue as a Counting Semaphore
void main (void){ OSInit(); … QSem = OSQCreate(&QMsgTbl[0], N_RESOURCES); for (i = 0; i < N_RESOURCES; i++) { OSQPost(Qsem, (void *)1); } OSTaskCreate(Task1, .., .., ..); OSStart(); void Task1 (void *pdata) { for (;;) { OSQPend(&QSem, 0, &err); /* Obtain access to resource(s) */ Task has semaphore, access resource(s) OSMQPost(QSem, (void )1); /* Release access to resource(s) */ 06:10 /27
139
uC/OS-II Real-Time Systems Concepts Kernel Structure Task Management
Time Management Intertask Communication & Synchronization Memory Management 06:10 /27
140
Memory Management Overview Memory Control Blocks OSMemCreate()
OSMemGet() OSMemPut() OSMemQuery() Examples 06:10 /27
141
ANSI C malloc() and free()
Dangerous in an embedded real-time system. Fragmentation. Non-deterministic. 06:10 /27
142
µC/OS-II malloc() and free()
Obtain a contiguous memory area. Memory blocks are the same size. Partition contains an integral number of blocks. Allocation and de-allocation is deterministic. More than one memory partition can exist. Application can obtain memory blocks of different sizes. Memory block must always be returned to the partition from which it came from. 06:10 /27
143
Multiple Memory Partitions
06:10 /27
144
Memory Control Blocks Keeps track of memory partitions through MCB.
Each memory partition requires its own memory control block. Initialization is done by OSMemInit(). Number of memory partitions must be set to at least 2. 06:10 /27
145
Memory Control Block Data Structure
typedef struct { void *OSMemAddr; void *OSMemFreeList; INT32U OSMemBlkSize; INT32U OSMemNBlks; INT32U OSMemNFree; } OS_MEM; 06:10 /27
146
List of Free Memory Control Blocks
06:10 /27
147
OSMemCreate() (1) 06:10 /27
148
OSMemCreate() (2) OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err) { pmem = OSMemFreeList; if (OSMemFreeList != (OS_MEM *)0) { OSMemFreeList = (OS_MEM *)OSMemFreeList>OSMemFreeList; } if (pmem == (OS_MEM *)0) { *err = OS_MEM_INVALID_PART; return ((OS_MEM *)0); plink = (void **)addr; pblk = (INT8U *)addr + blksize; for (i = 0; i < (nblks - 1); i++) { *plink = (void *)pblk; plink = (void **)pblk; pblk = pblk + blksize; *plink = (void *)0; pmem->OSMemAddr = addr; pmem->OSMemFreeList = addr; pmem->OSMemNFree = nblks; pmem->OSMemNBlks = nblks; pmem->OSMemBlkSize = blksize; *err = OS_NO_ERR; return (pmem); >2 entry, entry size>a pointer 06:10 /27
149
OSMemGet() void *OSMemGet (OS_MEM *pmem, INT8U *err) {
if (pmem->OSMemNFree > 0) { pblk = pmem->OSMemFreeList; pmem->OSMemFreeList = *(void **)pblk; pmem->OSMemNFree--; *err = OS_NO_ERR; return (pblk); } else { *err = OS_MEM_NO_FREE_BLKS; return ((void *)0); } 06:10 /27
150
Returning a Memory Block
INT8U OSMemPut (OS_MEM *pmem, void *pblk) { if (pmem->OSMemNFree >= pmem->OSMemNBlks) return (OS_MEM_FULL); *(void **)pblk = pmem->OSMemFreeList; pmem->OSMemFreeList = pblk; pmem->OSMemNFree++; return (OS_NO_ERR); } 06:10 /27
151
Obtaining Status about Memory Partition
Pass a OS_MEM_DATA structure to query. OS_MEM_DATA contains following fields OSAddr OSFreeList OSBlkSize OSNBlks OSNFree OSNUsed 06:10 /27
152
OSMemQuery() INT8U OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *pdata) {
pdata->OSAddr = pmem->OSMemAddr; (1) pdata->OSFreeList = pmem->OSMemFreeList; pdata->OSBlkSize = pmem->OSMemBlkSize; pdata->OSNBlks = pmem->OSMemNBlks; pdata->OSNFree = pmem->OSMemNFree; pdata->OSNUsed = pdata->OSNBlks - pdata->OSNFree; (2) return (OS_NO_ERR); } 06:10 /27
153
Using Dynamic Memory Allocation
06:10 /27
154
Waiting for Memory Blocks (1)
Sometimes it’s useful to have a task wait for a memory block in case a partition runs out of blocks. µC/OS-II doesn’t support ‘pending’ on a partitions. Can support this requirement by adding a counting semaphore. 06:10 /27
155
Waiting for Memory Blocks (2)
void main (void) { … SemaphorePtr = OSSemCreate(100); PartitionPtr = OSMemCreate(Partition, 100, 32, &err); OSTaskCreate(Task, (void *)0, &TaskStk[999], &err); OSStart(); } 06:10 /27
156
Waiting for Memory Blocks (3)
void Task (void *pdata) { INT8U err; INT8U *pblock; for (;;) { OSSemPend(SemaphorePtr, 0, &err); pblock = OSMemGet(PartitionPtr, &err); /* Use the memory block */ OSMemPut(PartitionPtr, pblock); OSSemPost(SemaphorePtr); } 06:10 /27
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.