STM32duino / FreeRTOS problems

Hello,

My first day with PlatformIO and having strange issues with FreeRTOS. Please see at the bottom of the post the code I am using. Situation is this:

FreeRTOS example works just fine if I paste the same code into Arduino IDE
When I try to run the same code on PlatformIO (only difference is the addition of Arduino.h, nothing happens.

  • If I comment out FreeRTOS related commands, loop works, leds blink.
  • If I add back the TaskCreate commands, but don’t start the scheduler, loop() runs only once, lights stay lit.
  • If I start the scheduler as well, nothing happens (doesn’t reach to loop())

I cannot tell what is different or what I might be doing wrong. It almost feels like on FreeRTOS stacks somehow get corrupted, hence I increased stack sizes to 1000 but didn’t make a difference.

I appreciate if you can offer ideas on how this issue can be further troubleshoot (unless of course it is an obvious mistake)

#include <Arduino.h>
#include <STM32FreeRTOS.h>

#define LED1 PB0
#define LED2 PB1
 
static void task1(void *pvParameters) {
  for (;;) {
      vTaskDelay(1000);
      digitalWrite(LED1, HIGH);
      vTaskDelay(1000);
      digitalWrite(LED1, LOW);
  }
}
 
static void task2(void *pvParameters) {
  for (;;) {
      vTaskDelay(200);
      digitalWrite(LED2, HIGH);
      vTaskDelay(200);
      digitalWrite(LED2, LOW);
  }
}
 
void setup() 
{
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  xTaskCreate(task1,"Task1",
              1000,NULL,tskIDLE_PRIORITY + 2,NULL);
  xTaskCreate(task2,"Task2",
              1000,NULL,tskIDLE_PRIORITY + 2,NULL);
  vTaskStartScheduler();
}
 
void loop() 
{
  digitalWrite(PB1, !digitalRead(PB1));
  digitalWrite(PB0, !digitalRead(PB0));
  delay(1000);
}

I’m not familiar with STM32, so don’t take my words for it.

As for FreeRTOS, the vTaskStartScheduler() function does not return, as specified in FreeRTOS reference manual, unless memory allocation fails. You can see framework-arduinoststm32/cores/arduino/main.cpp where the main() function is defined.

/*
 * \brief Main entry point of Arduino application
 */
int main(void)
{
  initVariant();

  setup();

  for (;;) {
#if defined(CORE_CALLBACK)
    CoreCallback();
#endif
    loop();
    serialEventRun();
  }

  return 0;
}

Since vTaskStartScheduler() does not return, you can’t use FreeRTOS task mechanism along with Arduino loop().

Thanks for the reply.

The commands in the loop() are there purely to test. If I comment out vTaskStartScheduler(); one would expect loop to work. But it gets only executed once, LEDs stays lit and it hangs.

I came across another similar FreeRTOS problem (on ESP32) resolved using lib_archive = no in platformio.ini but it didn’t help in my case.

With STM32 and platformio, STLink V2 is your friend.

It will unlock for you hardware debugging capabilities that the Arduino IDE lacks. For example, you will be able to single step or break or pause the program and understand what’s going on.

What’s the platformio.ini file?

@zapta , I do debug but I don’t think platformio/stm32 pair is capable of debugging into the threads and the scheduler. Please let me know if your understanding is different.

@maxgerhardt , thank you, please see platformio below. One of the many tries but this is the latest and most simple one. If Arduino IDE can run RTOS code with no issues using generic variant I am expecting to be able to do the same with platformio.

[env:genericSTM32L052K6]
platform = ststm32
board = genericSTM32L052K6
framework = arduino
lib_deps = stm32duino/STM32duino FreeRTOS@^10.3.2
lib_archive = no

@cagz, yes, that’s possible. You need to add a specific symbol and make sure it doesn’t optimized out and then add --rtos auto in the appropriate ocd file. More info here How to make the STM32 debugger aware of FreeRTOS tasks? - #2 by maxgerhardt . maxgerhardt is the expert for this as well.

It works great for me.

I wish platformio would make the debugger thread aware out of the box.

https://github.com/platformio/platform-ststm32 does not contain this board definition. Thus seems custom made. What’s the boards/genericSTM32L052K6.json content?

Wow this was helpful. Debugging actually works into the scheduler (without needing to make the changes mentioned) and obviously I hit the error which says HEAP cannot be allocated.

Now the question is why it manages to allocate heap fine on Arduino IDE but not on Platformio.

Contents of the json file is below, thanks for the help.

{
“build”: {
“core”: “stm32”,
“cpu”: “cortex-m0plus”,
“extra_flags”: “-DSTM32L052xx -DSTM32L0”,
“f_cpu”: “32000000L”,
“mcu”: “stm32l052k6t6”,
“product_line”: “STM32L052xx”,
“variant”: “STM32L0xx/L052K(6-8)T_L062K8T”
},
“debug”: {
“jlink_device”: “STM32L052K6”,
“openocd_target”: “stm32l0”,
“svd_path”: “STM32L052xx.svd”
},
“frameworks”: [
“arduino”
],
“name”: “Cagri STM32L052K6 (8k RAM. 64k Flash)”,
“upload”: {
“maximum_ram_size”: 8192,
“maximum_size”: 32768,
“protocol”: “stlink”,
“protocols”: [
“stlink”
]
},
“url”: “STM32F103RD - Mainstream Performance line, Arm Cortex-M3 MCU with 384 Kbytes of Flash memory, 72 MHz CPU, motor control, USB and CAN - STMicroelectronics”,
“vendor”: “Generic”
}

Very brave of you to try an RTOS on 8K of RAM. It should be close to its memory limit.

Can you decrease the stack memory created for each thread to e.g., 512 or 256? These should be stack elements (aka 4 bytes / sizeof(int)), so for the executed task code this seems to be very overkill.

Hmm, good point. Still on Arduino I can run 4 tasks (not doing much) with 128 words (256 bytes) stack each. On platformio 2 tasks doesn’t start with 128, or even when I reduce it to 16 words.

Which structure does it fail to allocate in the debugger?

And what are the “Tools” Arduino IDE settings you use there?

That will need mode debugging will get back with data !
Arduino IDE, nothing special,

What version of the STM32 core does it show in Arduino IDE → Tools → Board → Board Manager?

2.6.0 is the version under board manager

I can move to 20k RAM version of the chip, sound like it is a good idea anyways. However I don’t think we are hitting 8k limit here.

Further debugging, in task.c (task creation)

		StackType_t *pxStack;

			/* Allocate space for the stack used by the task being created. */
			pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); 
    // allocation is for 16 x 4U and looks ok, xTaskCreate is called with 16 bytes stack

			if( pxStack != NULL )
			{
				/* Allocate space for the TCB. */
				pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 
      // this one is odd, sizeof(TCB_t) is 1160U, task surely does not require this much.

Yeah that looks insane, 1160 bytes for a task control block? Damn.

I’ll try and reproduce this under PlatformIO with your settings.

That’s more recent than what PlatformIO is using (2.5.0)

Well that was easy to find

Now let’s see why it needs that and whether it’s the same on the Arduino IDE 2.0…

I think a smoking gun here is that the STM32Duino core uses

          "toolsDependencies": [
            {
              "packager": "STMicroelectronics",
              "name": "xpack-arm-none-eabi-gcc",
              "version": "12.2.1-1.2"
            },

But PlatformIO uses in the latest version

PLATFORM: ST STM32 (16.1.0) > Cagri STM32L052K6 (8k RAM. 64k Flash)
HARDWARE: STM32L052K6T6 32MHz, 8KB RAM, 32KB Flash
DEBUG: Current (stlink) External (stlink)
PACKAGES:
 - framework-arduinoststm32 @ 4.20500.230714 (2.5.0)
 - framework-cmsis @ 2.50700.210515 (5.7.0)
 - toolchain-gccarmnoneeabi @ 1.100301.220327 (10.3.1)

So, the compiler versions differ and it could be that their 12.2.1 toolchain was build with e.g. _REENT_SMALL to reduce the size of the reentrancy structure, making the TCB_t structure smaller.

And yes…

grafik

Using

[env:genericSTM32L052K6]
platform = ststm32@16.1.0
board = genericSTM32L052K6
framework = arduino
lib_deps = stm32duino/STM32duino FreeRTOS@^10.3.2
; upgrade compiler
platform_packages =
    toolchain-gccarmnoneeabi@~1.120201.0

and after saving and waiting for re-init also doing a Ctrl+Shift+P → Reload Window, it does now use a toolchain which has a much small _reent structure size. This should be what STM32Duino is using at 2.5.0. This is also what the Arduino iDE is using at 2.5.0 but PlatformIO differs from that.

Can you test the above platformio.ini and check if now the firmware behaves the same in PlatformIO as in the Arduino IDE?

Oh wow ! It worked ! It for some reason broke debugger hence I cannot report what the TCB dropped to from 1160 bytes, but it is working.

I couldn’t run more than two 128byte tasks, so it is not still as good as Arduino IDE but definitely workable. I’ll try to debug both and see the difference, if any, in stack allocation.

Thanks a lot for your help.

I am getting this error btw, with both compilers, does it ring a bell with you ?

ld/genericSTM32L052K6/lib6dd/STM32duino FreeRTOS/port.c.o
In file included from .pio/libdeps/genericSTM32L052K6/STM32duino FreeRTOS/src/heap.c:25:
.pio/libdeps/genericSTM32L052K6/STM32duino FreeRTOS/src/../portable/MemMang/heap_useNewlib_ST.c:77:4: warning: #warning "This wrapper was verified for newlib versions 2.5 - 3.3; please ensure newlib's external requirements for malloc-family are unchanged!" [-Wcpp]
   77 |   #warning "This wrapper was verified for newlib versions 2.5 - 3.3; please ensure newlib's external requirements for malloc-family are unchanged!"
      |    ^~~~~~~
Compiling .pio/build/genericSTM32L052K6/lib6dd/STM32duino FreeRTOS/portasm.c.o
Compiling .pio/build/genericSTM32L052K6/src/main.cpp.o

And after upgrading compiler I started getting:

PlatformIO: debug_tool = stlink
PlatformIO: Initializing remote target...
.pioinit:13: Error in sourced command file:
Remote connection closed