Versions Compared

Key

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

...

The operations described above are summarized using the pseudo-code shown in Listing 13-5.

Listing 13-5 Using a semaphore to access a shared resource

L13-5(1) The application must declare a semaphore as a variable of type OS_SEM. This variable will be referenced by other semaphore services.

...

L13-5(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.

Listing 13-6 Using a semaphore to access a shared resource

L13-6(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.

...

L13-6(9) As with most µC/OS-III functions, you specify the address of a variable that will receive an error message from the call.

Listing 13-7 Using a semaphore to access a shared resource

L13-7(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 printing “I am Task 1,” and Task 2 printing “I am Task 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 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.

Image Removed

Figure 13-1 Using a semaphore to access a printer

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-2.

Image Removed

Figure 13-2 Hiding a semaphore from a task

The 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-8.Listing 13-8 Encapsulating the use of a semaphore

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.