This video presentation is the fourth installment of the Essentials of MQX RTOS Application Development training course. In this session, you will learn about synchronization techniques for managing data, task flow, and resources. You will be introduced to the various synchronization options available with MQX RTOS including events, semaphores, mutexes, and message passing.
This training was created by Embedded Access Inc., a Freescale sponsored training provider and proven partner.
|Session 4 Course Line||Lab Outline|
First, watch the video for Session 4: Synchronization and Message Passing.
Then, follow through with the interactive lab assignment below.
SESSION 4: LAB ASSIGNMENT
In this lab we will implement our first type of synchronization - Message Passing. We will also see the data flow of our application begin to take shape as 3 of our tasks (Accel, Temp, and Input Tasks) will be using Message Passing in order to send data to the Health Task.
The objective of this lab is to understand message passing and implement the message passing as outlined in (diagram of message passing between tasks).This objective will be accomplished by:
- Adding code to Health Task to send a message to Display task with health status
- Setting up Message Passing from Temp Task to Health Task
- Setting up Message Passing from Accel Task to Health Task and Theft Task
- Setting up Message Passing from Health Task to Display Task
- Setting up Message Passing from Input Task to Health Task
- Setting up bi-directional Message Passing between Health Task and CAN Task
- Ensuring that when each task starts, it sends a "Message from Task x" message to the tasks it talks to. When the Health task receives a message from a task, it should forward it to the Display task who will print it.
New functions/ structures you will use:
_msgq_get_id, _msg_alloc_system, _msgq_send, _msgq_open, _msgq_receive, _msg_free, MESSAGE_HEADER_STRUCT
- Since we will be using message passing you need to include message.h in main.h.
- Message Passing does not stipulate a structure for the messages and as far as it's concerned it's just sending a packet of bytes, so you need to define a structure for your messages. In our message structure the receiver will need to know who the message was sent from so we should have an entry for the 'message type'. An example of a message type is a report of the current temperature or a report of the current accelerometer data. The message type could simply be a 32 bit entry with a unique number used for each message type and each type is defined using #define statements. However it's a bit less error prone to use an enum structure to ensure that each message type gets a unique number. You'll see this technique used throughout the labs. Create a define for the following message types: TEMP_MESSAGE, ACCEL_MESSAGE, INPUT_MESSAGE, CAN_MESSAGE, TIMER_MESSAGE, ISR_MESSAGE, HEALTH_MESSAGE, UI_MESSAGE, DISPLAY_MESSAGE. Using an enum. Create an instance of this enum called 'APPLICATION_MESSAGE_TYPE_T'
- Message structures always start with a MESSAGE HEADER STRUCT and typically you follow that by the message type. The last thing we need in our message structure is a data field and for now we'll just use a single 32 bit entry for our data. Create a structure to define the format of our messages and create an instance of this structure called APPLICATION_MESSAGE.
- Each task that will receive a message will need a queue and each message queue needs a unique ID. Create the queue ID numbers for the Temp, Accel, Input, CAN, Health, and UI tasks (ie use defines INPUT_QUEUE, CAN_QUEUE, DISPLAY_QUEUE, and THEFT_QUEUE). Again an enum should be used
- Somewhere in your system you need to create the system pool of message buffers and this needs to be done before anyone tries to use one of the buffers. So it makes sense that this goes in the initialization code of the highest priority task. Use the msgpool_create_system() function to do this. Use a message size of "sizeof (*msg)" where msg is a pointer to the message structure APPLICATION_MESSAGE. A size of 10 buffers should be adequate and there should be no reason for the size of the pool to grow.
INITIATING MESSAGES (ACCEL TASK, TEMP TASK, INPUT TASK)
- Each task that sends a message to Health Task will need to get the ID of the queue for the Health Task so it can send messages to it. This can be done using the msgq_get_id() function.
- The body of theses sending tasks then will be to do the following:
- Wait for a time delay to space out it's messages
- allocate a message using msg_alloc_system(),
- populate the target queue ID of the receiver (ie the Health Task),
- populate the message type (eg ACCEL_MESSAGE),
- set the message data (can be set to 0 for now since we don't have any real data to send. This will be updated later.
- Send the message using msgq_send()
- Repeats the above process
- The time delay each task waits between sending a message will vary based on the desired frequency for each type of information. The accelerometer data is important to get on a fairly frequent basis so perhaps a time delay of 500ms would be best here. The temperature won't change very quickly so a delay of 5,000ms would work best here and the voltage reading being done by the Input Task can be set to 2,000ms.
PROCESSING MESSAGE (HEALTH TASK)
- Since the Health Task will be receiving messages it needs to open the message queue which can be done with msgq_open () function. And since it will be sending messages to the Display Task it will need to know the queue id of the Display Task's queue which it can get with the msgq_get_id() function.
- The body of the Health Task then uses msgq_receive() to fetch a message from it's queue (which is a blocking function), prints out a message to say that it received a message with the message number, passes the message on to the Display Task using msgq_send(), and then waits for the next message to come in.
DISPLAYING THE MESSAGES (DISPLAY TASK)
- As was done with the Health Task, the Display Task will need to open its message queue before it can read messages and this is done with the msgq_open() function.
- The body of the Display Task will block on msgq_receive(), once a message is received it will print a message to say that it received a message with the message number, frees up the message so it can be returned to the system pool using msg_free(), and then waits for the next message.
- Compile and run your application. We should be receiving all 3 types of messages at both the Health Task and the UI Task, but now each at the same frequency because we used different time delays. However, you will only be getting a small number of messages at the Health Task and then the printouts will stop. Why do you think this is? Pause the application and look at the MQX Task Summary window in TAD for a clue.
- 14. Fix the problem and re-run your application. Verify that both the Health Task and Display Task print out the messages of all 3 message types (Accel, Temp, and Input).
Need more help? The full source code for this lab can be found in the 'Lab Source Code' folder here.