540 views
in System Integration by

Hi Embedded Wizard team,

 

for an upcoming project I would like to use a FreeRTOS based system on a iMXRT1060 with one thread Embedded Wizard and another thread.

The Build Environment which I was able to download is already prepared to be used with FreeRTOS, that is great, but your documentation explicitly says that Embedded Wizard isn’t thread safe.

I would like to know what will be an good approach to communicate between the Embedded Wizard FreeRTOS thread and another thread which, for example, collects data from an GPIO source.

Thank you for your help,

 

Fridolin

1 Answer

0 votes
by

Hi Fridolin,

well that is a pretty good question, this probably will be interesting for lot Embedded Wizard user!

As you already have figured out, we have integrated FreeRTOS in our Build Environment per default. This means, a preconfigured FreeRTOS thread, which contains the Embedded Wizard UI, is already present in your main.c.

If not already done, please download a NXP i.MX RT1060 Build Environment. To answer your question, let me use the Device Integration example within the ‘Example/DeviceIntegration’ folder as base for some modifications.

In this example, you can see in ‘ew_bsp_inout.c/.h’ that there is an user button configured which initiates an interrupt every time when the button is pressed. That’s a good approach to react immediately to GPIO inputs. But let’s assume we want to use a second FreeRTOS thread, which permanently polls the GPIO input level without using an interrupt.

As first I would like to mention the FreeRTOS documentary about inter-task communication. As you can see, this OS has several features to realize a thread safe communication between tasks. In my example I will use a queue for exchanging data. Other common features for a solution like that would be FreeRTOS semaphores or mutexes. But in our really simple example, the queue based approach is enough. This method is simple and universal.

As next, we have to configure a second thread within the ‘Application/Source/main.c’. Let’s call this thread ‘ButtonThread’. This thread should initialize  and poll the GPIO input every 10 milliseconds. Every time after that is done, the new value should be send to a queue. The queue basically ‘stores’ a certain amount of values and those can be received (read) by any other thread. In our case from the Embedded Wizard thread or more explicitly the device integration.

In following code snippets you can see what’s necessary to add to your main.c for the second task.

Declaration:

static void ButtonThread( void* arg );
int ButtonCounterSend;
QueueHandle_t ButtonCounterQueue = NULL;

Button thread:

static void ButtonThread( void* arg )
{
  /* Define the init structure for the input switch pin */
  gpio_pin_config_t sw_config = { kGPIO_DigitalInput, 0, kGPIO_IntRisingOrFallingEdge };


  /* Init input switch GPIO. */
  GPIO_PinInit( BOARD_USER_BUTTON_GPIO, BOARD_USER_BUTTON_GPIO_PIN, &sw_config );

  ButtonCounterQueue = xQueueCreate(10, 4);
  if (ButtonCounterQueue != NULL)
  {
    vQueueAddToRegistry(ButtonCounterQueue, "Queue");
  }

  while(1)
  {
    vTaskDelay(10);

    if ( !GPIO_PinRead( BOARD_USER_BUTTON_GPIO, BOARD_USER_BUTTON_GPIO_PIN ) )
    {
      ButtonCounterSend++;
    }
    else
    {
      ButtonCounterSend = 0;
    }
    xQueueSend(ButtonCounterQueue, &ButtonCounterSend, 0);
  }
}

After the embedded wizard task is added, we have to add our new button thread:

EwPrint( "Create button thread...                          " );
xTaskCreate( ButtonThread, "Button_Task", 200, NULL, 0, NULL );
EwPrint( "[OK]\n" );

 

Hereby please take into account that the thread stack size is an extremely important parameter for the second thread. In most cases when your thread will not start, the stack size isn’t great enough. To configure this please also use the FreeRTOS reference here. Just to avoid doubts in modifying the main.c, you can download the new version of this file here.

 

Further on we have to configure our ‘Application/Source/DeviceIntegration.c’ in that way, that the button won’t be initialized and no interrupt call back function will be called. Therefore, please remove all button related code parts. To avoid a mess in the answer here, you can the new DeviceIntegration.c file download here as well.

As you can see in this file at line 227, there is a locally declared variable ‘ButtonCounterReceive’ which is used to receive the queue values one after other.

int ButtonCounterReceive = 0;
if(uxQueueMessagesWaiting(ButtonCounterQueue) != 0)
{
  if (xQueueReceive(ButtonCounterQueue, &ButtonCounterReceive, portMAX_DELAY) != pdTRUE)
  {
    EwPrint("Failed to receive queue.\n");
  }
}

Every time when the received button counter value is greater than 0, we would like to update the hard button counter which represent the progress bar in the user interface.

if(ButtonCounterReceive > 0)
{
  ApplicationDeviceClass__UpdateHardButtonCounter( DeviceObject, (XInt32)ButtonCounterReceive );
  needUpdate = 1;
}

 

Now Embedded Wizard gets the button counter data from another task in a thread safe approach. Clearly in our example the queue only stores one 32 bit integer value, which honestly doesn’t need to be queued cause it can be written in one cycle. But a queue also can be used for bigger data packages like an integer array or character array.

 

I hope this helps to understand how the inter-task communication with embedded wizard can look like. Please take into account that this answer was made based on an Embedded Wizard 11.0 Build Environment.

 

Best regards,

Tim

Ask Embedded Wizard

Welcome to the question and answer site for Embedded Wizard users and UI developers.

Ask your question and receive answers from the Embedded Wizard support team or from other members of the community!

Embedded Wizard Website | Privacy Policy | Imprint

...