Subscribe to receive new articles by mail:

Integrating FreeRTOS in STM32 on the CooCox CoIDE. Step number 96

Hello everyone. Considering in the last article examples of the practicality of using the C language, where one of them is writing operating systems, in this article we will take a closer look at one of them — FreeRTOS . We briefly describe it, consider the architecture, install the stm32discovery debugging board based on the stm32f303vc microcontroller on the core Cortex M4 . Repeat the experience of article №89 — transferring data from stm in android, but already with the built-in OC.

Why use it? It happens that the project grows and the moment comes when the finished device must perform a large variety of functions, plus be able to add new tasks in the future. Here comes the RTOS (real-time operating system, RTOS ), which has a control unit memory ( MMU ) and mechanisms for implementing multitasking, relatively small (4-9 kilobytes depending on the platform and kernel settings) and a simple dispatcher ( scheduler ), which can set different process priorities, forcing out and not forcing out multitasking, semaphores and queues.

To start working at you can download the latest version, today it is FreeRTOS v10.2.0 — to have MIT licensed, and including RISC-V and ARMv8-M (Cortex-M33) demos from February 25, 2019. In general additions and release changes read in the same place. Ported to more than 35 microprocessor systems. Of these, we are interested in ARM Cortex-M4 for our debug card and Atmel AVR (in the future we will also install stones on this one).


Downloaded. Our next step is unpacking the library. As you remember, we work with CooCox , although it does not make sense — here the main thing to throw the necessary files, and with which editor and compiler to work already your choice. Dragging the libraries into our CoIDE  — we have already learned how connect the CMSIS libraries . To work with the OS, we need to drag and drop files into the project from the following directories (below). Also below is a screen of used files. For convenience, I created a separate folder RTOS .
FreeRTOSv ... / FreeRTOS / Source  — we drop all files with the .c extension into the source folder, where:
queue.c  — functions of queues and mutexes;
tasks.c  — functions for working with tasks;
timers.c  — functions for working with timers;
croutine.c  — functions with coroutines;
event_groups.c  — functions for working with flags;
list.c  — everything is there for debugging;
stream_buffer.c  — to transfer a stream of bytes from the interrupt service routine to a task or from one task to another.

FreeRTOSv ... / FreeRTOS / Source / include  — copy all files to include.

The following directory depends on the compiler and kernel used — I have GCC . There are two folders for one core. The fact is, that not all Cortex-M4 have a floating point module. I have The board has support for floating point DSP instructions. If you do not have, then you must either manually inform GCC that the hardware floating point unit is used using a separate command line option, or the latest directory option. Here I paid attention to this moment, because errors during the compilation process are possible. FreeRTOSv ... / FreeRTOS / Source / portable / GCC / ARM_CM4_MPU or / and ARM_CM4F  — here are two files where:
port.c — платформозависимые параметры, для каждого МК – свои.
portmacro.h — настройки платформы. Тоже индивидуальный для каждого типа МК

FreeRTOSv ... / FreeRTOS / Source / portable / MemMang  — Here we select the heap file (memory management file) and also copy to ourselves, where:
heap_1 is the simplest, does not allow to free memory.
heap_2  — allows the release of memory, but does not combine adjacent free blocks. Dynamic fragmented memory. Those. the memory is dynamically allocated, freed, but at the same time the memory sections are obtained fragmented, with holes in the place of the released memory.
heap_3  — just transfers standard malloc () and free () for thread safety.
heap_4  — combines adjacent free blocks to avoid fragmentation. Includes absolute addressing capability.
heap_5  — according to heap_4 , with the ability to distribute the heap across several non-adjacent memory areas.

What memory to choose depends on you. I chose the 2nd. More details on the official docks — or http : // . I think for necessity you can always change the memory management and recompile the project.

And the last one is FreeRTOSConfig.h  — these are OS settings. This file can be taken either in the examples directory if our stone is present there. If not then smoke the official docks. FreeRTOSv ... / FreeRTOS / Demo ... or . Unfortunately, I didn’t have mine, and I really didn’t want to sit over them. Found on github for stm32 : FreeRTOSConfig.h . Also as an option, you can generate a file in a cube and tear it from there. Open the STM32Cube , select the board, turn on the checkmark in front of FreeRTOS and build the project as usual. We have already met with the “cube” in article 78, but so far we have abandoned the libraries on HAL ...

At the moment I will not describe the file settings. Let's leave it for later, when our project grows a little and the file is corrected.


Before compiling, it is necessary to define 4 functions in main.c, without which the project will not build. Let it even be stub functions. In order to eliminate the following errors:
[cc]C:\CocPrj\SmartHouse\RTOS\portable\MemMang/heap_2.c:202: undefined reference to `vApplicationMallocFailedHook'
[cc] collect2.exe: error: ld returned 1 exit status

void vApplicationIdleHook ( void ){
void vApplicationMallocFailedHook ( void ){
for ( ;; );
void vApplicationStackOverflowHook ( xTaskHandle pxTask, signed char *pcTaskName ){
( void ) pcTaskName;
( void ) pxTask;
for ( ;; );
void vApplicationTickHook ( void ){

And also to connect libraries:
#include «FreeRTOSConfig.h»
#include «projdefs.h»
#include «portmacro.h»
#include «FreeRTOSConfig.h»
#include «FreeRTOS.h»
#include «croutine.h»
#include «task.h»
#include «queue.h»
And " portmacro.h " should be below " FreeRTOSConfig.h " and " projdefs.h "because the compiler produces an error:
[cc] C:\CocPrj\SmartHouse\RTOS\portable\GCC\ARM_CM4F/portmacro.h:219:58: error: 'configMAX_SYSCALL_INTERRUPT_PRIORITY' undeclared (first use in this function).

So compile the project with these files. I immediately have the first bug: C:\CocPrj\SmartHouse\RTOS\portable\GCC\ARM_CM4_MPU/portmacro.h:54:23: error: missing binary operator before token «long»
In the FreeRTOSConfig.h file, # define configMAX_PRIORITIES ((unsigned portBASE_TYPE) 5) , you must remove the type conversion. Just override as 5.

On the next compilation attempt, the following problem appeared:
C:\Users\B4D2~1\AppData\Local\Temp\ccVy8f9Z.s: Assembler messages:
[cc] C:\Users\B4D2~1\AppData\Local\Temp\ccVy8f9Z.s:908: Error: selected processor does not support `vstmdbeq r0!,{s16-s31}' in Thumb mode
[cc] C:\Users\B4D2~1\AppData\Local\Temp\ccVy8f9Z.s:910: Error: instruction not allowed in IT block — `mrs r1,control'
It may be that GCC does not expect the presence of floating point instructions. This problem is overcome by including in the compiler options hard FPU (or soft FPU).


компиляция-ртос_4 Everyone compiled the project. Now we have a built-in OS. Before you repeat the data transfer experience. Let's take a brief look at architecture and how to work with it. Consider «today» only the basics, what we use in the project. As the program grows, we will try to consider all the capabilities of the system.
This OS is controlled by dispatcher , which is caused by interruption of the timer — quantum. That is, it turns out that we allocate one timer from our resources for the OS, in ARM Cortex-M4 it is hung on SysTick timer . The timer generates system tics at which the dispatcher starts. What the dispatcher does is to call functions, each of which has its own priority, which in turn is set during creation and can be manually changed on the fly via API RTOS function . The first thing you need is to implement the task as a function.
Construction task
void MyTask1 ( void *pvParameters ){...Тело...}
We create as many tasks as we need.
where pvParameters — parameter that has a pointer type of void (i.e., void *). The value specified in pvParameters will be transferred to the task. We do not use it yet . What am I doing next. I move the cyclic code that I had in main () to the function where the data is sent to the module and the LED flashes on the board. I still need only one task.

void vLedBlinkAndSend (void *pvParameters){
  while (1){
    UART4_Send_String ("AT+CIPSEND=0,5\r\n");
    delay ();
    delay ();
  vTaskDelete( NULL ); //Handle to the task to be deleted. Passing NULL will remove the calling task.

Running on before it, we will have it spinning constantly. To remove it in case of uselessness, you can use the API function vTaskDelete (NULL) , which completes the current task from which it was called, for example by some condition. At the same time, it will be unloaded from memory, it will release the RAM, but its current state and local variables will be lost. However, nothing prevents to run it again, first. In this code, it will never be executed by me.

Now we need to create a task. Let's get acquainted with another API task management function — xTaskCreate  — creates a new task, allocating memory for it and sends it to the dispatcher, where the name of the task, its priority, the name for debugging and the amount of memory allocated for it. As a result, a piece of memory is allocated for it, it starts its own stack and it starts into free life. Let's talk about the task states when the corresponding example is turned up. Function prototype
portBASE_TYPE xTaskCreate (
pdTASK_CODE pvTaskCode, /*Function name.*/
const signed portCHAR * const pcName, /*Descriptive name for the task. Used only for debugging.*/
unsigned portSHORT usStackDepth, /*The number of words that can be saved on the stack, not the number of bytes. For example, if the stack has a width of 32 bits, and the transferred value of usStackDepth is 100, then 400 bytes (100 * 4 bytes) will be allocated for the stack.*/
void *pvParameters, /*The pvParameters value will be passed to the task*/
unsigned portBASE_TYPE uxPriority, /*Sets the priority from 0 (tskIDLE_PRIORITY) the minimum priority to (configMAX_PRIORITIES — 1) the maximum priority.*/
xTaskHandle *pxCreatedTask ); /*Used to pass the created task to the outside.*/
A function can return one of two values:
1. pdTRUE shows that the task was successfully created.
2. errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY indicates that the task has not been created, since there is not enough free memory in the heap for FreeRTOS, so that it can allocate space for the data structures of the task and the stack. 

Below is our bit of code:

int main (void) {
   UART_Init(); /*We initialize the interface.*/
   Init_WIFI(); /*Wi-Fi.*/
   xTaskCreate(vLedBlinkAndSend,"vLedBlinkAndSend", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
    /*где NULL,- do not use task parameter.
             1,- the task will run with priority 1.
         NULL);- do not use handle.*/
    /*Starting the scheduler, after which the task (s) will be launched for execution.*/
}//The end main().

Function main () initial interface, module and creates tasks before running scheduler. Next will take care of everything dispatcher . It checks the return value from the xTaskCreate () call to make sure that the task was successfully created. If everything is good, then control in main () will never reach the end, and the dispatcher will twist tasks and make sure that the task is kept and remembered: the current position, stack, variables, registers and nothing happened from the point of view of the task. If main () still reaches the end, this may mean that there is not enough memory heaps (heap) for creating a special idle task (idle task). In fig. below the result of the experiment.
In send-stm-android-rtos_5 of this entry, we described the process and order of installation of the FreeRTOS system on the microcontroller, possible errors when compiling the project, eliminating them, considered the basis of the API functions for starting a simple task in the system. Repeated the experience of data transfer from MK to android. The function is spinning, data is being transferred. The next step is to try to implement various tasks in the microcontroller on the OS in the form of a menu, in parallel with its nuances, and try to synchronize the whole thing with the interface on Android. On this note today and finish. Bye everyone
Used books:

Seen times.

Я на Google+

Добавить комментарий

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe without commenting