Child pages
  • Using the GPIO's and Timer Module's on the Renesas DK-S124 with the HC-SR04 Ultrasonic Sensor
Skip to end of metadata
Go to start of metadata

This is the 4th guide for using the Renesas DK-S124. This guide will cover using the GPIO’s of the Renesas DK-S124 to poll an input pin after sending a trigger pulse on another pin via a timer configured in one-shot mode. It will use concepts from the previous guides such as setting up and using threads from Renesas’s ThreadX RTOS platform and sending information over UART. You will find again that due to Renesas’s HAL (Hardware Abstraction Layer), most of the configuration can be done with a few clicks instead of setting several registers by hand. However, there is currently one missing feature in the SSP which requires one register to be set by hand. Like before, this guide aims to be standalone; however, attached below are some useful reference documents if you would like to dive deeper into how the platform works.

This guide will be using the HC-SR04, available for less than $4 from Digi-Key: https://www.digikey.com/short/30878b

This sensor is a cheap ultrasonic ranging sensor which operates by first waiting for an approximately 10uS pulse on the TRIG pin. Once this pulse is received, a small pulse of ultrasonic sound is emitted and the ECHO pin is brought high. When the sensor receives the echo from this pulse from some object, it brings the ECHO pin back low. The time that the ECHO pin was high can then be multiplied by the speed of sound and divided by 2 (there and back) to find how far away the object was.

For using this sensor and for any other prototyping/development you would like to do, a breadboard and hookup wire is recommended for making quick, temporary connections. These are also available at Digi-Key for a reasonable price. This one even includes some hook-up wire: https://www.digikey.com/short/308tcj

Links:

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

Contents of this guide:

Setting up the Project

By now, you should be comfortable opening e2 studio and creating a new Synergy C Project for the board. After entering a name for your project (ultrasonicGPIO in this guide), remember to double check that the correct board and device have been chosen. Like in the last guide, this project will be using the BSP project template. If you are not comfortable with these steps, please refer to the previous guides.

Next, like in the last guide, make two new threads. One will be the thread that polls the ultrasonic sensors and the other will be the thread that sends information over UART. This time, there will be some additional processing done in the sensor thread before the information is sent to the UART thread. Remember to name the threads something useful so you can tell them apart at a glance. The code in this guide assumes they are named "ultrasonic_thread" and "uartthread".

To configure the threads for inter-communication and UART use, the steps will be identical to the last guide. First, add the Messaging Framework to the Ultrasonic Thread. Then, in the UART thread, add the UART module. Remember to configure the interrupts for the UART module as was done in the previous tutorials as well as set the user defined callback function to NULL. Now go to the Messaging tab. The code from last time can be reused if the messages are set up identically. To do this, make a new event class called “Conversions” and then add an event called “ConversionDone”. Remember to add the UART Thread as a subscriber. When the code is being added, the old file will be copied to this new project.

Two more modules must still be added at this point: the timers. From the Ultrasonic Thread, add the Timer Driver on r_gpt twice from the “Ultrasonic Thread Stacks” pane. One timer will be used to generate the 10uS pulse and the other will be used to measure the pulse length from the ultrasonic sensor. For the first timer, g_timer0, make sure your settings match those below in the image. 

And for the second timer, g_timer1:

After you have done the above, change to the “Pins” tab of the Synergy Configurator. Now the GPIO pins will be configured for this project. Go to P4 under Ports and select P400. This pin will be the input from the echo output of the ultrasonic sensor. You should be able to change its mode to “Input mode” and change “Pull up” as well as “IRQ” to “None”. If you are unable to make these changes, make sure that any additional peripherals that may be using P400 are not in use/disabled. You can look at “Port Capabilities” to see which peripherals can use this port. Also, the e2 studio can be finicky about which order things are enabled and disabled. Be sure to disable the peripheral first, then try changing the mode of the pin.

Next, go to P401, this will be the output pin for the 10uS pulse from g_timer0 on GPT channel 6. Note that you can’t actually make the required changes here and must instead go to Peripherals>Timer:GPT>GPT6. From here, change “Pin Group Selection” to “Mixed”, “Operation Mode” to “GTIOCA or GTIOCB”, “GTIOCA” to “None” and “GTIOCB” to “P401”.

At this point, you may save the configuration and press “Generate Project Content” to create some of the source files that will be edited in the next section.

The Code

First, if you still have the project from the last guide (ADCUART), copy and paste the conversions_api.h file from the src directory of the old project into the src directory of the new project. If you do not have it, you may find it embedded below. Note that as there is only one conversion being done this time, so you may change conversionchannel and conversiondata fields to be an array of one, not an array, or even omit the conversionchannel if you choose. This guide will use an array of length one so the example could easily be expanded to multiple sensors if needed and so most of the code from last time can be reused. Additionally, a preprocessor define is added to more easily support different numbers of conversions being posted at once.

These should be your conversions_api.h and uartthread_entry.c files.

conversions_api.h
#ifndef _MYMESSAGE_ //header guard
#define _MYMESSAGE_
#define NUMCHANNELS 1
#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[NUMCHANNELS]; //this is the channel the ADC conversion came from
    uint16_t conversiondata[NUMCHANNELS]; //this is the data from the conversion
} conversions_payload_t; //This name is specified in "Event Class" properties as "Payload Type"
#endif
uartthread_entry.c
#include <conversions_api.h>
#include "uartthread.h"
#include <stdio.h>

/* UART Thread entry function */
void uartthread_entry(void)
{
    uint8_t cstr[18*NUMCHANNELS];// = "Channel 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 (uint8_t index=0; index<NUMCHANNELS; index++)
                {
                    sprintf(cstr+index*18, "Channel %1c: %5d\n", thepayload->conversionchannel[index]+'0', thepayload->conversiondata[index]); //formatted output string
                }
				g_uart0.p_api->write(g_uart0.p_ctrl, cstr, 18*NUMCHANNELS); //send the information over UART
            }
        }
        tx_thread_sleep (1);
    }
}

Next will be the ultrasonic thread. The message sending code will be nearly the same as in the last example. The key difference here is setting up and using the GPIO’s.

ultrasonic_thread_entry.c
#include "ultrasonic_thread.h"
#include "r_gpt.h"
/* Ultrasonic Thread entry function */
void ultrasonic_thread_entry(void)
{
    g_timer0.p_api->open(g_timer0.p_ctrl, g_timer0.p_cfg);
    g_timer1.p_api->open(g_timer1.p_ctrl, g_timer1.p_cfg);
    //currently there is a bug/missing feature in the Renesas Synergy Software Platform
    //which requires this register to be set manually. See S124 User's Manual Section 19
    //for information about the General Purpose Timer registers
    R_GPTB0_Type * p_gpt;
    p_gpt = ((gpt_instance_ctrl_t *)g_timer0.p_ctrl)->p_reg;
    p_gpt->GTIOR_b.GTIOB = 0x14; //GTIOB Initial High, low output at GTCCRB compare match
    timer_size_t timervalue = 0;// typedef'd uint32 to store timer ticks in
    timer_info_t timerinfo; //struct with information about the timer
    double hightime;
    uint16_t distancemm;
    ioport_level_t pinlevel = IOPORT_LEVEL_LOW;
    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
    ssp_err_t error; //place for error codes from buffer acquisition to go
    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[0] = 0; //only one channel
    while (1)
    {
        //turn on pin, wait for 10us, turn off pin
        //this will trigger the sensor
        g_timer0.p_api->start(g_timer0.p_ctrl);
        //impatiently wait for input pin to be high
        while (pinlevel == IOPORT_LEVEL_LOW) g_ioport.p_api->pinRead(IOPORT_PORT_04_PIN_00, &pinlevel);
        //start timer
        //wait for input pin to go low and measure time passed while high
        //timer will stop if time exceeds 60ms (set in project configuration)
        g_timer1.p_api->start(g_timer1.p_ctrl);
        g_timer1.p_api->infoGet(g_timer1.p_ctrl, &timerinfo);
        while (pinlevel == IOPORT_LEVEL_HIGH)
        {
            g_ioport.p_api->pinRead(IOPORT_PORT_04_PIN_00, &pinlevel);
        }
        //stop the timer if it has not already stopped and take the time
        g_timer1.p_api->infoGet(g_timer1.p_ctrl, &timerinfo);
        if (timerinfo.status != TIMER_STATUS_STOPPED)
        {
            g_timer1.p_api->stop(g_timer1.p_ctrl); //stop the counter
            g_timer1.p_api->counterGet(g_timer1.p_ctrl, &timervalue);
            //compute: approx. distance  = High time * speed of sound / 2
            //high time = ticks/frequency
            hightime = ((double)(timervalue))/((double)(timerinfo.clock_frequency));
        }
        else //timer was stopped and the counter has reset
        {
            hightime = .06;
        }
        //approx. speed of sound in air at sea level = 340,000 mm/s
        distancemm = (uint16_t)(hightime * 340000/2);
        g_timer1.p_api->reset(g_timer1.p_ctrl); //reset the counter for next time
        //send as message
        mypayload->conversiondata[0] = distancemm;
        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 (4); //throttle a little bit so the UART thread can keep up.
    }
}

 

The Output/Running the Program

You may configure the UART hardware identical to last time (see Configuring and Using the UART Interface on the DK-S124) and use PuTTY to view the output from the program. To interface with the HC-SR04, attach a GND pin from the DK-S124 to the GND pin of the sensor, a 5V pin to the 5V input on the sensor, pin P400 to the ECHO pin and P401 to the trigger pin. See the image below if any clarification is needed.

You can also see that for this example, a modest test rig has been constructed. You can see that the target object is close to 100mm away from the sensor. The output from the program is below.

The sensor is disagreeing with reality by about half an inch. Not bad for a small target this close and a $4 sensor. The actual signals to and from the sensor can be seen below.

Here, the yellow line (1) is the 10uS input pulse to the sensor's TRIG pin from P401. Note that it is very close to 10uS in width, this can be seen as the +Width line at the bottom. The blue line (2) is the ECHO response from the sensor to pin P400. In this shot, the box has remained stationary for several minutes, but you may see that there is some variance in the signal's duration. This is most likely due to the sensor only being $4. However, it is accurate to an inch or two over the range of about 0 to 2 meters (about 6 feet). This is good enough for many applications.

 

This concludes this guide. In it, the GPIO pins were used in input mode, the Timer Driver was used both to output a precise pulse and measure the duration of an event, and the UART code from the last example was reused to output information. For more information about using the GPIO pins and any other peripherals, please refer to the Synergy Software Package User's Manual which is linked above. Section 7.18 contains the API reference for the I/O Port Interface.

Related articles