Listing 7.1: Pseudo code for non-interlocked, one-way data communication.
tSourceTask () {
:
Send message to message queue
:
}
tSinkTask () {
:
Receive message from message queue
:
}
If tSinkTask is set to a higher priority, it runs first until it blocks on an empty message queue. As soon as tSourceTask sends the message to the queue, tSinkTask receives the message and starts to execute again.
If tSinkTask is set to a lower priority, tSourceTask fills the message queue with messages. Eventually, tSourceTask can be made to block when sending a message to a full message queue. This action makes tSinkTask wake up and start taking messages out of the message queue.
ISRs typically use non-interlocked, one-way communication. A task such as tSinkTask runs and waits on the message queue. When the hardware triggers an ISR to run, the ISR puts one or more messages into the message queue. After the ISR completes running, tSinkTask gets an opportunity to run (if it’s the highest-priority task) and takes the messages out of the message queue.
Remember, when ISRs send messages to the message queue, they must do so in a non-blocking way. If the message queue becomes full, any additional messages that the ISR sends to the message queue are lost.
7.7.2 Interlocked, One-Way Data Communication
In some designs, a sending task might require a handshake (acknowledgement) that the receiving task has been successful in receiving the message. This process is called interlocked communication, in which the sending task sends a message and waits to see if the message is received.
This requirement can be useful for reliable communications or task synchronization. For example, if the message for some reason is not received correctly, the sending task can resend it. Using interlocked communication can close a synchronization loop. To do so, you can construct a continuous loop in which sending and receiving tasks operate in lockstep with each other. An example of one-way, interlocked data communication is illustrated in Figure 7.7.
Figure 7.7: Interlocked, one-way data communication.
In this case, tSourceTask and tSinkTask use a binary semaphore initially set to 0 and a message queue with a length of 1 (also called a mailbox). tSourceTask sends the message to the message queue and blocks on the binary semaphore. tSinkTask receives the message and increments the binary semaphore. The semaphore that has just been made available wakes up tSourceTask. tSourceTask, which executes and posts another message into the message queue, blocking again afterward on the binary semaphore.
The pseudo code for interlocked, one-way data communication is provided in Listing 7.2.
The semaphore in this case acts as a simple synchronization object that ensures that tSourceTask and tSinkTask are in lockstep. This synchronization mechanism also acts as a simple acknowledgement to tSourceTask that it’s okay to send the next message.
7.7.3 Interlocked, Two-Way Data Communication
Sometimes data must flow bidirectionally between tasks, which is called interlocked, two-way data communication (also called full-duplex or tightly coupled communication). This form of communication can be useful when designing a client/server-based system. A diagram is provided in Figure 7.8.
Figure 7.8: Interlocked, two-way data communication.
Listing 7.2: Pseudo code for interlocked, one-way data communication.
tSourceTask() {
:
Send message to message queue
Acquire binary semaphore
:
}
tSinkTask () {
:
Receive message from message queue
Give binary semaphore
:
}
In this case, tClientTask sends a request to tServerTask via a message queue. tServer-Task fulfills that request by sending a message back to tClientTask.
The pseudo code is provided in Listing 7.3.
Listing 7.3: Pseudo code for interlocked, two-way data communication.
tClientTask () {
:
Send a message to the requests queue
Wait for message from the server queue
:
}
tServerTask () {
:
Receive a message from the requests queue
Send a message to the client queue
:
}
Note that two separate message queues are required for full-duplex communication. If any kind of data needs to be exchanged, message queues are required; otherwise, a simple semaphore can be used to synchronize acknowledgement.
In the simple client/server example, tServerTask is typically set to a higher priority, allowing it to quickly fulfill client requests. If multiple clients need to be set up, all clients can use the client message queue to post requests, while tServerTask uses a separate message queue to fulfill the different clients’ requests.
7.7.4 Broadcast Communication
Some message-queue implementations allow developers to broadcast a copy of the same message to multiple tasks, as shown in Figure 7.9.
Figure 7.9: Broadcasting messages.
Message broadcasting is a one-to-many-task relationship. tBroadcastTask sends the message on which multiple tSink-Task are waiting.
Pseudo code for broadcasting messages is provided in Listing 7.4.
Listing 7.4: Pseudo code for broadcasting messages.
tBroadcastTask ()
{
:
Send broadcast message to queue
:
}
Note: similar code for tSignalTasks 1, 2, and 3.
tSignalTask () {
:
Receive message on queue
:
}
In this scenario, tSinkTask 1, 2, and 3 have all made calls to block on the broadcast message queue, waiting for a message. When tBroadcastTask executes, it sends one message to the message queue, resulting in all three waiting tasks exiting the blocked state.
Note that not all message queue implementations might support the broadcasting facility. Refer to the RTOS manual to see what types of message-queue-management services and operations are supported.
Some points to remember include the following:
· Message queues are buffer-like kernel objects used for data communication and synchronization between two tasks or between an ISR and a task.
· Message queues have an associated message queue control block (QCB), a name, a unique ID, memory buffers, a message queue length, a maximum message length, and one or more task-waiting lists.
· The beginning and end of message queues are called the head and tail, respectively; each buffer that can hold one message is called a message-queue element.
· Message queues are empty when created, full when all message queue elements contain messages, and not empty when some elements are still available for holding new messages.
Читать дальше