FreeRTOS newbie - STM32F103 + libopenCM3 + build errors

Hello Kind folks,
A newbie to freeRTOS here. I am wanting to get my hands dirty with freeRTOS and I have some experience with libopenCM3. I am trying to work on a custom STM32 board with the STM32F103RCT6 processor, not the bluepill board. minor difference but a difference nevertheless. I am using a arm-none-eabi-gcc toolshain which is present in my system path. I am using a MBP running 11.4 Big Sur for sake of completeness.

I have gone through various posts… googled for answers… tried incorporating some of the suggestions but still fail to get a simple blinky working. I come here to seeking knowledge and information, not to inconvenience other folks. :slight_smile: Please bear with me.

First , my directory structure.

.
├── freertos_test
├── libopencm3
│   ├── doc
│   ├── include
│   ├── ld
│   ├── lib
│   ├── mk
│   ├── scripts
│   └── tests
└── shared_libs
    ├── FreeRTOS
    ├── cryptoauthlib
    ├── hd44780_v2
    ├── littlefs
    └── mhz19b

My projects working directory is freertos_test (surprise! surprise! ) . I have unzipped the freertos into the shared_libs/FreeRTOS directory. I have modified the makefile to include the necessary ( I hope) source files from freertos and also added the include directories.
I have used the FreeRTOSConfig.h file from CORTEX_STM32F103_GCC_Rowley as a starting point.

These are the contents of my makefile.

PROJECT = freeRTOS_test
BUILD_DIR = bin

SHARED_DIR = ../shared_libs 

FREERTOS_SRC = ../shared_libs/FreeRTOS/FreeRTOS/Source
FREERTOS_HEADERS = ../shared_libs/FreeRTOS/FreeRTOS/Source/include

INCLUDES += -I$(FREERTOS_HEADERS)
INCLUDES += -I$(FREERTOS_SRC)/portable/GCC/ARM_CM3

TGT_CFLAGS	+= -I$(FREERTOS_HEADERS)
TGT_CXXFLAGS	+= -I$(FREERTOS_HEADERS)

CFILES = main.c
CFILES +=$(FREERTOS_SRC)/portable/GCC/ARM_CM3/port.c  $(FREERTOS_SRC)/tasks.c  $(FREERTOS_SRC)/list.c $(FREERTOS_SRC)/queue.c $(FREERTOS_SRC)/portable/MemMang/heap_2.c 

# TODO - you will need to edit these two lines!
DEVICE=stm32f103rct6
# ST-FLASH = st-flash
# OOCD_INTERFACE = stlink-v2-1
# OOCD_TARGET = stm32l1
# OOCD_FILE = board/stm32ldiscovery.cfg

# You shouldn't have to edit anything below here.
VPATH += $(SHARED_DIR)
INCLUDES += $(patsubst %,-I%, . $(SHARED_DIR))
OPENCM3_DIR=../libopencm3

include $(OPENCM3_DIR)/mk/genlink-config.mk
include ../rules.mk
include $(OPENCM3_DIR)/mk/genlink-rules.mk

size: all
	$(SIZE) $(PROJECT).elf

write: $(PROJECT).bin
	$(ST-FLASH) write $(PROJECT).bin 0x8000000

erase:
	$(ST-FLASH) erase

And this is the content of my main.c file. As I have said before, this is on a custom board that I have made and am wanting to start learning freertos on. As such, the leds are on PORTB GPIO0 and GPIO1.

#include "FreeRTOS.h"
#include "task.h"

#include "libopencm3/stm32/gpio.h"
#include "libopencm3/stm32/rcc.h"

void BlinkLED1(void *parameters) {
  (void)parameters;

  for (;;) {
    gpio_toggle(GPIOB, GPIO0);
    vTaskDelay(500 / portTICK_PERIOD_MS);
  }
}

void BlinkLED2(void *parameters) {
  (void)parameters;

  for (;;) {
    gpio_toggle(GPIOB, GPIO1);
    vTaskDelay(700 / portTICK_PERIOD_MS);
  }
}

int main(void) {

  // Set GPIO8 and clear GPIO9 to see toggle

  rcc_clock_setup_in_hsi_out_64mhz();
 // rcc_clock_setup_pll(&rcc_hsi_configs[RCC_CLOCK_HSI_64MHZ]);
 

  rcc_periph_clock_enable(RCC_GPIOB);
  // Output mode, no pull ups or pull down
  gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL,
                GPIO0 | GPIO1);

  xTaskCreate(BlinkLED1, "LED1 Blink", configMINIMAL_STACK_SIZE + 500, NULL, 2,
              NULL);

  xTaskCreate(BlinkLED2, "LED2 Blink", configMINIMAL_STACK_SIZE + 500, NULL, 2,
              NULL);

  vTaskStartScheduler();
  while (1) {
  }

  return 0;
}

Now when I try to run make, I got this error.

main.c:7:6: warning: no previous prototype for 'BlinkLED1' [-Wmissing-prototypes]
    7 | void BlinkLED1(void *parameters) {
      |      ^~~~~~~~~
main.c:16:6: warning: no previous prototype for 'BlinkLED2' [-Wmissing-prototypes]
   16 | void BlinkLED2(void *parameters) {
      |      ^~~~~~~~~
main.c: In function 'main':
main.c:41:3: warning: 'rcc_clock_setup_in_hsi_out_64mhz' is deprecated: use rcc_clock_setup_pll(&rcc_hsi_configs[RCC_CLOCK_HSI_64MHZ]) [-Wdeprecated-declarations]
   41 |   rcc_clock_setup_in_hsi_out_64mhz();
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../libopencm3/include/libopencm3/stm32/rcc.h:26,
                 from main.c:5:
../libopencm3/include/libopencm3/stm32/f1/rcc.h:776:6: note: declared here
  776 | void rcc_clock_setup_in_hsi_out_64mhz(void) LIBOPENCM3_DEPRECATED("use rcc_clock_setup_pll(&rcc_hsi_configs[RCC_CLOCK_HSI_64MHZ])");
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/tasks.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/list.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/queue.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/portable/MemMang/heap_2.c
  GENLNK  stm32f103rct6
  LD    freeRTOS_test.elf
/Users/srikrishnachaitanyanarumanchi/gcc-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld: bin/../shared_libs/FreeRTOS/FreeRTOS/Source/tasks.o: in function `vTaskSwitchContext':
/Users/srikrishnachaitanyanarumanchi/Workspace/libopencm3/test/freertos_test/../shared_libs/FreeRTOS/FreeRTOS/Source/tasks.c:3052: undefined reference to `vApplicationStackOverflowHook'
collect2: error: ld returned 1 exit status
make: *** [freeRTOS_test.elf] Error 1

Which, from my limited understanding and reading on the forums around needs to defined somewhere in case the application stackoverflows or to disable it in the freertosconfig.h file by settings this #define configCHECK_FOR_STACK_OVERFLOW 2 to 0

I did that and reran the make command again, and it compiles fine this time…

 make
  CC    main.c
main.c:7:6: warning: no previous prototype for 'BlinkLED1' [-Wmissing-prototypes]
    7 | void BlinkLED1(void *parameters) {
      |      ^~~~~~~~~
main.c:16:6: warning: no previous prototype for 'BlinkLED2' [-Wmissing-prototypes]
   16 | void BlinkLED2(void *parameters) {
      |      ^~~~~~~~~
main.c: In function 'main':
main.c:41:3: warning: 'rcc_clock_setup_in_hsi_out_64mhz' is deprecated: use rcc_clock_setup_pll(&rcc_hsi_configs[RCC_CLOCK_HSI_64MHZ]) [-Wdeprecated-declarations]
   41 |   rcc_clock_setup_in_hsi_out_64mhz();
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../libopencm3/include/libopencm3/stm32/rcc.h:26,
                 from main.c:5:
../libopencm3/include/libopencm3/stm32/f1/rcc.h:776:6: note: declared here
  776 | void rcc_clock_setup_in_hsi_out_64mhz(void) LIBOPENCM3_DEPRECATED("use rcc_clock_setup_pll(&rcc_hsi_configs[RCC_CLOCK_HSI_64MHZ])");
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/tasks.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/list.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/queue.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/portable/MemMang/heap_2.c
  GENLNK  stm32f103rct6
  LD    freeRTOS_test.elf
  OBJCOPY       freeRTOS_test.bin
arm-none-eabi-size freeRTOS_test.elf
   text    data     bss     dec     hex filename
  16624      24   18712   35360    8a20 freeRTOS_test.elf

except… nothing happens… the program literally does nothing… I expect to see two blinking LEDS… and there is literally no activity…
SO, I fired up the debugger to find that the program was ending up in the blocking_handler, when it entered the vTaskStartScheduler function.

The exact call stack is as follows.
vTaskStartScheduler -> xPortStartScheduler -> prvPortStartFirstTask -> <HardFault_Exception>

From a little more googling around, I understand that this is due to this
FreeRTOS FAQ - My application does not run ( Apparently new users cannot post a link.)
about a wrong interrupt vector table. I tried copy pasting the following three #defines to my FreeRTOSConfig.h file but the behavior is still the same. blocking handler.

#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler

I suspected that this might also be caused if the handler functions in libopenCM3 are not defined as they are in the FAQ and looked around in the source of libopenCM3 to get the exact handler names, and used those in the #defines, like so.

/* Redirect FreeRTOS port interrupts. */
#define vPortSVCHandler     sv_call_handler
#define xPortPendSVHandler  pend_sv_handler
#define xPortSysTickHandler sys_tick_handler

Unfortunately, the same behavior persists.

I would be extremely grateful if someone more knowledgeable than me can point out what I am doing wrong and how to correct it. Again, I am a newbie with freeRTOS andI suspect this setup trouble are few and far inbetween.

I do apologize for the rather long post but I figured it would better to be verbose, in order to not waste your time and to help you folks, help me… :slight_smile:

Thank you again folks, in advance and I do apologize if I have broken any of the forums rules. Please excuse me on the account of a noob.

I strongly recommend to define configASSERT and also enable stack overflow checking for development/debugging.
A simple implementation of vApplicationStackOverflowHook could be similar to the configASSERT macro ending up in a forever loop allowing to halt the program with the debugger and check the call stack to see what caused the assertion or stack overflow.
I’m not familiar with libopencm3 but can you just leave the default handlers as they are defined in the demo ? They should work. The libopencm3 handlers are maybe not appropriate or is it documented that they’re compatible with FreeRTOS (invoking the required FreeRTOS functions) ?

Thank you for the prompt response @hs2 .
I did try those suggestions earlier as well. I tried to keep the post simple to avoid unnecessary complexity, to see if I was missing something simple.

  1. I did leave the configCHECK_FOR_STACK_OVERFLOW to 2 and added this snippet to the main.c file.
void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{
	//  This function will get called if a task overflows its stack.   If the
	// parameters are corrupt then inspect pxCurrentTCB to find which was the
	// offending task. 

	( void ) pxTask;
	( void ) pcTaskName;

	for( ;; );
}
  1. I did comment out the handlers entirely.

I reran the make clean and make command to ensure a clean build and the application builds… but the leds do not blink.

The call stack remain the same. I will however look into the configASSERT to see how I can utilize it to debug better…

Thanks again for the suggestions. :slight_smile:

This is correct for using libopencm3. So keep this.

Also the warning about the deprecated RCC function seems important. The updated code is commented out in your source code, so maybe there’s more to this story. But it’s probably best to make the recommended RCC call instead of the deprecated one.

Do you have a debugger? Can you put a breakpoint at the beginning of vPortSVCHandler() and then single step the assembly language? You should end up in one of your tasks after you step through this instruction.

@jefftenney Thank you the response Jeff.

  1. Thanks for confirming the correct handlers for libopenCM3. :slight_smile:
  2. I did try making the recommended function call instead of the deprecated one and got this error.
 CC    main.c
main.c:7:6: warning: no previous prototype for 'BlinkLED1' [-Wmissing-prototypes]
    7 | void BlinkLED1(void *parameters) {
      |      ^~~~~~~~~
main.c:16:6: warning: no previous prototype for 'BlinkLED2' [-Wmissing-prototypes]
   16 | void BlinkLED2(void *parameters) {
      |      ^~~~~~~~~
main.c:25:6: warning: no previous prototype for 'vApplicationStackOverflowHook' [-Wmissing-prototypes]
   25 | void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/tasks.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/list.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/queue.c
  CC    ../shared_libs/FreeRTOS/FreeRTOS/Source/portable/MemMang/heap_2.c
  GENLNK  stm32f103rct6
  LD    freeRTOS_test.elf
/Users/srikrishnachaitanyanarumanchi/gcc-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld: bin/main.o: in function `main':
/Users/srikrishnachaitanyanarumanchi/Workspace/libopencm3/test/freertos_test/main.c:42: undefined reference to `rcc_clock_setup_pll'
/Users/srikrishnachaitanyanarumanchi/gcc-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld: /Users/srikrishnachaitanyanarumanchi/Workspace/libopencm3/test/freertos_test/main.c:57: undefined reference to `rcc_hsi_configs'
collect2: error: ld returned 1 exit status
make: *** [freeRTOS_test.elf] Error 1

but did not pursue beyond this and instead went back to the deprecated call.

  1. I do have a debugger, however I ll be able to get to it only after the workday is finished for me. :frowning: . Please do give me sometime before I can report back with my findings.

Thanks again for the suggestions.

@jefftenney
It was a missing #define. These are the contents of the FreeRTOSConfig.h file from the demo in CORTEX_STM32F103_GCC_ROWLEY folder

/*
 * FreeRTOS V202104.00
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://www.FreeRTOS.org
 * http://aws.amazon.com/freertos
 *
 * 1 tab == 4 spaces!
 */

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 *
 * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
 *
 * See http://www.freertos.org/a00110.html
 *----------------------------------------------------------*/

#define configUSE_PREEMPTION		1
#define configUSE_IDLE_HOOK			0
#define configUSE_TICK_HOOK			0
#define configCPU_CLOCK_HZ			( ( unsigned long ) 72000000 )
#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES		( 5 )
#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 120 )
#define configTOTAL_HEAP_SIZE		( ( size_t ) ( 18 * 1024 ) )
#define configMAX_TASK_NAME_LEN		( 16 )
#define configUSE_TRACE_FACILITY	1
#define configUSE_16_BIT_TICKS		0
#define configIDLE_SHOULD_YIELD		1

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 		0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

#define configUSE_MUTEXES				1
#define configUSE_COUNTING_SEMAPHORES 	1
#define configUSE_ALTERNATIVE_API 		0
#define configCHECK_FOR_STACK_OVERFLOW	2
#define configUSE_RECURSIVE_MUTEXES		1
#define configQUEUE_REGISTRY_SIZE		0
#define configGENERATE_RUN_TIME_STATS	0

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */

#define INCLUDE_vTaskPrioritySet		1
#define INCLUDE_uxTaskPriorityGet		1
#define INCLUDE_vTaskDelete				1
#define INCLUDE_vTaskCleanUpResources	0
#define INCLUDE_vTaskSuspend			1
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay				1

/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY 		255
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */


/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15.  This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
NVIC value of 255. */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15

/*-----------------------------------------------------------
 * UART configuration.
 *-----------------------------------------------------------*/
#define configCOM0_RX_BUFFER_LENGTH		128
#define configCOM0_TX_BUFFER_LENGTH		128
#define configCOM1_RX_BUFFER_LENGTH		128
#define configCOM1_TX_BUFFER_LENGTH		128

#endif /* FREERTOS_CONFIG_H */


It is missing the
#define configSYSTICK_CLOCK_HZ ( configCPU_CLOCK_HZ / 8 )
define. I referred to warren gays repo and compared the FreeRTOSConfig file vs the example.
This was one of the differences, but this popped out at me since the actual line was this.
#define configSYSTICK_CLOCK_HZ ( configCPU_CLOCK_HZ / 8 ) /* fix for vTaskDelay() */

The comment at the end made look twice and check with this define… Before this I tried the fixes suggested in the book for FreeRTOS… including creating a separate opencm3.c file with the interrupt handlers and so on… but it always ended in a blocking handler. This fixed the issue.

Thank you @jefftenney and @hs2 for the prompt help and suggestions. It turned out to be a mistake from my end. This is what I suppose happens when one blindly copies stuff without understanding the implications… I dont know if the original example compiles and runs fine though. This might need to be fixed there as well…

Thanks for reporting back. In your case, the definition of configSYSTICK_CLOCK_HZ is optional, so I can’t understand how defining it fixed the issues you were having. When you define that symbol, FreeRTOS configures the SysTick a little differently, but that shouldn’t have impacted anything of any consequence in your case.

Also it’s best to avoid that file you mentioned, opencm3.c. The PendSV handler is intended to be the ISR, not to be called from the ISR. If the PendSV handler is called from the ISR, and if the ISR uses stack, the PendSV handler can’t do its job properly. The proper alternative to opencm3.c is the three #define statements you mentioned earlier that put the FreeRTOS handlers directly into the vector table.

You are right. the configSYSTICK_CLOCK_HZ was a red herring. I tried a few different things yesterday, one of them was to use the FreeRTOSConfig.h file from the authors repo along with the opencm3.c file. and during one the attempts it worked. I do not remember exactly what I did to make it work. I had a brief look this morning and commenting out the #define made no difference. The leds still blink. I even tried reverting to the old sample FreeRTOSConfig.h file from the demo folder what I pasted above and even that works now. :neutral_face:

And I did not end up using the opencm3.c file - either yesterday or earlier today. I had the same doubts which you just confirmed. The way the author does is by essentially going in the reverse order by calling the PendSV handler from inside the ISR, whereas the #define basically defines it so. I do (well did not know until you educated me so) not know the implications of the authors approach.

I ll try and recreate the example I posted above from scratch and see if I can figure out what I was doing wrong and what actually fixes it, later in the evening after work.

@jefftenney Thank you for pointing that out and correcting me. :slight_smile:

Okay… I started from scratch in another folder and did the following steps.

  1. I copied the FreeRTOSConfig.h file from the demo folder.
  2. Added the required FreeRTOS c files to the makefile along with the header paths, as shown in the makefile.
  3. Modified the freeRTOSConfig.h file to include the ISR handler definitions as per your suggestion @jefftenney . Disabled the configCHECK_FOR_STACK_OVERFLOW by setting it to 0.
  4. Set the correct value for configCPU_CLOCK_HZ to 64Mhz as per the code.
  5. Used the same main.c file as shown in my explanation above.

And it just works… I m not sure why it was behaving otherwise yesterday. I m guessing I did some small mistake that I forgot about.

Either way, @jefftenney and @hs2 Thank you folks again for your patience and support. It looks like it was some mistake I did, but you guys were helpful and walked me through clearing my misunderstanding and helped fix the issue.

Thank you again!

1 Like