Last updated February 23, 2011 The IdeaToday the multitasking approach for embedded development is robust and effective. The demo projects published in this site are the code base of many projects I followed with success ( and a little lucky! :-). So, what is the next step for the embedded world? Looking at the today MCU and imagining the roadmaps for the next years, I think that Object Oriented (OO) approach is the answer. This demo shows how to use a set of C++ class to build a multitasking application on top of FreeRTOS. This work was possible thanks to the support of Francesco. My personal thanks for his contribution. Main Components
Please refer to the instructions provided in the Eclipse demo (STM32) to install and configure Eclipse, and note that the project is ready to be used with Eclipse Helios. IntroductionThis demo is the classical Hello World demo for embedded application. It uses three tasks in order to blink the four user led of the evaluation board. I also have added some of the standard FreeRTOS demo tasks. The challenge was to write the source code using the C++ language and to design all tasks as a C++ class. For this purpose I developed a set of class, the FreeRTOS Extension Class, that wrapper the FreeRTOS handle and provide an object oriented interface for the standard FreeRTOS API. The class designLet me start by showing the whole picture. Fig. 1 - Architecture (UML) Just on top of the FreeRTOS API layer there is the FreeRTOS Extension Class layer. It consists of a set of classes logically split into two groups. The lower one is the FreeRTOS Wrapper. The upper group is the Managed Class Extension. This layer aims to provide a Object Oriented framework to develop a FreeRTOS application. The FreeRTOOS wrapper classThe main feature of these objects are:
All class in this sublayer, but CFreeRTOS , implement the FreeRTOS protocol declared in the interface IFreeRTOSObj . The FreeRTOS protocol declare the following pure virtual method:bool IsValid () const void Attach (xGenericHandle handle) xGenericHandle Detach () When an object is allocated it is not attached to a native FreeRTOS handle. This means that FreeRTOS resources are not allocated and the IsValid method returns false . To alloc the FreeRTOS resource the application calls the Create method of the object. This method attach the handle of the FreeRTOS resource to the object. From this moment the object owns the handle (and hence the low level resource) and the IsValid method return true . For example the following lines of code create a mutex object:CMutex g_aMutex // global mutex declaration. void taskControlFunc(void *pParams) // a task control loop { g_aMutex.Create(); for (;;) When the object is disposed its owned handle is deleted and the FreeRTOS resources are freed. It is possible to keep the low level FreeRTOS handle alive by detaching it before the owner object is disposed. All class in this layer declare just one member that is the owned handle. In this way the RAM footprint is limited to the four byte of the handle plus the virtual table. Moreover all methods wrapping the native API are inline. For more information on FreeRTOS Wrapper Class see the html API documentation available for download in the Download area. The Managed class ExtensionThis layer aims to provide a more structured and easy to use programming approach to the application. At the moment it has only one abstract class, the Fig. 2 - AManagedClass collaboration diagram The class inherit from the virtual bool HardwareInit() { return true; } To create a task using the managed extension, the application makes a subclasses of the AManagedTask class and provides the task control loop by implementing the Run method in the derived class.class CMyTask: public AManagedTask { // Task private variables. public: void Run() { /* task control loop*/ }; } Then instantiate the task object and call its Create method. That is all! The application could be designed as a set o self contained global task objects./** * Heap allocated task object */ CHelloWorld *g_pLed1Task; /** * Global task objects. */ CHelloWorld g_Led2Task(GPIOE, GPIO_Pin_14, 1000); CCheckTask g_checkTask(4000/portTICK_RATE_MS); /** * Main program. */ int main() { prvSetupHardware(); // While usually dynamic memory is not a good practice in an embedded program, // this task is allocated on the heap only to test if the low level layer works fine. // (linker script and runtime support) g_pLed1Task = new CHelloWorld(GPIOD, GPIO_Pin_13, 2000); g_pLed1Task->Create("Led1", configMINIMAL_STACK_SIZE, mainFLASH_TASK_PRIORITY); g_Led2Task.Create("Led2", configMINIMAL_STACK_SIZE, mainFLASH_TASK_PRIORITY); // Static task object static CHelloWorld led3Task(GPIOD, GPIO_Pin_3 | GPIO_Pin_4, 3000); led3Task.Create("Led3", configMINIMAL_STACK_SIZE, mainFLASH_TASK_PRIORITY); CFreeRTOS::InitHardwareForManagedTasks(); CFreeRTOS::StartScheduler(); while(1); return 0; } If the task needs to perform some hardware initialization, like to configure the GPIO pins, it overrides the HardwareInit method. The framework calls this method for each managed task before the scheduler starts. The flow control of the application starting phase is like the one showed in Fig. 3.For an example look at the CHelloWorld class.Do you remember the purpose of this demo? It is to create a task in order to blink one or more led at a given rate. A class need some parameters to implement this task, so it could be declared in the following way:
}; The object must know what pins it manage, and on the STM32 this is specified by a GPIO port ( To perform the wanted behavior a CHelloWorld object must be also a task so that it has its own execution flow. This is easy to do using the C++ inheritance feature. Let the CHelloWorld class to derive from the AManagedTask class.
The last step is to implement the task control loop. Since the class derives from the AManagedTask class this must be done by overriding the Run method. So I add the declaration of the Run method in the CHelloWorld class declaration and following is the method definition:void CHelloWorld::Run() { portTickType xLastFlashTime; xLastFlashTime = GetTickCount(); for(;;) { /* Delay for half the flash period then turn the LED on. */ DelayUntil(&xLastFlashTime, m_nFlashRate); GPIO_WriteBit(m_pPort, m_pin, m_bState ? Bit_SET : Bit_RESET); m_bState = !m_bState; /* Delay for half the flash period then turn the LED off. */ DelayUntil(&xLastFlashTime, m_nFlashRate); GPIO_WriteBit(m_pPort, m_pin, m_bState ? Bit_SET : Bit_RESET); m_bState = !m_bState; } } Now the class is linked to the framework - FreeRTOS Extension Class - and ready to be used. C++ LimitationsNot all C++ features were used for this demo. Even if the embedded world is growing quickly, at the moment the MCU are not powerful enough, so I choose to not use these language features:
About FreeRTOS configuration parameters"A number of configurable parameters exist that allow the FreeRTOS kernel to be tailored to your particular application. These items are located in a file called FreeRTOSConfig.h" as explained in this web page. For example it is possible to reduce the scheduler ROM footprint by adding the following define in the configuration file:#define INCLUDE_xTaskGetSchedulerState 0 In this way the preprocessor excludes the function xTaskGetSchedulerState , and if the application uses that function, a compiler error is raised. How the FreeRTOS Extension Class handle the FreeRTOS configuration options? Let me show the implementation of the CTask::GetSchedulerState metod.inline portBASE_TYPE CTask::GetSchedulerState() { #if ( INCLUDE_xTaskGetSchedulerState == 1 ) return xTaskGetSchedulerState(); #else return 0; #endif } When the INCLUDE_xTaskGetSchedulerState is defined to zero the method is declared as an empty method, so the compiler does not raise the error if the application call that method. This is the default behavior for the class library.A note on the FreeRTOS_ROOT environment variable The FreeRTOS_ROOT variable must refer to the root of FreeRTOS installation. Eclipse Helios has improved the environment variable management. Follow this instruction to modify the variable:
The Managed Build System (MBS) Plug-in The workspace contains two projects (Fig. 4). The second project, CppDemo_mbs, is closed. To open the project, select it and choose the Open Projectcommand from the Project menu. Fig. 4 - Workspace Projects This project has not a Makefile, but you can build it with the Build command. This is because this project uses the managed build system of the Eclipse framework. This means that the Makefile is automatically generated and managed by the IDE for all project files. This is a great features if you don't like to manually edit a Makefile! I configured this workspace so that the source files are the same for both projects, so it is possible to use the preferred project type, the Standard Makefile project (CppDemo) or the Managed project (CppDemo_mbs). To use the last one you have to install the GNU ARM Eclipse Plug-in, a great open source project! :-) Simply follow the The recommended way in the plug-in installation instruction web page. Remember to modify the OpenOCD configuration file stm32_program_eclipse_jtag.cfg, located in the workspace folder, in order to tell to OpenOCD the location of the binary image to flash. #flash write_image ./CppDemo/bin/firmware.elf 0x08000000 elf flash write_image ./CppDemo_mbs/Debug/CppDemo_mbs.elf 0x08000000 elf Uncomment one of the two script command showed above according to the active project. How to modify the linker script for a different STM32 MCU?Francesco gives me a very interesting tutorial about the linker script and how to changing it according to the memory capability of the MCU I use. I published it in this web page. Once again, thanks a lot Francesco! The latest version of the demo file is located in the Download page of the site. To build the demo are needed the following components:
Latest newsQuestions, Suggestions, Bugs, and...
Every feedback is welcome. If you have a suggestion to improve a demo or
the web pages, it's ok. If you found a bug in a demo, please let me
know. If you have an idea to improve a demo, I'm pleasure to discuss
about it. If you have any questions about a demo, I hope to have the
answer! |