Home | Projects | Notes > Real-Time Operating Systems (RTOS) > Exercise: Toggle LEDs (02_LED_Tasks
)
02_LED_Tasks
)
Toggle the three LEDs of the STM32F407 Discovery board with the frequency as defined below:
LED_GREEN (Task1) - 1000 ms
LED_ORANGE (Task2) - 800 ms
LED_RED (Task3) - 400 ms
Create 3 FreeRTOS tasks of the same priority to handle three different LEDs.
Each task will NOT interfere with one another. (No shared resources)
Since this is a single-core system, "parallel" processing will be impossible to achieve. Just implement it in a time-sharing fashion!
All user level code (i.e., tasks) runs in Thread Mode of the processor.
CPU time-sharing is achieved by the scheduler.
Task management is required and provided by RTOS kernel.
Using priority among tasks can achieve prioritized task management.
Can achieve low power (CPU is not always engaged; run a task and stay idle for some time...)
Create an STM32 project.
Instead of integrating the third party tools every time you create a project, you can create a common folder to all projects.
Create a Workspace/Common/
folder, and copy the Project/ThirdParty/
folder (which we created in the previous section when creating the first project) into the Workspace/Common/
folder.
Link it to the project. (Not copy, but link!)
Common
Include paths (Project
GNU C (Compiler include paths settings)
Workspace/Common/ThirdPary/FreeRTOS/include
Workspace/Common/ThirdPary/FreeRTOS/portable/GCC/ARM_CM4F
Workspace/Common/ThirdPary/SEGGER/Config
Workspace/Common/ThirdPary/SEGGER/OS
Workspace/Common/ThirdPary/SEGGER/SEGGER
Assembly
Workspace/Common/ThirdPary/SEGGER/Config
Workspace/Common/ThirdPary/SEGGER/SEGGER
[!] Note: Don't forget to click "Apply" after adding new include paths.
If this include paths setting is to be used over and over again with other projects, you can export and import this setting, which will save some time.
To export the include paths settings, do the following and export the settings for BOTH "GNU C" and "Assembly" using the same .xml
file. (You can open the .xml
file and check if all the necessary include paths are there.)
Create three tasks and implement each task function.
xxxxxxxxxx
71/* main.h */
2...
3/* USER CODE BEGIN Private defines */
4
5
6
7/* USER CODE END Private defines */
xxxxxxxxxx
1041/* main.c */
2...
3/* Private includes ----------------------------------------------------------*/
4/* USER CODE BEGIN Includes */
5
6
7/* USER CODE END Includes */
8...
9/* USER CODE BEGIN PV */
10
11/* USER CODE END PV */
12...
13/* USER CODE BEGIN PFP */
14static void led_green_task_handler(void *parameters);
15static void led_orange_task_handler(void *parameters);
16static void led_red_task_handler(void *parameters);
17/* USER CODE END PFP */
18...
19
20int main(void)
21{
22 /* USER CODE BEGIN 1 */
23 TaskHandle_t led_green_task_handle;
24 TaskHandle_t led_orange_task_handle;
25 TaskHandle_t led_red_task_handle;
26
27 BaseType_t status; // Stores return value of xTaskCreate()
28 /* USER CODE END 1 */
29
30 ...
31
32 /* USER CODE BEGIN 2 */
33
34 // Enable the cycle counter
35 DWT_CTRL |= (0x1 << 0); // Set SYCCNTENA bit of DWT_CYCCNT register
36
37 // Initialize UART with desired baudrate for SEGGER SystemView with UART-based recording
38 SEGGER_UART_init(500000); // Comment this line out if you are not using SEGGER SystemView
39
40 // Start the SEGGER SystemView recording of events
41 SEGGER_SYSVIEW_Conf(); // Comment this line out if you are not using SEGGER SystemView
42 //SEGGER_SYSVIEW_Start(); // This function will be called from ThirdParty/Rec/segger_uart.c
43
44 // Create LED_Green_Task and make sure that the task creation was successful
45 status = xTaskCreate(led_green_task_handler, "LED_Green_Task", 200, NULL, 2, &led_green_task_handle);
46 configASSERT(status == pdPASS);
47
48 // Create LED_Orange_Task and make sure that the task creation was successful
49 status = xTaskCreate(led_orange_task_handler, "LED_Orange_Task", 200, NULL, 2, &led_orange_task_handle);
50 configASSERT(status == pdPASS);
51
52 // Create LED_Red_Task and make sure that the task creation was successful
53 status = xTaskCreate(led_red_task_handler, "LED_red_Task", 200, NULL, 2, &led_red_task_handle);
54 configASSERT(status == pdPASS);
55
56 // Start FreeRTOS scheduler
57 // vTaskStartScheduler() never returns unless there's a problem launching scheduler
58 vTaskStartScheduler();
59
60 // This line will only be reached if the kernel could not be started because there was
61 // not enough FreeRTOS heap to create the idle task or the timer task.
62
63 /* USER CODE END 2 */
64 ...
65}
66
67...
68
69/* USER CODE BEGIN 4 */
70
71static void led_green_task_handler(void *parameters)
72{
73 while (1)
74 {
75 SEGGER_SYSVIEW_PrintfTarget("Toggling green LED");
76 HAL_GPIO_TogglePin(GPIOD, LED_GREEN_PIN);
77 HAL_DELAY(1000); // Blocking delay (Keeps the processor engaged in a while loop)
78 // Later will be replaced by the non-blocking delay.
79 }
80}
81
82static void led_orange_task_handler(void *parameters)
83{
84 while (1)
85 {
86 SEGGER_SYSVIEW_PrintfTarget("Toggling orange LED");
87 HAL_GPIO_TogglePin(GPIOD, LED_ORANGE_PIN);
88 HAL_DELAY(800); // Blocking delay (Keeps the processor engaged in a while loop)
89 // Later will be replaced by the non-blocking delay.
90 }
91}
92
93static void led_red_task_handler(void *parameters)
94{
95 while (1)
96 {
97 SEGGER_SYSVIEW_PrintfTarget("Toggling red LED");
98 HAL_GPIO_TogglePin(GPIOD, LED_RED_PIN);
99 HAL_DELAY(400); // Blocking delay (Keeps the processor engaged in a while loop)
100 // Later will be replaced by the non-blocking delay.
101 }
102}
103
104/* USER CODE END 4 */
GPIO settings are automatically done by the CubeIDE. These settings can be found in the MX_GPIO_Init()
function. (Also, you can check the "Device Configuartion Tool
To see their implementations, see Project/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c
. (Ctrl + O will show you the list of public APIs defined in the file.)
Before building the project, make sure to do the following settings:
Select the time base for STM32 HAL something other than the SysTick. (SysTick timer will be used for the FreeRTOS tick)
Do the priority group setting.
See, Timebase Source Selection section in the Creating FreeRTOS Project notes for the detailed steps for above 2 settings.
Also, disable the code generation of SysTick, SVC, and PendSV handlers.
Save
Call vInitPrioGroupValue()
from HAL_MspInit()
in the file stm32f4xx_hal_msp.c
. For this to work, FreeRTOS.h
must be included.
xxxxxxxxxx
151/* stm32f4xx_hal_msp.c */
2...
3/* Includes ------------------------------------------------------------------*/
4/* USER CODE BEGIN Includes */
5
6/* USER CODE END Includes */
7
8void HAL_MspInit(void)
9{
10 ...
11 /* USER CODE BEGIN MspInit 1 */
12 vInitPrioGroupValue();
13 /* USER CODE END MspInit 1 */
14 ...
15}
Copy Workspace/Common/ThirdParty/FreeRTOS/FreeRTOSConfig.h
to Project/Inc/
since FreeRTOSConfig.h
is a project-specific configuration file that cannot be shared among multiple projects. And, delete the original one.
Build and test it on the target.
Make sure in the Project/Inc/FreeRTOSConfig.h
, preemption is turned on #define configUSE_PREEMPTION 1
.
Turn on SEGGER SystemView UART-based recording.
In the file Project/Common/ThirdParty/SEGGER/Config/SEGGER_SYSVIEW_Conf.h
, make sure #define SEGGER_UART_REC 1
.
In the Project/Src/main.c
add the following lines in the main function:
SEGGER_UART_init(500000);
, SEGGER_SYSVIEW_Conf();
(Also, declare these function as extern
.)
xxxxxxxxxx
131/* main.c */
2...
3int main(void)
4{
5 ...
6 SEGGER_UART_init(500000);
7
8 // Enable the CYCCNT counter
9 DWT_CTRL |= (1 << 0);
10
11 SEGGER_SYSVIEW_Conf();
12 ...
13}
Since the UART recording calculates the Baudrate based on the HCLK
of 168 MHz, so change the HCLK
frequency to 168 MHz in the "Device Configuration Tool".
In the Project/Src/main.c
, now uncomment SEGGER_SYSVIEW_PrintfTarget("Toggling green LED");
lines in the task handlers.
Connect the board to the host PC using the USB-to-Serial cable. (Now, the board should be connected to your PC via 2 cables.)
Open SEGGER SystemView and do the UART-based recording.
These three tasks are continuous tasks, meaning that even if the task has nothing to do, it still consumes the CPU clocks (in a loop) until it gets preempted. (You can see also from the CPU load window that the CPU has never been idle.) Very inefficient!
We'll improve it in the following sections by making CPU sleep when it's not needed thus minimizing the power consumption.
Nayak, K. (2022). Mastering RTOS: Hands on FreeRTOS and STM32Fx with Debugging [Video file]. Retrieved from https://www.udemy.com/course/mastering-rtos-hands-on-with-freertos-arduino-and-stm32fx/