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,