Platformio create libopencm3 + FreeRTOS project

Time:2022-7-26

PlatformIO: libopencm3 + FreeRTOS

The following steps are based on the common bluepill stm32f103c8t6, and are also applicable to other MCU models supported by libopencm3

Scheme 1: copy only the required files

  1. In platformio, select bluepill f103c8 for board and libopencm3 for framework to create the project
  2. Create a new directory FreeRTOS under lib of the project
  3. Unzip the latest FreeRTOS
    1. Copy all files and directories except portable directory under freertos/source/ directory to lib/freertos
    2. Copy freertos/source/portable/gcc/arm_ All files in the cm3 directory (port.c, portmacro.h) to lib/freertos
    3. Copy all files (mpu_wrappers.c) in the directory freertos/source/portable/common to lib/freertos
    4. Copy heap under freertos/source/portable/memmang directory_ 4. C, to lib/freertos
  4. Copy freertosconfig h. To lib/freertos/include
  5. Write src/main.c

After completion, the directory structure should be

├── include
│   └── README
├── lib
│   ├── FreeRTOS
│   │   ├── croutine.c
│   │   ├── event_groups.c
│   │   ├── heap_4.c
│   │   ├── include
│   │   │   ├── atomic.h
│   │   │   ├── croutine.h
│   │   │   ├── deprecated_definitions.h
│   │   │   ├── event_groups.h
│   │   │   ├── FreeRTOSConfig.h
│   │   │   ├── FreeRTOS.h
│   │   │   ├── list.h
│   │   │   ├── message_buffer.h
│   │   │   ├── mpu_prototypes.h
│   │   │   ├── mpu_wrappers.h
│   │   │   ├── portable.h
│   │   │   ├── projdefs.h
│   │   │   ├── queue.h
│   │   │   ├── semphr.h
│   │   │   ├── stack_macros.h
│   │   │   ├── StackMacros.h
│   │   │   ├── stdint.readme
│   │   │   ├── stream_buffer.h
│   │   │   ├── task.h
│   │   │   └── timers.h
│   │   ├── list.c
│   │   ├── mpu_wrappers.c
│   │   ├── port.c
│   │   ├── portmacro.h
│   │   ├── queue.c
│   │   ├── stream_buffer.c
│   │   ├── tasks.c
│   │   └── timers.c
│   └── README
├── platformio.ini
├── README.md
├── src
│   └── main.c
└── test
    └── README

Scheme 2: complete FreeRTOS, using library.json

  1. In platformio, select bluepill f103c8 for board and libopencm3 for framework to create the project
  2. Create a new directory FreeRTOS under lib of the project
  3. Unzip the latest FreeRTOS
  4. Copy all files in the freertos/source/ directory to lib/freertos
  5. Copy freertosconfig h. To lib/freertos
  6. Add library.json under lib/freertos
  7. Write src/main.c

library. JSON content is as follows. It only contains the required C files. UseflagsInclude additional include paths,issue #4148

{
    "name": "FreeRTOS",
    "version": "10.4.6",
    "build": {
        "flags": [
            "-Iportable/GCC/ARM_CM3"
        ],
        "srcFilter": [
            "+",
            "+",
            "+"
        ]
    }
}

If it is a multi-core MCU, it needs to include MPU_ wrappers. c. No need for f103c8

"+",

After completion, the directory structure is

├── include
│   └── README
├── lib
│   ├── FreeRTOS
│   │   ├── croutine.c
│   │   ├── event_groups.c
│   │   ├── FreeRTOSConfig.h
│   │   ├── include/
│   │   ├── library.json
│   │   ├── list.c
│   │   ├── miniprintf.c
│   │   ├── miniprintf.h
│   │   ├── portable/
│   │   ├── queue.c
│   │   ├── stream_buffer.c
│   │   ├── tasks.c
│   │   └── timers.c
│   └── README
├── platformio.ini
├── src
│   └── main.c
└── test

FreeRTOSConfig. H function name adaptation

In libopencm3/lib/cm3/vector In C,.Sv is defined_ call,. pend_ Processing functions of SV and.Systick,

/* Those are defined only on CM3 or CM4 */
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
	.memory_manage_fault = mem_manage_handler,
	.bus_fault = bus_fault_handler,
	.usage_fault = usage_fault_handler,
	.debug_monitor = debug_monitor_handler,
#endif

	.sv_call = sv_call_handler,
	.pend_sv = pend_sv_handler,
	.systick = sys_tick_handler,
	.irq = {
		IRQ_HANDLERS
	}
};

These function names are inconsistent with those in FreeRTOS and need to be associated Using macro replacement is more efficient than using function forwarding, so in freertosconfig The following definitions need to be added to h, otherwise FreeRTOS cannot work normally

/**
 * In libopencm3/lib/cm3/vector.c, these 3 handlers(right side) are for .sv_call,.pend_sv and .systick
 * These macro will rename the methods in port.c to make it work, more efficient than wrapped by method
*/
#define vPortSVCHandler sv_call_handler
#define xPortPendSVHandler pend_sv_handler
#define xPortSysTickHandler sys_tick_handler

Example code

UART sending and receiving using queue

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

#include 
#include 
#include 
#include #define mainECHO_TASK_PRIORITY                ( tskIDLE_PRIORITY + 1 )

static QueueHandle_t uart_txq;                // TX queue for UART

/*
 * Handler in case our application overflows the stack
 */
void vApplicationStackOverflowHook(
    TaskHandle_t xTask __attribute__((unused)),
    char *pcTaskName __attribute__((unused))) {

    for (;;);
}

void usart1_isr(void) {
    uint8_t data;
    /* Check if we were called because of RXNE. */
    if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) &&
        ((USART_SR(USART1) & USART_SR_RXNE) != 0)) {
        /* Retrieve the data from the peripheral. */
        data = usart_recv(USART1);
        xQueueSendFromISR(uart_txq, &data, NULL);
    }
}

static void gpio_setup(void) {
    // GPIO PB12,PC13:
    rcc_periph_clock_enable(RCC_GPIOB);
    rcc_periph_clock_enable(RCC_GPIOC);
    gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
    gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
    // Turn LED off
    gpio_set(GPIOB, GPIO12);
    gpio_set(GPIOC, GPIO13);

    rcc_periph_clock_enable(RCC_GPIOA);
    gpio_set_mode(GPIOA,GPIO_MODE_OUTPUT_50_MHZ,GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,GPIO_USART1_TX);
    gpio_set_mode(GPIOA,GPIO_MODE_OUTPUT_50_MHZ,GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,GPIO11);

    gpio_set_mode(GPIOA,GPIO_MODE_INPUT,GPIO_CNF_INPUT_FLOAT,GPIO_USART1_RX);
    gpio_set_mode(GPIOA,GPIO_MODE_INPUT,GPIO_CNF_INPUT_FLOAT,GPIO12);
}

/*********************************************************************
 * Configure and initialize USART1:
 *********************************************************************/
static void
uart_setup(void) {

    rcc_periph_clock_enable(RCC_USART1);

    usart_set_baudrate(USART1,115200);
    usart_set_databits(USART1,8);
    usart_set_stopbits(USART1,USART_STOPBITS_1);
    usart_set_mode(USART1,USART_MODE_TX_RX);
    usart_set_parity(USART1,USART_PARITY_NONE);
    usart_set_flow_control(USART1,USART_FLOWCONTROL_NONE);
    usart_enable(USART1);

    nvic_enable_irq(NVIC_USART1_IRQ);
    usart_enable_rx_interrupt(USART1);

    // Create a queue for data to transmit from UART
    uart_txq = xQueueCreate(256,sizeof(char));
}

static void uart_puts(const char *s) {
    for ( ; *s; ++s ) {
        // blocks when queue is full
        xQueueSend(uart_txq,s,portMAX_DELAY); 
    }
}

/*********************************************************************
 * USART Task: 
 *********************************************************************/
static void uart_task(void *args __attribute__((unused))) {
    char ch;

    for (;;) {
        // Receive char to be TX
        if ( xQueueReceive(uart_txq,&ch,500) == pdPASS ) {
            // if not tx data buffer empty
            while ( !usart_get_flag(USART1,USART_SR_TXE) )
                taskYIELD();    // Yield until ready
            usart_send(USART1,ch);
        }
        // Toggle LED to show signs of life
        gpio_toggle(GPIOB,GPIO12);
        gpio_toggle(GPIOC,GPIO13);
    }
}

/*********************************************************************
 * Demo Task:
 *    Simply queues up two line messages to be TX, one second
 *    apart.
 *********************************************************************/
static void demo_task(void *args __attribute__((unused))) {

    for (;;) {
        uart_puts("Now this is a message..\n\r");
        uart_puts(" sent via FreeRTOS queues.\n\n\r");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

/*********************************************************************
 * Main program & scheduler:
 *********************************************************************/
int main(void) {

    rcc_clock_setup_in_hse_8mhz_out_72mhz();    // CPU clock is 72 MHz

    gpio_setup();
    uart_setup();

    xTaskCreate(uart_task,"UART",100,NULL,configMAX_PRIORITIES-1,NULL);
    xTaskCreate(demo_task,"DEMO",100,NULL,configMAX_PRIORITIES-2,NULL);

    vTaskStartScheduler();
    for (;;);
    return 0;
}

FreeRTOS data

introduce

  1. FreeRTOS author Richard Barry’s speech in 2013,Oil pipe address

FreeRTOS source code

If you need to know the source code of FreeRTOS, first set up the above environment in platformio. With the help of IDE environment, you can greatly improve the reading efficiency Then get familiar with the data structure and interface of FreeRTOS. It is recommended to see the following PDF

  1. Beginning STM32 – Developing with FreeRTOS, libopencm3 and GCCThis book is suitable for beginners, with pictures and detailed step descriptions, as well as some in-depth analysis
  2. Mastering the FreeRTOS Real Time Kernel – a Hands On Tutorial Guide
  3. FreeRTOS source code interpretation nrushThis document is based on v10.2.1 and Cortex-M3, which is close to the current FreeRTOS version. The source code is annotated and easy to understand