Session 13: Memory Services

Document created by Gabriela Godinez Employee on Jun 30, 2016Last modified by Gabriela Godinez Employee on Jun 30, 2016
Version 2Show Document
  • View in full screen mode

This video presentation is the thirteenth installment of the Essentials of MQX RTOS Application Development training course. In this session, you will be introduced to Memory Services.

This training was created by Embedded Access Inc., a Freescale sponsored training provider and proven partner.

Session 13 Course LineLab Outline
  • Different uses for RAM in an Embedded System
  • Where different types of data are stored
  • Creating memory pools
  • Allocating memory from a memory pool
  • Creating partitions
  • Allocating memory from a partition
  • Light Weight Memory Manager vs Full Featured Memory Manager
  • Adding a queue to be used for logging health records
  • Adding a timer and a timer ISR
  • Adding the Health Record structure
  • Processing messages sent to the Health Task to fill in the Health Record entries and store them in the queue
  • Adding a new UI command to print out the Health Records

 

First, watch the video for Session 13: Memory Services.

Then, follow through with the interactive lab assignment below.

SESSION 13: LAB ASSIGNMENT

INTRODUCTION

In this lab we will add the logging of data to memory that is allocated from the system memory pool. We will continue to flesh out our application in this lab and we will focus on the Health Task. The Health Task receives data from various tasks through Message Passing. When a periodic timer expires the Health Task will place the data it has received onto a queue and the UI Task will print out that data. Queue structures have not been covered so far in the course so you may want to review this in the MQX Reference Manual briefly, but it should be quite quick for you to pick this up. A queue is simply a sequential list of data which you can read from and add to.

Note that our implementation does not put a cap on the amount of data that could eventually be allocated, so eventually the system will run out of memory and additional health records will not be logged. For a system that you intend to deploy to the field it would obviously be good practice to not exhaust the available memory and an ideal way to do this is with a partition. This lab can be updated on your own to allocate memory from a partition you define instead of allocating memory directly from the system pool.

OBJECTIVE

The objective of this lab is to use Memory Services to support the logging of health data for our application. We will also cover the use of queues and will implement a timer. Light Weight Timers were covered in session 8.

ASSIGNMENT

ADDING A QUEUE

    1. The first thing we need to do is to add a Queue Structure and the best place to locate this is in main.c where our other global structures are located. Add the following line, and make sure that it is also declared as an extern in main.h.
                 QUEUE_STRUCT     log_queue; 
    2. In the Health Task, add the following init function for our log_queue. The '0' parameter indicates that the queue is unbounded and will grow to any size, assuming the system has the memory.
                 _queue_init(&log_queue, 0 ); 

ADDING A TIMER

    1. In order to set up a timer we'll need a structure to be declared in the Health Task of type LWTIMER_PERIOD_STRUCT and another one of type LWTIMER_STRUCT.
    2. In the initialization section of the Health Task create the periodic queue with the _lwtimer_create_periodic_queue() function that has a period of 1 second and no wait time. Note that for this BSP of MQX the define BSP_ALARM_FREQUENCY is set to 200 ticks and each tick is 5 msec, so it represents 1 second. Then add a timer to the queue using the _lwtimer_add_timer_to_queue() function. You do not need an offset and the timer should call a 'health_timer' ISR function that will be defined later. The parameter to pass is the Health Task Queue ID. Note that the 'my_qid' variable isn't valid until after the _msgq_open() function so these new lines should go after the _msgq_open() call.
      cheat.png
      Cheat 13-1.png

CREATING THE TIMER FUNCTION

    1. At the top of HealthTask.c create the 'health_timer' function that will be called when the timer expires. It will receive the Health Task's queue ID as its only parameter, which will have to be converted into a '_queue_id' type.
    2. This function will send a message to the Health Task essentially to let it know that it's time to write a log entry of the current values. As was done elsewhere in the application, declare a message of type 'APPLICATION MESSAGE *', set the target queue id, set the message type to be 'LOG_TICK_MESSAGE' and then send the message. That's all that this function needs to do. This new message type needs to be added to the 'APPLICATION_MESSAGE_TYPE_T' data structure in main.h.
      cheat.png
      Cheat 13-2.png

HEALTH INFORMATION STRUCTURE

    1. The Health Task needs a structure to hold all of its health data so in main.h declare a structure as shown below. Since this is going to be copied to a queue, the first element should be of type QUEUE_ELEMENT_STRUCT which MQX uses to manage queue entries. The structure should also have a number to indicate which entry it is in the queue, the temperature, the voltage, and the accelerometer x, y, and z data.
      typedef struct { 
         QUEUE_ELEMENT_STRUCT    QE;
           uint32_t               NUM;
           uint32_t               TEMP;
           uint32_t               MV;
           uint16_t               X;
           uint16_t               Y;
           uint16_t               Z;
      } HEALTH_RECORD;
    2. Declare a pointer of type 'HEALTH_RECORD *' at the top of the Health Task.
    3. In the Initialization section of the Health Task (ie before the while(1) loop) uses the _mem_alloc_system_zero() function to allocate memory for an instance of "HEALTH_RECORD" from the system pool that is set to zero. For now you don't need to check if this was successful or not, we'll do that later.
      cheat.png
      Cheat 13-3.png
    4. Declare a 32 bit variable that will be used to count the number of health records, and after the first one has been created (in step 9 above), set this variable to 1.

UPDATE THE PROCESSING OF MESSAGES

    1. In the while(1) loop of the Health Task it checks if an incoming message is from the Temp Task, and if there is an over temperature condition a message is sent to the Display Task. The Health Task now needs to be processing several types of messages so it would make sense that a switch statement on the received message type is used. Add a case to the switch to handle messages of type TEMP_MESSAGE that will contain the same functionality that is already in the while(1) loop for messages of type TEMP_MESSAGE. And add another case for messages of type LOG_TICK_MESSAGE which for now won't do anything, we'll fix that later.
      cheat.png
      Cheat 13-4.png
    2. When a TEMP_MESSAGE is received we need to store the passed in temperature, so in the case that handles TEMP_MESSAGEs update the "TEMP" parameter of the health record to be the passed in temperature in this message. Here is where it would be a good idea to first check if our health record != NULL.
      cheat.png
      Cheat 13-5.png
    3. Add in a case for ACCEL_MESSAGEs which will record the passed in x, y, and z axis motion values into the health record, assuming it's valid.
    4. Add in a case for ADC_MESSAGEs which will record the passed in voltage into the health record, assuming it's valid.
    5. We are now ready to add the handling of a LOG_TICK_MESSAGE. Since the health record has been updated when the other messages were processed we only need to save the record number (count) into the health record, increment the record number for next time, and then add this health record to the queue using the _queue_enqueue() function like this:
      _queue_enqueue(&log_queue, &health_record->QE)
    6. The Health Task is done with the health record since it just put it on the queue so the 'health_record' pointer should be set to NULL so the rest of your code doesn't try to use it. A valid health_record pointer is required for the next record of course and it is required now so its fields can be filled in as new messages arrive. Use the _mem_alloc_system_zero() function as was done before to allocate memory for another record.
      whiteboard.png
      Whiteboard 13-1.jpg
      cheat.png
      Cheat 13-6.png
    7. Currently, at the end of the while(1) loop all received messages are being passed on to the Display Task. However, we don't want to pass on messages of type LOG_TICK_MESSAGE so the code needs to be updated such that it only passes on the other types of messages. This could be done with a test for msg->MESSAGE_TYPE != LOG_TICK_MESSAGE condition, but to be more generic and accommodating of future message types that shouldn't be passed on to the Display Task it might be better for the handling of the LOG_TICK_MESSAGEs to free the message using the _msg_free() function and to set the 'msg' pointer to NULL. Then at the end of the while loop check that only non-NULL messages are sent to the Display Task.
      cheat.png
      Cheat 13-7.png

UPDATING THE UI TASK

  1. It is intended that the health records can be printed out using the UI Task. This is a menu driven interface and so we need to add a new command to read the log file. In UiTask.c add a case for the user pressing the letter 'l' (lower case L) for the log file and get the health record from the queue using the queue_dequeue() function. It can be a good idea to us a small function for items like this since there can be multiple places in your code that need to do a similar task. Such a function could look like this:
    HEALTH_RECORD  *      get_log_record(void) 
    {
          return (HEALTH_RECORD * )   _queue_dequeue(&log_queue);
    }
  2. Add print statements for the record number and all other data that is in the health record. Free up the memory of the health record once you're done with it to avoid a memory leak.
  3. This will cover the printing of the first record but the intention is for this UI command to print out all health records that are stored on the queue. Use a while loop to fetch records from the log using our new get_log_record() function until this function returns a NULL entry indicating that there are no more records to fetch from the queue. Note that the function returns a pointer to a HEALTH_RECORD, so a variable in the UI_TASK of this type will have to be declared.
    cheat.png
    Cheat 13-8.png
  4. Compile and run your code. Use the user interface to request the health logs to be printed out and confirm that the data is correct. You can adjust the potentiometer and move the tower board to change the data. You may also want to comment out the print messages from the Health Task and Display Task so these messages don't interfere.
    results.png
    Results 13-1.png

Need more help? The full source code for this lab can be found in the 'Lab Source Code' folder here.

Attachments

    Outcomes