2024-07-31 12:32 PM - edited 2024-07-31 02:51 PM
If this question is silly or obvious, I apologize, but I'd like some guidance on understanding this.
I am playing around with C++ and freeRTOS on a STM32L476RGT6, below is a code snippet that creates an object main function of my program. This object contains a task function which blinks and an LED via calling a set(bool on) member method.
int main_cpp() {
Led led_1(GPIO_PORT, GPIO_NUM); //case 1 (broken): allocating object application stack/main stack of program (RAM)
//static Led led_2(GPIO_PORT, GPIO_NUM); //case 2 (works): allocating in .bss segment (Flash)
//Led *led_3 = new Led(GPIO_PORT, GPIO_NUM) //case 3 (works): allocating on heap (RAM)
vTaskStartScheduler();
return 0;
}
//inside Led.cpp:
#include "Led.h"
Led::Led(GPIO_TypeDef *gpio_port, uint16_t gpio_num):
gpio_port(gpio_port), gpio_num(gpio_num){
xTaskCreate(blink_task_trampoline, "Blink Task", 255, this, 1, &task_blink_hdl);
}
void Led::set(bool on)
{
HAL_GPIO_WritePin(gpio_port, (1U << 8), on ? GPIO_PIN_RESET : GPIO_PIN_SET); //set pin according to led_state
}
void Led::blink_task_trampoline(void *pvParameters)
{
//recast void pointer to led object pointer
Led *this_led = (Led *)pvParameters;
//launch the blink task
this_led->blink_task();
}
void Led::blink_task()
{
static bool led_state = true; //whether the led is on or off
set(true); //initially set LED high
while (1) {
//delay for 200ms, portTICK_PERIOD_MS is the duration of 1 tick period in ms
vTaskDelay(200 / portTICK_PERIOD_MS);
//toggle led
set(led_state);
led_state = !led_state;
}
}
In the main function, initializing via case 2 or case 3 works correctly, case 1 has an issue in which member variables gpio_num and gpio_port are overwritten with different values once the set method is called.
My hunch is that when vTaskScheduler() is called, freeRTOS uses the portion of the application stack that the Led object lives on, overwriting some of its members. Therefore, case 2 and 3 work correctly as declaring as static places it in .bss segment of flash, and using new places it on the heap.
Here are my questions:
1. Is my assumption most likely correct? How can I go about proving it and what are the best methods for debugging issues like this?
2. For those of you who've worked on professional embedded code bases, what is generally best practice for declaring large objects or critical objects containing tasks?
Outside of edge cases, is it usually best to place them in flash for safety reasons?
Thanks for your time and patience,
Solved! Go to Solution.
2024-07-31 03:23 PM
> Is my assumption most likely correct?
Yes. When vTaskStartScheduler is called, the "main stack" is reset and reused, so it isn't a good idea to leave objects on this stack.
> How can I go about proving it
It's up to you )) A hint: try to read some documentation. Reading the actual sources of FreeRTOS can help too.
> what is generally best practice for declaring large objects or critical objects containing tasks?
Not on stacks, sure enough )) Splitting objects to read-only (ROM) and read-write (RAM) parts is a common practice. Not only for safety, rather to save the RAM.
2024-07-31 03:23 PM
> Is my assumption most likely correct?
Yes. When vTaskStartScheduler is called, the "main stack" is reset and reused, so it isn't a good idea to leave objects on this stack.
> How can I go about proving it
It's up to you )) A hint: try to read some documentation. Reading the actual sources of FreeRTOS can help too.
> what is generally best practice for declaring large objects or critical objects containing tasks?
Not on stacks, sure enough )) Splitting objects to read-only (ROM) and read-write (RAM) parts is a common practice. Not only for safety, rather to save the RAM.