The code of Listing 3-4 through Listing 3-8 shows the listings below shows a more complete example and contains three tasks: , a mutual exclusion , semaphore, and a message queue.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
/* *********************************************************************************************** * INCLUDE FILES *********************************************************************************************** */ #include <app_cfg.h> #include <bsp.h> #include <os.h> /* *********************************************************************************************** * LOCAL GLOBAL VARIABLES *********************************************************************************************** */ static OS_TCB AppTaskStartTCB; (1) static OS_TCB AppTask1_TCB; static OS_TCB AppTask2_TCB; static OS_MUTEX AppMutex; (2) static OS_Q AppQ; (3) static CPU_STK AppTaskStartStk[APP_TASK_START_STK_SIZE]; (4) static CPU_STK AppTask1_Stk[128]; static CPU_STK AppTask2_Stk[128]; /* *********************************************************************************************** * FUNCTION PROTOTYPES *********************************************************************************************** */ static void AppTaskStart (void *p_arg); (5) static void AppTask1 (void *p_arg); static void AppTask2 (void *p_arg); |
Panel | ||
---|---|---|
| ||
(1) |
...
Here we allocate storage for |
...
the (2) |
...
A mutual exclusion semaphore (a.k.a. a mutex) is a kernel object (a data structure) that is used to protect a shared resource from being accessed by more than one task. A task that wants to access the shared resource must obtain the mutex before it is allowed to proceed. The owner of the resource relinquishes the mutex when it has finished accessing the resource. This process is demonstrated in this example. (3) |
...
A message queue is a kernel object through which Interrupt Service Routines (ISRs) and/or tasks send messages to other tasks. The sender “formulates” a message and sends it to the message queue. The task(s) wanting to receive these messages wait on the message queue for messages to arrive. If there are already messages in the message queue, the receiver immediately retrieves those messages. If there are no messages waiting in the message queue, then the receiver will be placed in a wait list associated with the message queue. This process will be demonstrated in this example. (4) |
...
A stack is allocated for each task. (5) |
...
The prototype of the tasks are declared. |
Listing 3-5 The listing below shows the C entry point, i.e. main()
.
Anchor Listing - app.c (2nd Part) Listing - app.c (2nd Part)
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
void main (void) { OS_ERR err; BSP_IntDisAll(); OSInit(&err); /* Check for 'err' */ OSMutexCreate((OS_MUTEX *)&AppMutex, (1 |
...
)
(CPU_CHAR *)"My App. Mutex",
(OS_ERR *)&err);
/* Check for 'err' */
OSQCreate ((OS_Q *)&AppQ, (2)
(CPU_CHAR *)"My App Queue",
(OS_MSG_QTY )10,
(OS_ERR *)&err);
/* Check for 'err' */
OSTaskCreate((OS_TCB *)&AppTaskStartTCB, (3)
(CPU_CHAR *)"App Task Start",
(OS_TASK_PTR )AppTaskStart,
(void *)0,
(OS_PRIO )APP_TASK_START_PRIO,
(CPU_STK *)&AppTaskStartStk[0],
(CPU_STK_SIZE)APP_TASK_START_STK_SIZE / 10,
(CPU_STK_SIZE)APP_TASK_START_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void *)0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
/* Check for 'err' */
OSStart(&err);
/* Check for 'err' */
} |
Panel | ||
---|---|---|
| ||
(1) Creating a mutex is simply a matter of |
...
calling |
...
the |
...
object that will be used for the mutex. Resource Management |
...
provides additional information about mutual exclusion semaphores. You can assign an ASCII name to the mutex, which is useful when debugging. (2) |
...
You create the message queue by |
...
calling |
...
and specify the address of |
...
the |
...
object. Message Passing |
...
provides additional information about message queues. You can assign an ASCII name to the message queue which can also be useful during debugging. You need to specify how many messages the message queue is allowed to receive. This value must be greater than zero. If the sender sends messages faster than they can be consumed by the receiving task, messages will be lost. This can be corrected by either increasing the size of the message queue, or increasing the priority of the receiving task. (3) |
...
The first application task is created. |
Listing 3-6 The listing below shows how to create other tasks once multitasking as started.
Anchor Listing - app.c (3rd Part) Listing - app.c (3rd Part)
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
static void AppTaskStart (void *p_arg) { OS_ERR err; p_arg = p_arg; BSP_Init(); CPU_Init(); BSP_OS_TickInit(); OSTaskCreate((OS_TCB *)&AppTask1_TCB, (1) (CPU_CHAR *)"App Task 1", (OS_TASK_PTR )AppTask1, (void *)0, (OS_PRIO )5, (CPU_STK *)&AppTask1_Stk[0], (CPU_STK_SIZE)0, (CPU_STK_SIZE)128, (OS_MSG_QTY )0, (OS_TICK )0, (void *)0, (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), (OS_ERR *)&err); OSTaskCreate((OS_TCB *)&AppTask2_TCB, (2) (CPU_CHAR *)"App Task |
...
2",
(OS_TASK_PTR )AppTask2,
(void *)0,
(OS_PRIO )6,
(CPU_STK *)&AppTask2_Stk[0],
(CPU_STK_SIZE)0,
(CPU_STK_SIZE)128,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void *)0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
BSP_LED_Off(0);
while (1) {
BSP_LED_Toggle(0);
OSTimeDlyHMSM((CPU_INT16U) 0,
(CPU_INT16U) 0,
(CPU_INT16U) 0,
(CPU_INT32U)100,
(OS_OPT )OS_OPT_TIME_HMSM_STRICT,
(OS_ERR *)&err);
}
} |
Panel | ||
---|---|---|
| ||
(1) Task #1 is created by calling |
...
will return |
...
to |
...
and continue execution. (2) |
...
Task #2 is created and if it has a higher priority |
...
than |
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
static void AppTask1 (void *p_arg) { OS_ERR err; CPU_TS ts; p_arg = p_arg; while (1) { OSTimeDly ((OS_TICK )1, (1 |
...
)
(OS_OPT )OS_OPT_TIME_DLY,
(OS_ERR *)&err);
OSQPost ((OS_Q *)&AppQ, (2)
(void *)1;
(OS_MSG_SIZE)sizeof(void *),
(OS_OPT )OS_OPT_POST_FIFO,
(OS_ERR *)&err);
OSMutexPend((OS_MUTEX *)&AppMutex, (3)
(OS_TICK )0,
(OS_OPT )OS_OPT_PEND_BLOCKING;
(CPU_TS *)&ts,
(OS_ERR *)&err);
/* Access shared resource */ (4)
OSMutexPost((OS_MUTEX *)&AppMutex, (5)
(OS_OPT )OS_OPT_POST_NONE,
(OS_ERR *)&err);
}
} |
Panel | ||
---|---|---|
| ||
(1) The task starts by waiting for one tick to expire before it does anything useful. If the µC/OS-III tick rate is configured for 1000 Hz, the task will be suspended for 1 millisecond. (2) |
...
The task then sends a message to another task using the message |
...
queue (3) |
...
The task then waits on the mutual exclusion semaphore since it needs to access a shared resource with another task. If the resource is already owned by another task, |
...
will wait forever for the mutex to be released by its current owner. The forever wait is specified by passing 0 as the second argument of the call. (4) |
...
When |
...
returns, the task owns the resource and can therefore access the shared resource. The shared resource may be a variable, an array, a data structure, an I/O device, etc. You should note that we didn’t actually show the access to the shared resource. This is not relevant at this point. (5) |
...
When the task is done with the shared resource, it must |
...
call |
...
to release the mutex. |
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
static void AppTask2 ( |
...
void *p_arg)
{
OS_ERR err;
void *p_msg;
OS_MSG_SIZE msg_size;
CPU_TS ts;
CPU_TS ts_delta;
p_arg = p_arg;
while (1) {
p_msg = OSQPend((OS_Q *)&AppQ, (1)
(OS_MSG_SIZE *)&msg_size,
(OS_TICK )0,
(OS_OPT )OS_OPT_PEND_BLOCKING,
(CPU_TS *)&ts,
(OS_ERR *)&err);
ts_delta = OS_TS_GET() - ts; (2)
/* Process message received */ (3)
}
} |
Panel | ||
---|---|---|
| ||
(1) Task #2 starts by waiting for messages to be sent through the message |
...
queue When the message is |
...
received |
...
will contain the message (i.e., a pointer to “something”). In our case, |
...
will always receive a message value of ‘ Also, when the message is received, “ (2) |
...
Knowing when the message was sent allows the user to determine how long it took this task to get the message. This is done by reading the current timestamp and subtracting the timestamp of when the message was sent |
...
. Note that the receiving task may not get the message immediately since ISRs or other higher-priority tasks might execute before the receiver gets to run. (3) |
...
Here you would add your own code to process the received message. |