1.8k views
in System Integration by

Dear all,

I am building an application on a STM32F469 Discovery board which requires some computations to run indefinitely while a GUI is displaying info on the screen (based on these computations). For that, I would like to use a RTOS and have a worker thread doing all the computations (and interacting with other devices) and a GUI thread.

I based my work around the EW Build environment for my board, and started with the DeviceIntegration example. I managed to interact with my other devices thanks to the DeviceDriver file, but I cannot seem to understand how to properly run multiple threads via FreeRTOS.
I have set FREE_RTOS = 1 in the Makefile, and edited the provided source files in this way :

main.c :

#include "ewmain.h"
#include "ewrte.h"
#include "ew_bsp_system.h"
#include "ew_bsp_console.h"
#include "DeviceDriver.h"
#include "cmsis_os.h"

#define semtstSTACK_SIZE    configMINIMAL_STACK_SIZE * 10

static void GuiThread( const void* arg );
static void FlowThread(const void* arg);


int main( void )
{
  /* initialize system */
  EwBspSystemInit();

  /* initialize console interface for debug messages */
  EwBspConsoleInit();

  /* create worker thread */
  EwPrint( "Create custom thread...                      " );
  osThreadDef( FlowThreadHandle, FlowThread, osPriorityLow, 1, semtstSTACK_SIZE );
  osThreadCreate( osThread( FlowThreadHandle ), (void*)0 );
  EwPrint( "[OK]\n" );

  /* create thread that drives the Embedded Wizard GUI application... */
  EwPrint( "Create UI thread...                          " );
  osThreadDef( EwThreadHandle, GuiThread, osPriorityIdle, 1, semtstSTACK_SIZE );
  osThreadCreate( osThread( EwThreadHandle ), (void*)0 );
  EwPrint( "[OK]\n" );
 

  /* ...and start scheduler */
  osKernelStart();

  /* restore console */
  EwBspConsoleDone();

  /* terminate the system */
  EwBspSystemDone();

  return 0;
}


static void GuiThread( const void* arg )
{
  /* initialize Embedded Wizard application */
  if ( EwInit() == 0 )
    return;

  EwPrintSystemInfo();

  /* process the Embedded Wizard main loop */
  while( EwProcess())
    ;

  /* de-initialize Embedded Wizard application */
  EwDone();
}


static void FlowThread(const void* arg){
  while(1)
    MeasureFlow();
}

DeviceDriver.c :
 

#include "ewrte.h"
#include "ew_bsp_inout.h"
#include "ew_bsp_clock.h"
#include "stm32469i_discovery.h"
#include "stm32f4xx_hal_gpio.h"
#include "stm32f4xx_hal_rcc.h"
#include "stm32f469xx.h"
#include "cmsis_os.h"
#include "DeviceDriver.h"

volatile int flow = 0;

/*
   Include the generated header file to access the device class, for example to
   access the class 'DeviceClass' from the unit 'Application' include the
   generated file 'Application.h'.
*/
#include "Application.h"

#ifdef _ApplicationDeviceClass_

/*
   Create a static variable to keep the global instance (autoobject) of the
   device class. The type of the variable has to correspond to the device class
   and the unit name, for example the static variable of the class 'DeviceClass'
   from the unit 'Application' has the type 'ApplicationDeviceClass'.
*/
  static ApplicationDeviceClass DeviceObject = 0;

#endif

void DeviceDriver_Initialize( void )
{
  /* Irrelevant bits of code */


#ifdef _ApplicationDeviceClass_

  /*
     Get access to the counterpart of this device driver: get access to the
     device class that is created as autoobject within your Embedded Wizard
     project. For this purpose you can call the function EwGetAutoObject().
     This function contains two paramters: The pointer to the autoobject and
     the class of the autoobject.
     Assuming you have implemented the class 'DeviceClass' within the unit
     'Application' and you have an autoobject with the name 'Device', make
     the following call:
     EwGetAutoObject( &ApplicationDevice, ApplicationDeviceClass );
  */

  DeviceObject = EwGetAutoObject( &ApplicationDevice, ApplicationDeviceClass );

  /*
     Once you have the access to the autoobject, lock it as long as you need
     the access (typically, until your device driver is closed). By locking
     the autoobject, you can ensure that the object of the device class will
     be kept within the memory and not freed by the Garbage Collector.
     Once the device class is locked, you can easily store it within a static
     variable to access the driver class during the runtime.
  */

  EwLockObject( DeviceObject );

#endif

}


void DeviceDriver_Deinitialize( void )
{
  /*
     You can implement here the necessary code to deinitialize your particular
     hardware, to close used devices, to close communication channels, etc.
  */

#ifdef _ApplicationDeviceClass_

  /*
     If you have access to the device class autoobject, don't forget to unlock
     it. Clear the static variable to avoid later usage.
  */

  if ( DeviceObject )
    EwUnlockObject( DeviceObject );

  DeviceObject = 0;

#endif

}

int DeviceDriver_ProcessData( void )
{
  int needUpdate = 0;
  /*
     Get the data you want to provide to the GUI application.
     In case your are working with an operating system and your device is
     controlled from a separate task/thread/process, take all information
     from your device driver out of the message queue.
     Please note, that this function is called within the context of the main
     GUI thread.
     If you control your system by direct register access or some BSP functions,
     get all necessary data you want to provide to the GUI application.
  */

#ifdef _ApplicationDeviceClass_

  /* check for a valid access to the autoobject of the device class */
  if ( DeviceObject == 0 )
    return 0;


  ApplicationDeviceClass__UpdateInstFlow( DeviceObject, (XInt32) flow );
  needUpdate = 1;

#endif

  return needUpdate;
}

void MeasureFlow(){
   BSP_LED_Init(LED3);
   BSP_LED_On(LED3);
   flow = 180;
}

/* Irrelevant code */

This is a pretty crude test code, that is only meant to validate whether the RTOS is working, which is unfortunately not the case. Its goal is simply to start the GUI and worker threads, with the latter doing nothing but turning on an LED on the board, and changing the value of a global variable, which is ultimately reflected in the UI thanks to DeviceDriver_ProcessData().

My issue is that only the first thread declared in the main function seems to start : in this configuration, the screen stays black and the LED turns on ; but, inverting the OSThreadDef and OSThreadCreate statements of both threads yields the opposite result : the UI is displayed fine and working properly, but the LED never turns on and the on-screen value is not updated. I used various EwPrint() to verify that it was indeed the case via the serial interface, and the result is as observed, only a single thread starts.

I have done some research, but I am unable to make it work, would anyone be able to guide me? Any help would be greatly appreciated !
I apologize if this is a basic or out-of-topic question, I am a newbie in embedded development and never had an hands-on experience with a RTOS before. The EW Studio software and the provided build environments have already helped me a lot, and I am thankful for that !

Best regards,

1 Answer

+1 vote
by
 
Best answer

Hi Loris,

thank you for detailed question.

As first I would like to mention those two threads:

Incorporating native code

Task Communication between Embedded Wizard and other thread

In both of them an second task were added to our FreeRTOS based build environment. Could you please try out the code (second task) from one of that example?

Does the second task start then?

Please also take into account that the task stack size and task priority are extremely important parameters for the second task. In most cases when your task will not start, the stack size is not great enough or the priority is too low.

Btw: In your second task you’re initializing the led permanently. Probably only one time before the while should be enough.

Best regards,

Tim

by

Hi Tim,

Thank you very much for your answer !
I don't know exactly what fixed my issue, but I changed two things :

  • The first was to use the xTaskCreate() function directly instead of going through osThreadCreate() defined in the CMSIS drivers.
  • The second was to reduce the stack sizes of each thread, I didn't realize that I had increased semtstSTACK_SIZE by a factor 10, which apparently led to memory issues.
Thanks a lot for your help,
Best regards,
 
Loris

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

...