Using the ADC and ThreadX RTOS Messaging Framework on the Renesas DK-S124

Created by Austin Oltmanns, last modified on Jun 21, 2017

This is the third guide for the Renesas DK-S124. In it, instructions for using the Analog to Digital Converter (ADC) will be given as well as instructions for configuring multiple threads which communicate through the ThreadX RTOS Messaging Framework. To view output from the ADC, the information will be sent over UART from a separate thread. This guide assumes you have a working installation of e2 studio and the Synergy Software Platform. It also assumes you have completed the previous guide detailing how to use the UART module, where instructions were given on creating a new thread in e2 studio and adding modules to the thread. At this point, you should be familiar with uploading programs to the DK-S124 using e2 studio and creating projects with the proper configuration. However, if you need a refresher please refer to the previous tutorials.

This guide will cover adding the proper modules for using the ADC and configuring the threads, setting up the Messaging Framework so the threads can communicate, polling multiple input sources with the ADC (namely the potentiometer and the light sensor on board the DK-S124) and sending this information over UART from a separate thread. For this guide, the following documents will serve as a reference for any material not covered; however, this guide aims to be a standalone document.

Dev-Board User’s Manual: https://www.renesas.com/en-us/doc/products/renesas-synergy/doc/r12um0006eu0100-synergy-dk-s124.pdf

S124 Data Sheet: https://www.renesas.com/en-us/doc/products/renesas-synergy/doc/r01ds0264eu0100_synergy_s124.pdf

S124 User’s Manual: https://www.renesas.com/en-us/doc/products/renesas-synergy/doc/r01um0003eu0120-synergy-s124.pdf

Synergy Software Package User’s Manual: https://synergygallery.renesas.com/media/products/1/156/en-US/r01us0171eu0096_synergy_ssp.pdf

Basics of Synergy Software Platform Book: https://www.renesas.com/en-us/media/products/synergy/book/Basics_of_the_Renesas_Synergy_Platform_1703.pdf

Adding the proper modules and configuring the threads

To start this guide, make a new Synergy C Project in e2 studio. Name the project as you see fit, this guide will use ADCUART as the name. Ensure that the proper device and board are selected in the project configuration page, “Board” should be “S124 DK” and “Device” should be “R7FS124773A01CFM”. Choose “BSP” as the project template.

Once the project has loaded, open the “configuration.xml” in the root of the project. For this project, 3 threads will be used. The HAL/Common thread cannot use the messaging framework so it will simply sit. The second thread will be used to read values from the ADC and the third thread will send information received from the second thread via ThreadX Messaging Framework over UART. For this simple example, it might not be necessary to use three threads, but the point of the guide is to illustrate how to use the ThreadX RTOS Messaging Framework in addition to the ADC.

Open the “Threads” tab and click the add new thread button. This thread will be the ADC thread; you may change the name accordingly if you like. Add the ADC Driver stack by pressing the “add stack” button in the corner of the “HAL/Common Stacks” window. It is located in Driver > Analog > ADC Driver on r_adc. Select this stack and open the properties tab (located at the bottom of the screen if you are using the “Synergy Configuration” perspective). Change the “Resolution” to “14-bit”, make sure “Alignment” is “Right”, “Clear after read” should be “On”, “Mode” should be “Single Scan”. Select Channels 0,1 and 7 to be “Use in Normal/Group A” to use the light sensor, on board temperature sensor and potentiometer respectively. Be sure that “Normal/Group A Trigger” is set to “software” and the rest of the settings can be left as is. In the end, your Properties should be like this:

image



These concepts should be familiar from the last guide, Configuring and Using the UART Interface on the DK-S124 . These settings will allow us to use the ADC for three different devices and control when the scan starts via a function call.

Now, create another thread. This new thread will be the UART thread. Its configuration should be the same as it was in the last tutorial, i.e. “Name of UART callback function to be defined by user” should be “NULL” as it won’t be used in this example and the Receive Transmit and Transmit End Interrupt Priorities should be set to 0, 1 or 2 so that they are enabled.

Setting up the Messaging Framework

To set up the Messaging Framework, a Messaging Framework instance must be added to one of the non-HAL/Common threads. For this guide, the stack will be added to the ADC Thread. Click the New Stack button and choose Framework > Services > Messaging Framework. Both threads will communicate using this one instance.

Now, change over to the Messaging tab located right next to the Threads tab. Click the new Event Class button from the “Event Classes” window pane. Enter “Conversions” into the “Name” field and the rest of the fields will fill in kind. Press “OK” to create the new event class.

Next, add a new event. Press the new event button in the “Events” window pane. Fill in the name field with “ConversionDone” and the symbol field will fill in as well. After that, go to the Conversions Subscribers window pane and add a new one. Use the drop-down menu to select the UART thread. You may leave the “start” and “end” values as 0 as there is only one event class. All the configuration has been done at this point, and you may press the “Generate Project Content” button to generate all but one of the needed files. The one that remains is the custom definition of the “ConversionDone” Event.

If you would like more details on the Messaging Framework, please refer to sections 4.2.13 and 5.12 of the Synergy Software Platform User’s Manual. But beware, some of the documentation is out of date and incomplete at the time of writing of this guide. However, between the source code and the User’s Manual it is possible to derive the steps that follow.

For the sake of sanity, this project is split into several steps to ensure that each piece is working throughout development. That means that a test of the Messaging Framework setup will be done before moving on to writing the ADC code. This is a good development method that ends up saving time throughout a project because problems can be spotted and their cause determined as soon as something breaks.

The next step in setting up the Messaging Framework is to create a header with the same name as the “Payload header file” property in the “Conversions” “Event Class”.

To create the file, right click on the “src” directory in the ADCUART project and select New > Header File. In the name field, be sure to enter the same text that is in the “Payload header file” property. In this case, it is “conversions_api.h”

You now have three files to fill with code, they will be covered one at a time. First is conversions_api.h, it should look like this:

#ifndef _MYMESSAGE_ //header guard
#define _MYMESSAGE_
#include "sf_message_api.h" //this is a message, so the message api is needed
typedef struct conversions_payload_s
{
    sf_message_header_t header; //every message must include a header of this type
    uint8_t conversionchannel; //this is the channel the ADC conversion came from
    uint16_t conversiondata; //this is the data from the conversion
} conversions_payload_t; //This name is specified in "Event Class" properties as "Payload Type"
#endif

Next, the adcthread_entry.c file. It is set up to make a test message and post it.

#include <conversions_api.h>
#include "adcthread.h"
/* ADC Thread entry function */
void adcthread_entry(void)
{
    ssp_err_t error; //place for error codes from buffer acquisition to go
    sf_message_header_t * p_buffer; //pointer for the buffer that must be acquired
    sf_message_acquire_cfg_t acquireCfg = {.buffer_keep = true}; //buffer can be kept,
                                                                 //as messages will be posted repeatedly
                                                                 //and there are no other processes that
                                                                 //require the buffer
    do {
    error = g_sf_message0.p_api->bufferAcquire(g_sf_message0.p_ctrl, &p_buffer, &acquireCfg, 300);
    } while (error!=SSP_SUCCESS);
    //keep trying to acquire the buffer, waiting up to 300 processor ticks each time
    sf_message_post_err_t errPost; //place for posting error codes to go
    sf_message_post_cfg_t post_cfg =
        {
          .priority = SF_MESSAGE_PRIORITY_NORMAL, //normal priority
          .p_callback = NULL //no callback needed
        };
    conversions_payload_t * mypayload; //pointer to the message payload
    mypayload = (conversions_payload_t *) p_buffer; //cast buffer to our payload
    mypayload->header.event_b.class_code = SF_MESSAGE_EVENT_CLASS_CONVERSIONS; //set the event class
    mypayload->header.event_b.class_instance = 0; //set the class instance
    mypayload->header.event_b.code = SF_MESSAGE_EVENT_CONVERSIONDONE; //set the message type
    mypayload->conversionchannel = 7; //just some dummy information for testing
    mypayload->conversiondata = 13141;
    while (1)
    {
        g_sf_message0.p_api->post(g_sf_message0.p_ctrl, (sf_message_header_t *) mypayload,
                                  &post_cfg, &errPost, 300); //post the message
        tx_thread_sleep (1);
    }
}

Finally, uartthread_entry.c is set up to wait for incoming messages and then post some of the content over UART. The UART related code should look familiar from the last guide.

#include <conversions_api.h>
#include "uartthread.h"
/* UART Thread entry function */
void uartthread_entry(void)
{
    uint8_t cstr[] = "a"; //the text to be sent, stored as unsigned 8 bit data.
    g_uart0.p_api->open(g_uart0.p_ctrl, g_uart0.p_cfg); //initialization of the UART module
    sf_message_header_t * pHeader; //pointer to the message header
    conversions_payload_t * thepayload; //pointer to the message payload
    while (1)
    {
        g_sf_message0.p_api->pend(g_sf_message0.p_ctrl, &uartthread_message_queue,
                                  &pHeader, TX_WAIT_FOREVER); //wait for a message forever
        if (pHeader->event_b.class_code == SF_MESSAGE_EVENT_CLASS_CONVERSIONS) //if the message if the right kind
        {
            thepayload = (conversions_payload_t *) pHeader; //cast the received message to the custom type
            if (thepayload->header.event_b.code == SF_MESSAGE_EVENT_CONVERSIONDONE) //if the message event is the right kind
            {
                //spit out to UART
                cstr[0] = thepayload->conversionchannel + '0'; //quick and dirty int to char
                g_uart0.p_api->write(g_uart0.p_ctrl, cstr, 1); //send the information over UART
            }
        }
        tx_thread_sleep (1);
    }
}

After you have these three files entered properly, press build. After the project builds, you should have the board plugged in via USB through the debugging port and may press the debug button to upload the program. If you would like to see output from your program, you should configure PuTTY and a USB to RS232 Serial cable much like the previous example to view it. If everything has been entered properly, your terminal should fill with the number 7. Congratulations, you have now configured the Messaging Framework with a custom event!

Using the ADC

Configuring and using the ADC is very similar to using the UART thanks to Renesas’s HAL Modules. To use it, only a few more lines are needed in the source code. The new files are embedded below.

#ifndef _MYMESSAGE_ //header guard
#define _MYMESSAGE_
#include "sf_message_api.h" //this is a message, so the message api is needed
typedef struct conversions_payload_s
{
    sf_message_header_t header; //every message must include a header of this type
    uint8_t conversionchannel[3]; //this is the channel the ADC conversion came from
    uint16_t conversiondata[3]; //this is the data from the conversion
} conversions_payload_t; //This name is specified in "Event Class" properties as "Payload Type"
#endif

Note that each data field is an array of 3 values, one per ADC value.

#include <conversions_api.h>
#include "adcthread.h"
/* ADC Thread entry function */
void adcthread_entry(void)
{
    g_adc0.p_api->open(g_adc0.p_ctrl, g_adc0.p_cfg); //open the adc with configuration from the Synergy Configurator
    g_adc0.p_api->scanCfg(g_adc0.p_ctrl, g_adc0.p_channel_cfg); //more configuration
    ssp_err_t error; //place for error codes from buffer acquisition to go
    sf_message_header_t * p_buffer; //pointer for the buffer that must be acquired
    sf_message_acquire_cfg_t acquireCfg = {.buffer_keep = true}; //buffer can be kept,
                                                                 //as messages will be posted repeatedly
                                                                 //and there are no other processes that
                                                                 //require the buffer
    do {
    error = g_sf_message0.p_api->bufferAcquire(g_sf_message0.p_ctrl, &p_buffer, &acquireCfg, 300);
    } while (error!=SSP_SUCCESS);
    //keep trying to acquire the buffer, waiting up to 300 processor ticks each time
    sf_message_post_err_t errPost; //place for posting error codes to go
    sf_message_post_cfg_t post_cfg =
        {
          .priority = SF_MESSAGE_PRIORITY_NORMAL, //normal priority
          .p_callback = NULL //no callback needed
        };
    conversions_payload_t * mypayload; //pointer to the message payload
    mypayload = (conversions_payload_t *) p_buffer; //cast buffer to our payload
    mypayload->header.event_b.class_code = SF_MESSAGE_EVENT_CLASS_CONVERSIONS; //set the event class
    mypayload->header.event_b.class_instance = 0; //set the class instance
    mypayload->header.event_b.code = SF_MESSAGE_EVENT_CONVERSIONDONE; //set the message type
    //mypayload->conversionchannel = 7; //just some dummy information for testing
    //mypayload->conversiondata = 13141;
    adc_register_t scanchannels[] = {ADC_REG_CHANNEL_0,ADC_REG_CHANNEL_1,ADC_REG_CHANNEL_7};
    for (int index=0;index<3;index++)
        mypayload->conversionchannel[index] = scanchannels[index]; //load the channel data, it won't change
    while (1)
    {
        g_adc0.p_api->scanStart(g_adc0.p_ctrl); //start the scan
        do {
            error = g_adc0.p_api->scanStatusGet(g_adc0.p_ctrl);
        } while (error != SSP_SUCCESS); //impatiently wait for scan to finish
        for (int index=0;index<3;index++)
        {
            g_adc0.p_api->read(g_adc0.p_ctrl, scanchannels[index], mypayload->conversiondata+index); //load conversion data
        }
        g_sf_message0.p_api->post(g_sf_message0.p_ctrl, (sf_message_header_t *) mypayload,
                                          &post_cfg, &errPost, 300); //post the message
        tx_thread_sleep (1);
    }
}
#include <conversions_api.h>
#include "uartthread.h"
#include <stdio.h>
 
/* UART Thread entry function */
void uartthread_entry(void)
{
    uint8_t cstr[] = "Channel X: 12345 \nChannel X: 12345 \nChannel X: 12345 \n"; //the text to be sent, stored as unsigned 8 bit data.
    g_uart0.p_api->open(g_uart0.p_ctrl, g_uart0.p_cfg); //initialization of the UART module
    sf_message_header_t * pHeader; //pointer to the message header
    conversions_payload_t * thepayload; //pointer to the message payload
    while (1)
    {
        g_sf_message0.p_api->pend(g_sf_message0.p_ctrl, &uartthread_message_queue,
                                  &pHeader, TX_WAIT_FOREVER); //wait for a message forever
        if (pHeader->event_b.class_code == SF_MESSAGE_EVENT_CLASS_CONVERSIONS) //if the message if the right kind
        {
            thepayload = (conversions_payload_t *) pHeader; //cast the received message to the custom type
            if (thepayload->header.event_b.code == SF_MESSAGE_EVENT_CONVERSIONDONE) //if the message event is the right kind
            {
                //spit out to UART
                for (int index=0; index<3; index++)
                {
                    cstr[8 + index*18] = thepayload->conversionchannel[index] + '0'; //quick and dirty int to char
                    sprintf(cstr+11 +index*18, "%5d", thepayload->conversiondata[index]); //int to string at 11th index of cstr
                    g_uart0.p_api->write(g_uart0.p_ctrl, cstr, 54); //send the information over UART
                }
            }
        }
        tx_thread_sleep (1);
    }
}

After you have made these changes, build, upload and open your serial monitor. You should see data scream across your terminal from each source on the ADC. Try adjusting the potentiometer or waving your hand above the light sensor to verify that things are working. If you wish to freeze the data coming in, you can press the X on the PuTTY terminal window and a confirmation dialogue will ask if you are sure you would like to close. Meanwhile, the terminal will be frozen and you can take a good look at the numbers.

Congratulations again! You are now able to use the Renesas Messaging Framework as well as the ADC and UART on the DK-S124. If you would like to jump deeper into how each of these subsystems work, please refer to Renesas’s documentation. The Synergy Software Platform User’s Manual contains most of the instructions necessary; however, at the time of writing this guide, it is outdated and missing some key information on how to configure the Messaging Framework. Nonetheless, it serves as a useful reference for many details of the Renesas Synergy Platform.

Questions/Comments

Any questions or comments please go to Digi-Key’s TechForum