FreeRTOS is a simple way of adding an operating system to your microprocessor projects. It allows you to write more complex programs without worrying about the underlying details. It is quite user friendly and easy to use. However, as with any complex software, there are some details to get just right before it will work as desired. Here I present an example of how two periodic tasks can be created in  FreeRTOS. It is intended as a starting point where the basic setup has  been completed and working. It is easy to further modify to your own needs. That said, using a RTOS and a multitasking method to accomplish your goals is far from simple, and requires careful design.
Ingredients:
- GCC ARM EMBEDDED is used, running on a STM32F103RB processor.
- The tests were done on a STM32 Nucleo FRB103 board.
- FreeRTOS version is V8.2.1, which is recent at the time of writing.
- It can be built not only using commandline tools, but also the same source codes in a GUI environment such as Eclipse, Keil, Attollic etc. Please remember to modify the Makefile to suit your own directory structure.
The code can be found in my Github pages.
There are some pitfalls in the initial setup of the processor which you must take care of:
1. FreeRTOSConfig.h. It is necessary to set pointers to interrupt handlers to those provided by the FreeRTOS. This is done in the lines:
//Needed for the STM32F10x processors:
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
The remaining series of lines in FreeRTOSConfig.h are to set the properties of the processor etc. They are fairly easy to understand.
2. The main body of the program; freertos_testXX.c. This sets up  the processor to run the tasks. It is fairly easy to understand and get  running,  using the FreeRTOS supplied functions. It is necessary to add  the line to initialize the nested vectored interrupt controller on the  STM32F103:
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );  //Needed for STM32F10x
Sometimes it is necessary to pass a structure to the tasks e.g., to initialize task parameters. An example of that is shown in the last two examples. Task priorities etc. can be set as desired, as  documented in the FreeRTOS homepage.
The project is intended to run on a STM32 Nucleo F103RB board. But any similar is fine.
Several versions of freertos_testxx.c have been included. The project  can be built using any of them by changing the Makefile. I wrote them  when working with the project. Their function is shown below:
freertos_test01.c
First tests of periodic task creation using vTaskDelayUntil. Tasks  are created with no passed data structures. Their operation as intended  was confirmed.
As set up, T1 and T2 are "periodic" tasks; T2 has lower priority than  T1. T2 will only run when T1 is done. T1 flashes the on board LED when  it is running. Since T1 has a long execution time, the start of T2 is  delayed, and you can see this in the flashing pattern of T2; it is  irregular. However, when we exchange the priorities and T2 becomes the  high priority task, its flashing becomes regular; whenever it is  released, it is immediately executed by the the RTOS kernel.
freertos_test02.c
Same as previous.Added a single value data passed to the task; its period. The passed  value is declared in the function call as a void pointer. In the target  function body, it must be either assigned to the correct pointer type  (by declaring a pointer of the correct type, and equating it to the  function argument void pointer through a suitable type-cast), or each  time it is used, type-cast it to the correct pointer type. This may even  be done implicitly since the function argument where this is used, or  the left hand side of the equality will dictate a correct type-cast.  However, better not to leave it to the compiler as implicit; some  ambiguity might break the program.
Tasks are also truly periodic (in the sense of FreeRTOS) in which vTaskDelayUntil() function call is used.
It works fine.freertos_test03.c
Same as previous.Added a data structure to pass the data to the task. The  data structure must be in the memory when the task runs. So that  precludes the possibility of creating a data structure on the fly for  the task in main() and remove it after task creation. It must be there  all the time.
The data structure must be created by using a malloc call (apparently  this costs time, but it is done only during initialization, so it is  possible). 
TODO: Check if malloc is allowed in the heap-1 model.In this example, T1 parameters are passed using a structure. T2 has only one parameter. This is passed in a simpler manner, by type casting to void * on the fly. Even a literal (directly written number) can be passed in this way.
When created correctly, the data is passed to the task correctly. However, if a  normal structure declaration is used within the main function, then  apparently, the values are on the stack of main, and are not available  to the calling function. The latter method does not work. Program  compiles but the tasks do not get the intended values.
freertos_test04.c
Tasks receive all the critical values through the passed variable  structure, i.e. period, number of instructions, and GPIO port to toggle  for execution. This simplifies the structure for demos, because only 1  task function can be written, and shared among all created tasks.
freertos_test04a.c
Only one task function is written and all tasks are created as  different instances of this. Their parameters are set by the passed data  structure during the creation function call. This works fine. The body  of the same task is called with different arguments to create several  tasks.
The execution of the tasks can be recorded and visualized using a (cheap) logic analyzer and a viewing program such as GTK Wave, if they set a GPIO pin at entry, and reset it at completion. One sample run of 500ms is shown in the photo below. Task T1 period is 30ms and it has a higher priority. Task T2 period is 50ms and priority is lower at 50ms. 
|  | 
| The task execution times seen in GTK Wave | 
You can see the signals repeat at the  least common multiple of the periods (150ms), also called the  hyperperiod. You can also see T1 preemts T2 just after zero; T2 seems to  execute longer than usual, and T1 and T2 are setting their GPIO pins at  the same time. What is actually happening is that when the time to  execute T1 comes, T2 is preempted (suspended), and T1 runs instead. This happens three more times in the example above.
You can also see that the execution times of T1 are regular, but the execution times of T2 are irregular. Being lower priority, even if T2 is released, it must first wait for T1 to complete.
 
No comments:
Post a Comment