Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Anchor10499091049909 Binary Semaphores Anchor10499101049910A task that wants to acquire a resource must perform a Wait (or Pend) operation. If the semaphore is available (the semaphore value is greater than 0), the semaphore value is set to 0, and the task continues execution (owning the resource). If the semaphore’s value is 0, the task performing a Wait on the semaphore is placed in a waiting list. µC/OS-III allows a timeout to be specified. If the semaphore is not available within a certain amount of time, the requesting task is made ready-to-run, and an error code (indicating that a timeout has occurred) is returned to the caller.anchor

10528601052860A task releases a semaphore by performing a Signal (or Post) operation. If no task is waiting for the semaphore, the semaphore value is simply set to 1. If there is at least one task waiting for the semaphore, the highest-priority task waiting on the semaphore is made ready-to-run, and the semaphore value is not incremented. If the readied task has a higher priority than the current task (the task releasing the semaphore), a context switch occurs and the higher-priority task resumes execution. The current task is suspended until it again becomes the highest-priority task that is ready-to-run.

Anchor10528611052861The operations described above are summarized using the pseudo-code shown in Listing 13-5the listing below.anchor

Code Block

...

titleUsing a semaphore to access a shared resource
OS_SEM  MySem;                               (1) 
 
 
void  main (void)
{
    OS_ERR  err;
    :
    :
    OSInit(&err);
    :
    OSSemCreate(&MySem,                      (2) 
               "My Semaphore",               (3) 
               1,                            (4) 
               &err);                        (5) 
    /* Check "err" */
    :
    /* Create task(s) */
    :
    OSStart(&err);
    (void)err;
}


Panel
bgColor#f0f0f0
borderWidth0

(1) The application must declare a semaphore as a variable of

...

type OS_SEM. This variable will be referenced by other semaphore services.

...

(2) You create a semaphore by

...

calling OSSemCreate()

...

 and pass the address to the semaphore allocated in (1). The semaphore must be created before it can be used by other tasks. Here, the semaphore is initialized in startup code (i.e., main()), however it could also be initialized by a task (but it must be initialized before it is used).

...

(3) You can assign an ASCII name to the semaphore, which can be used by debuggers or µC/Probe to easily identify the semaphore. Storage for the ASCII characters is typically in ROM, which is typically more plentiful than RAM. If it is necessary to change the name of the semaphore at runtime, you can store the characters in an array in RAM and simply pass the address of the array

...

to OSSemCreate(). Of course, the array must be NUL terminated.

...

(4) You specify the initial value of the semaphore. You should initialize the semaphore to 1 when the semaphore is used to access a single shared resource (as in this example).

...

(5) OSSemCreate()

...

 returns an error code based on the outcome of the call. If all the arguments are valid, err

...

 will contain OS_ERR_NONE. Refer to the description

...

of OSSemCreate()

...

 in Appendix A, µC/OS-III API

...

Reference for a list of other error codes and their meaning.

...


Code Block

...

void Task1 (void {

{
    OS_ERR  err;

    CPU_TS
ts;     while
  ts;
 
 
    while (DEF_ON)
{ :
 {
        :
        OSSemPend(&MySem,
(1) 0, (2) OS_OPT_PEND_BLOCKING, (3) &ts, (4) &err); (5) switch (err) { case OS_ERR_NONE: Access Shared Resource; (6) OSSemPost(&MySem, (7) OS_OPT_POST_1, (8) &err); (9) /* Check “err” */ break;   case OS_ERR_PEND_ABORT: /* The pend was aborted by another task */ break;   case OS_ERR_OBJ_DEL: /* The semaphore was deleted */ break;   default: /* Other errors */ } : } }
HTML Table
summary
classCode_Listing
Table Row (tr)
Table Cell (td)
Anchor
10708491070849
titleUsing a semaphore to access a shared resource
void  Task1 (void *p_arg)
Anchor
10708501070850
Anchor
10708511070851
Anchor
10708521070852
Anchor
10708531070853
Anchor
10708541070854
Anchor
10708551070855
Anchor
10708561070856
Anchor
10708571070857
Anchor
10708581070858
Anchor
10708591070859
Anchor
10708601070860
Anchor
10708611070861
Anchor
10708621070862
Anchor
10708631070863
Anchor
10708641070864
Anchor
10708651070865
Anchor
10708661070866
Anchor
10708671070867
Anchor
10708681070868
Anchor
10708691070869
Anchor
10708701070870
Anchor
10708711070871
Anchor
10708721070872
Anchor
10708731070873
Anchor
10708741070874
Anchor
10708751070875
Anchor
10708771070877
Anchor
10708781070878
Anchor
10708791070879
Anchor
10708801070880
Anchor
10708811070881
Anchor
10708821070882
Anchor
10708831070883
Anchor
10708841070884
Anchor
10708851070885

...

                              (1) 
                  0,                                   (2) 
                  OS_OPT_PEND_BLOCKING,                (3) 
                  &ts,                                 (4) 
                  &err);                               (5) 
        switch (err) {
            case OS_ERR_NONE:
                 Access Shared Resource;               (6) 
                 OSSemPost(&MySem,                     (7) 
                           OS_OPT_POST_1,              (8) 
                           &err);                      (9) 
                 /* Check "err" */
                 break;
 
            case OS_ERR_PEND_ABORT:
                 /* The pend was aborted by another task     */
                 break;
 
            case OS_ERR_OBJ_DEL:
                 /* The semaphore was deleted                */
                 break;
 
            default:
                 /* Other errors                             */
        }
        :
    }
}


Panel
bgColor#f0f0f0

(1) The task pends (or waits) on the semaphore by

...

calling OSSemPend(). The application must specify the desired semaphore to wait upon, and the semaphore must have been previously created.

...

(2) The next argument is a timeout specified in number of clock ticks. The actual timeout depends on the tick rate. If the tick rate (

...

see os_cfg_app.h) is set to 1000, a timeout of 10 ticks represents 10 milliseconds. Specifying a timeout of zero (0) means waiting forever for the semaphore.

...

(3) The third argument specifies how to wait. There are two options: OS_OPT_PEND_BLOCKING

...

 and OS_OPT_PEND_NON_BLOCKING. The blocking option means that if the semaphore is not available, the task

...

calling OSSemPend()

...

 will wait until the semaphore is posted or until the timeout expires. The non-blocking option indicates that if the semaphore is not available, OSSemPend()

...

 will return immediately and not wait. This last option is rarely used when using a semaphore to protect a shared resource.

...

(4) When the semaphore is posted, µC/OS-III reads a “timestamp” and returns this timestamp

...

when OSSemPend()

...

 returns. This feature allows the application to know “when” the post happened and the semaphore was released. At this point, OS_TS_GET()

...

 is read to get the current timestamp and you can compute the difference, indicating the length of the wait.

...

(5) OSSemPend()

...

 returns an error code based on the outcome of the call. If the call is successful, err

...

 will contain OS_ERR_NONE. If not, the error code will indicate the reason for the error. See

...

Appendix A, µC-OS-III

...

Configuration Manual for a list of possible error code

...

for OSSemPend(). Checking for error return values is important since other tasks might delete or otherwise abort the pend. However, it is not a recommended practice to delete kernel objects at run time as the action may cause serious problems.

...

(6) The resource can be accessed

...

when OSSemPend()

...

 returns, if there are no errors.

...

(7) When finished accessing the resource, you simply

...

call OSSemPost()

...

 and specify the semaphore to be released.

...

(8) OS_OPT_POST_1

...

 indicates that the semaphore is signaling a single task, if there are many tasks waiting on the semaphore. In fact, you should always specify this option when a semaphore is used to access a shared resource.

...

(9) As with most µC/OS-III functions, you specify the address of a variable that will

...

void Task2 (void

receive an error message from the call.

HTML Table
summary
classCode_Listing
Table Row (tr)
Table Cell (td)
Anchor
10709181070918


{

{
    OS_ERR  err;

    CPU_TS
ts;     while (DEF_ON) { : OSSemPend(&MySem, (1) 0,
  ts;
 
 
    while (DEF_ON) {
        :
        OSSemPend(&MySem,                             (1) 
                  0,                       
                  OS_OPT_PEND_BLOCKING,
&ts, &err); switch (err) { case OS_ERR_NONE: Access Shared Resource; OSSemPost(&MySem, OS_OPT_POST_1, &err); /* Check “err” */ break;   case OS_ERR_PEND_ABORT: /* The pend was aborted by another task */ break;   case OS_ERR_OBJ_DEL: /* The semaphore was deleted */ break;   default: /* Other errors */ } : } }
Code Block
titleUsing a semaphore to access a shared resource
void  Task2 (void *p_arg)
Anchor
10709191070919
Anchor
10709201070920
Anchor
10709211070921
Anchor
10709221070922
Anchor
10709231070923
Anchor
10709241070924
Anchor
10709251070925
Anchor
10709261070926
Anchor
10709271070927
Anchor
10709281070928
Anchor
10709291070929
Anchor
10709301070930
Anchor
10709311070931
Anchor
10709321070932
Anchor
10709331070933
Anchor
10709341070934
Anchor
10709351070935
Anchor
10709361070936
Anchor
10709371070937
Anchor
10709381070938
Anchor
10709391070939
Anchor
10709401070940
Anchor
10709411070941
Anchor
10709421070942
Anchor
10709431070943
Anchor
10709441070944
Anchor
10709461070946
Anchor
10709471070947
Anchor
10709481070948
Anchor
10709491070949
Anchor
10709501070950
Anchor
10709511070951
Anchor
10709521070952
Anchor
10709531070953
Anchor
10709541070954

...

     
                  &ts,                      
                  &err); 
        switch (err) {
            case OS_ERR_NONE:
                 Access Shared Resource;
                 OSSemPost(&MySem,      
                           OS_OPT_POST_1,
                           &err);        
                 /* Check "err" */
                 break;
 
            case OS_ERR_PEND_ABORT:
                 /* The pend was aborted by another task     */
                 break;
 
            case OS_ERR_OBJ_DEL:
                 /* The semaphore was deleted                */
                 break;
 
            default:
                 /* Other errors                             */
        }
        :
    }
}


Panel
bgColor#f0f0f0

(1) Another task wanting to access the shared resource needs to use the same procedure to access the shared resource.

...


Semaphores are especially useful when tasks share I/O devices. Imagine what would happen if two tasks were allowed to send characters to a printer at the same time. The printer would contain interleaved data from each task. For instance, the printout from Task 1 Task 1 printing “I am Task 1Task 1,” and Task 2 Task 2 printing “I am Task 2Task 2,” could result in “I Ia amm T Tasask k1 2”. In this case, you can use a semaphore and initialize it to 1 (i.e., a binary semaphore). The rule is simple: to access the printer each task must first obtain the resource’s semaphore. Figure 13-1 The figure below  shows tasks competing for a semaphore to gain exclusive access to the printer. Note that a key, indicating that each task must obtain this key to use the printer, represents the semaphore symbolically.anchor


...

Panel

...

titleUsing a semaphore to access a printer

...

Image Added


The above example implies that each task knows about the existence of the semaphore to access the resource. It is almost always better to encapsulate the critical section and its protection mechanism. Each task would therefore not know that it is acquiring a semaphore when accessing the resource. For example, an RS-232C port is used by multiple tasks to send commands and receive responses from a device connected at the other end as shown in Figure 13-2the figure below. Anchor10529061052906 Image Removed

...


...

Panel
titleHiding a semaphore from a task

...

Image Added


10500191050019The function CommSendCmd() is called with three arguments: the ASCII string containing the command, a pointer to the response string from the device, and finally, a timeout in case the device does not respond within a certain amount of time. The pseudo-code for this function is shown in Listing 13-8the listing below.anchor


Code Block

...

1070974

                      CPU_CHAR
*response, OS_TICK timeout) { Acquire serial port’s semaphore; Send “cmd” to device; Wait for response with “timeout”; if (timed out) { Release serial port’s semaphore; return (error code); } else { Release serial port’s semaphore; return (no error); } }
HTML Table
summary
classCode_Listing
Table Row (tr)
Table Cell (td)
Anchor
1070974
titleEncapsulating the use of a semaphore
APP_ERR  CommSendCmd (CPU_CHAR  *cmd,
Anchor
10709751070975
Anchor
10709761070976
Anchor
10709771070977
Anchor
10709791070979
Anchor
10709801070980
Anchor
10709811070981
Anchor
10709821070982
Anchor
10709841070984
Anchor
10709851070985
Anchor
10709861070986
Anchor
10709881070988
Anchor
10709891070989
Anchor
10709901070990
Anchor
10709911070991

...

  *response,
                      OS_TICK    timeout)
{
    Acquire serial port's semaphore;
    Send "cmd" to device;
    Wait for response with "timeout";
    if (timed out) {
        Release serial port's semaphore;
        return (error code);
    } else {
        Release serial port's semaphore;
        return (no error);
    }
}


Each task that needs to send a command to the device must call this function. The semaphore is assumed to be initialized to 1 (i.e., available) by the communication driver initialization routine. The first task that calls CommSendCmd() acquires the semaphore, proceeds to send the command, and waits for a response. If another task attempts to send a command while the port is busy, this second task is suspended until the semaphore is released. The second task appears simply to have made a call to a normal function that will not return until the function performs its duty. When the semaphore is released by the first task, the second task acquires the semaphore and is allowed to use the RS-232C port.