<< Chapter < Page | Chapter >> Page > |
if (QUE_empty(&queue)) { // Check to see if the queue is empty
LOG_printf(&trace,"queue error\n"); // Print something if it is empty
// If the queue is empty you probably will not want to proceed}
msg = QUE_get(&queue); // If there is a message, dequeue it
// use the message here// After using the message, free the memory
// This tells MEM_free the location of the msg and size so it// can remove it from memory
MEM_free(0, msg, sizeof(MsgObj)); // Free up the memory
Functions
QUE_put
and
QUE_get
are atomic in that they add and remove elements from the queue with interrupts turned off. Therefore there should not be a problem of more than one task trying to access the queue at the same time. The function
QUE_get
is also non-blocking so the tasks should determine if there are any elements on the queue before calling
QUE_get
.
A semaphore can be used to count the number of elements on a queue and be used to block a task that needs access to a queue. Figure 2 shows how one task
TSK0
will write to a queue,
QUE0
, and the other task
TSK1
will read from the queue. Also, the semaphore
QUE0_SEM
is used to keep track of how many elements are on the queue. After
TSK0
puts a message on the queue it will call
SEM_post
and increment the semaphore. Before reading from the queue,
TSK1
will call
SEM_pend
on the semaphore and if the task does not block, there is an element on the queue. If there are no elements on the queue, the task will block on the semaphore.
For the setup with one queue and one semaphore notice that if the program ran for a long time the code would have to continually allocate memory for a message and then de-allocate it when it was done using the message. This could take up a substantial amount of time and could cause fragmentation of the memory space. A better method is to have two queues where one queue holds messages that are free and one holds messages that contain data being transmitted from one task to another. Figure 3 shows the same setup in Figure 2 except there is now a queue that contains free messages or empty messages.
During the initialization phase of the program, memory for empty messages is allocated and the messages are put in the
QUE_Free
queue and the semaphore
QUE_Free_SEM
is incremented for each message put on the queue. When
TSK0
needs to send a message to
TSK1
it will check
QUE_Free_SEM
to see if there are any free messages. If not, it will block. If there are free messages it will take one off of
QUE_Free
after the
QUE_Free_SEM
is decremented and then fill the message with data and put it on the queue
QUE0
. The semaphore QUE0_SEM is incremented after the message is put on
QUE0
.
The task
TSK1
will block on
QUE0_SEM
until task
TSK1
puts a message on the queue. Then it will decrement the semaphore and use the message. When it is done it will put the message on
QUE_Free
and increment its semaphore.
This is a very simple example to demonstrate the structure of a program that uses queues. In the example we will assume that DSP/BIOS has been set up with two tasks, TSK0 and TSK1, and one queue, QUE0. The two tasks have the same priority and TSK0 is set to execute first. The code for the example follows.
#include<std.h>// Target definition header
#include<sys.h>// DSP/BIOS config/error header
#include<log.h>// LOG module header
#include<mem.h>// MEM module header
#include<que.h>// QUE module header
#include<tsk.h>// TSK module header#include "QUE_Examplecfg.h" // header generated by QUE_Example.tcf config filetypedef struct MsgObj {
QUE_Elem elem; /* first field for QUE */Int val; /* message value */
} MsgObj, *Msg;Void main()
{}
// TSK0 will generate two messages and put them on the queueVoid funTSK0()
{Msg msg; // Pointer to the message object
// allocate memory for first messagemsg = MEM_alloc(0, sizeof(MsgObj), 0);
if (msg == MEM_ILLEGAL) {// If the memory allocation fails, abort
SYS_abort("Memory allocation failed!\n");}
msg->val = 1; // put the message number in the message
// print the message numberLOG_printf(&trace, "Writing message %d", msg->val);
// Put the message on the queueQUE_put(&QUE0, msg);
// repeat for the second message
msg = MEM_alloc(0, sizeof(MsgObj), 0);if (msg == MEM_ILLEGAL) {
// If the memory allocation fails, abortSYS_abort("Memory allocation failed!\n");
}msg->val = 2; // put the message number in the message
// print the message numberLOG_printf(&trace, "Writing message %d", msg->val);
// Put the message on the queueQUE_put(&QUE0, msg);
}// TSK1 will get two messages from the queue
Void funTSK1(){
Msg msg; // Pointer to the message object// If the queue is empty, we should not proceedif (QUE_empty(&QUE0)) {
LOG_printf(&trace,"TSK1 queue error");
return; // This will make the task terminate}
// Get the message off the queuemsg = QUE_get(&QUE0);
// print value in the messageLOG_printf(&trace, "Reading message %d", msg->val);
// Since we are done with the message, free the memoryMEM_free(0, msg, sizeof(MsgObj));
// Repeat for the second message
// If the queue is empty, we should not proceedif (QUE_empty(&QUE0)) {
LOG_printf(&trace,"TSK1 queue error");
return; // This will make the task terminate}
// Get the message off the queuemsg = QUE_get(&QUE0);
// print value in the messageLOG_printf(&trace, "Reading message %d", msg->val);
// Since we are done with the message, free the memoryMEM_free(0, msg, sizeof(MsgObj));
}
It is important to free the memory of each message after it is used so that the memory does not get used up. The result of the run follows.
Writing message 1
Writing message 2Reading message 1
Reading message 2
Notification Switch
Would you like to follow the 'Ti dsp/bios lab' conversation and receive update notifications?