Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2012 / uCOS / uCOSII_ebook.pdf
Скачиваний:
70
Добавлен:
10.02.2015
Размер:
1.08 Mб
Скачать

application will thus need to allocate a variab le of type OS_SEM_DATA that will be used to receive the information about the desired semaphore. I decided to use a new data structure because the caller should only be concerned with semaphore specific data as opposed to the more generic OS_EVENT data structure which contain two additional fields (i.e. .OSEventType and .OSEventPtr). OS_SEM_DATA contains the current semaphore count (.OSCnt) and the list of tasks waiting on the semaphore (.OSEventTbl[] and .OSEventGrp).

As always, our function checks that pevent points to an ECB containing a semaphore L6.13(1). OSSemQuery() then copies the wait list L6.13(2) followed by the current semaphore count L6.13(3) from the OS_EVENT structure to the OS_SEM_DATA structure.

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);

}

Listing 6.13, Obtaining the status of a semaphore.

6.06 Message Mailboxes

A message mailbox (or simply a mailbox) is a μC/OS-II object that allows a task or an ISR to send a pointer size variable to another task. The pointer would typically be initialized to point to some application specific data structure containing a ‘message’. To enable µC/OS-II’s message mailbox services, you must set the configuration constant

OS_MBOX_EN to 1 (see file OS_CFG.H).

A mailbox needs to be created before it can be used. Creating a mailbox is accomplished by calling OSMboxCreate() (see next section) and specifying the initial value of the pointer. Typically, the initial value is a NULL pointer but a mailbox can initially contain a message. If you use the mailbox to signal the occurrence of an event (i.e. send a message) then you would typically initialize it to a NULL pointer because the event (most likely) would not have occurred. If you use the mailbox to access a shared resource then you would initialize the mailbox with a non-NULL pointer. In this case, you would basically use the mailbox as a binary semaphore.

µC/OS-II provides five services to access mailboxes: OSMboxCreate(), OSMboxPend(), OSMboxPost(), OSMboxAccept() and OSMboxQuery(). Figure 6-6 shows a flow diagram to illustrate the relationship between

tasks, ISRs and a message mailbox. Note that the symbology used to represent a mailbox is an I -beam. The content of the mailbox is a pointer to a message. What the pointer points to is application specific. A mailbox can only contain one pointer (mailbox is full) or a pointer toNULL (mailbox is empty). As you can see from figure 6-6, a task or a n ISR can call OSMboxPost(). However, only tasks are allowed to call OSMboxPend() and OSMboxQuery().

OSMboxCreate()

Task OSMboxPost()

OSMboxPend()

OSMboxAccept()

OSMboxQuery()

Task

ISR

OSMboxPost() Mailbox

Message

 

OSMboxAccept()

Figure 6-6, Relationship between tasks, ISRs and a message mailbox.

6.06.01 Creating a Mailbox, OSMboxCreate()

The code to create a mailbox is shown in listing 6.14 and is basically identical to OSSemCreate() except that the ECB type is set to OS_EVENT_TYPE_MBOX L6.14(1) and, instead of using the .OSEventCnt field, we use the .OSEventPtr field to hold the message pointer L6.14(2).

OSMboxCreate() returns a pointer to the ECB L6.14(3). This pointer MUST be used in subsequent calls to access the mailbox OSMboxPend(), OSMboxPost(), OSMboxAccept() and OSMboxQuery()). The pointer is basically used as the mailbox handle. Note that if there were no more ECBs, OSMboxCreate() would have returned a NULL pointer.

You should note that once a mailbox has been created, it cannot be deleted. It would be ‘dangerous’to delete a message mailbox object if tasks were waiting on the 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)

}

Listing 6.14, Creating a mailbox.

6.06.02 Waiting for a message at a Mailbox, OSMboxPend()

The code to wait for a message to arrive at a mailbox is shown in listing 6.15. Again, the code is very similar to OSSemPend() so I will only discuss the differences. OSMboxPend() verifies that the ECB being pointed to by pevent has been created by OSMboxCreate() L6.15(1). A message is available when .OSEventPtr contains a non-NULL pointer L6.15(2). In this case, OSMboxPend() stores the pointer to the message in msg and places a NULL-pointer in .OSEventPtr to empty the mailbox L6.15(3). Again, this is the outcome you are looking for. This also happens to be the fastest path through OSMboxPend().

If a message is not available (.OSEventPtr contains a NULL-pointer), we check to see if the function was called by an ISR L6.15(4). As with OSSemPend(), you should not call OSMboxPend() from an ISR because an ISR cannot be made to wait. Again, I decided to add this check just in case. However, if the message is in fact available, the call to OSMboxPend() would be successful even if called from an ISR!

If a message is not available then the calling task must be suspended until either a message is posted or the specified timeout period expires L6.15(5). When a message is posted to the mailbox (or the timeout period expires) and the task that called OSMboxPend() is again the highest priority task then OSSched()returns. OSMboxPend() checks to see if a message was placed in the task’s TCB by OSMboxPost() L6.15(6). If this is the case, the call is successful

and the message is returned to the caller. Note that we again need to clear the mailbox’s content by placing a

NULL-pointer in .OSEventPtr.

A timeout is detected by looking at the .OSTCBStat field in the task’s TCB to see if the OS_STAT_MBOX bit is still set. A timeout occurred when the bit is set L6.15(7). The task is removed from the mailbox’s wait list by calling OSEventTO() L6.15(8). Note that the returned pointer is set to NULL L6.15(9) because there was no message. If the status flag in the task’s TCB doesn’t have the OS_STAT_MBOX bit set then a message must have been sent. The task that called OSMboxPend() will thus receive the pointer to the message L6.15(10). Also, the link to the ECB is removed L6.15(11).

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

{

void *msg;

OS_ENTER_CRITICAL();

 

(1)

if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {

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;

 

(4)

} else if (OSIntNesting > 0) {

 

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;

(7)

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

OSEventTO(pevent);

 

(8)

OS_EXIT_CRITICAL();

= (void *)0;

(9)

msg

*err

= OS_TIMEOUT;

 

} else {

 

 

msg

= pevent->OSEventPtr;

(10)

pevent->OSEventPtr

= (void *)0;

 

OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;

(11)

OS_EXIT_CRITICAL();

 

 

*err

= OS_NO_ERR;

 

}

 

 

}

return (msg);

}

Listing 6.15, Waiting for a message to arrive at a mailbox.

6.06.03 Sending a message to a mailbox, OSMboxPost()

The code to deposit a message in a mailbox is shown in listing 6.16. After making sure that the ECB is used as a mailbox L6.16(1), OSMboxPost() checks to see if any task is waiting for a message to arrive at the mailbox L6.16(2). There are tasks waiting when the OSEventGrp field in the ECB contains a non-zero value. The highest priority task waiting for the message will be removed from the wait list by OSEventTaskRdy() (see section 6.02, Making a task ready, OSEventTaskRdy() ) L6.16(3), and this task will be made ready-to-run. OSSched() is then

called to see if the task made ready is now the highest priority task ready-to-run. If it is, a context switch will result (only if OSMboxPost() is called from a task) and the readied task will be executed. If the readied task is not the

highest priority task then OSSched() will return and the task that called OSMboxPost() will continue execution. If there were no tasks waiting for a message to arrive at the mailbox, then the pointer to the message is saved in the mailbox L6.16(6), assuming there isn’t already a non-NULL pointer L6.16(5). Storing the pointer in the mailbox allows the next task to call OSMboxPend() to immediately get the message.

You should note that a context switch does not occur if OSMboxPost() is called by an ISR because context switching from an ISR can only occurs when OSIntExit() is calle d at the completion of the ISR, and from the last nested ISR (see section 3.09, Interrupts under µC/OS-II).

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);

 

}

(2)

if (pevent->OSEventGrp) {

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);

 

}

 

}

}

Listing 6.16, Depositing a message in a mailbox.

6.06.04 Getting a message without waiting, OSMboxAccept()

It is possib le to obtain a message from a mailbox without putting a task to sleep if the mailbox is empty. This is accomplished by calling OSMboxAccept() and the code for this function is shown in listing 6.17. OSMboxAccept() starts by checking that the ECB being pointed to by pevent has been created by OSMboxCreate() L6.17(1). OSMboxAccept() then gets the current contents of the mailbox L6.17(2) in order to determine whether a message is available (i.e. non-NULL pointer) L6.17(3). If a message is available, the ma ilbox is emptied L6.17(4). Finally, the original contents of the mailbox is returned to the caller L6.17(5). The code that called OSMboxAccept() will need to examine the returned value. If OSMboxAccept() returns a NULL pointer then a message was not available. A non-NULL pointer indicates that a message was deposited in the mailbox. An ISR should use OSMboxAccept() instead of OSMboxPend().

You can use OSMboxAccept() to ‘flush’the contents of 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)

}

Listing 6.17, Getting a message without waiting.

6.06.05 Obtaining the status of a mailbox, OSMboxQuery()

OSMboxQuery() allows your application to take a ‘snapshot’of an ECB used for a message mailbox. The code for this function is shown in listing 6.18. OSMboxQuery() is passed two arguments: pevent contains a pointer to the message mailbox which is returned by OSMboxCreate() when the mailbox is created and, pdata which is a pointer to a data structure (OS_MBOX_DATA, see uCOS_II.H) that will hold information about the message mailbox. Your application will thus need to allocate a variable of type OS_MBOX_DATA that will be used to receive the

information about the desired mailbox. I decided to use a new data structure because the caller should only be concerned with mailbox specific data as opposed to the more generic OS_EVENT data structure which contain two additional fields (i.e. .OSEventCnt and .OSEventType). OS_MBOX_DATA contains the current contents of the message (i.e. .OSMsg) and the list of tasks waiting for a message to arrive ( .OSEventTbl[] and .OSEventGrp).

As always, our function checks that pevent points to an ECB containing a mailbox L6.18(1). OSMboxQuery() then copies the wait list L6.18(2) followed by the current message L6.18(3) from the OS_EVENT structure to the

OS_MBOX_DATA structure.

INT8U OSMboxQuery (OS_EVENT *pevent, OS_MBOX_DATA *pdata)

{

INT8U

i;

 

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);

}

Listing 6.18, Obtaining the status of a mailbox.

6.06.06 Using a mailbox as a binary semaphore

A message mailbox can be used as a binary semaphore by initializing the mailbox with a non -NULL pointer ((void *1) works well). A task requesting the ‘semaphore’would call OSMboxPend() and would release the ‘semaphore’ by calling OSMboxPost(). Listing 6.19 shows how this works. You would use this technique to conserve code space if your application only needed binary semaphores and mailboxes. In this case, you could set OS_SEM_EN to 0 and only use mailboxes instead of both mailboxes and semaphores.

OS_EVENT *MboxSem;

 

void Task1 (void *pdata)

 

{

 

 

INT8U err;

 

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) */

}

}

Listing 6.19, Using a mailbox as a binary semaphore.

Соседние файлы в папке uCOS