• PlatformIO 创建 libopencm3 + FreeRTOS 项目


    PlatformIO: libopencm3 + FreeRTOS

    以下步骤基于常见的 Bluepill STM32F103C8T6, 也适用于其它 libopencm3 支持的MCU型号

    方案一: 只复制需要的文件

    1. 在 PlatformIO 中, Board 选择 Bluepill F103C8, Framework 选择 libopencm3, 创建项目
    2. 在项目的lib下新建目录 FreeRTOS
    3. 解压缩最新的 FreeRTOS
      1. 复制 FreeRTOS/Source/ 目录下, 除 portable 目录以外其它全部文件和目录, 至 lib/FreeRTOS 下
      2. 复制 FreeRTOS/Source/portable/GCC/ARM_CM3 目录下所有文件(port.c, portmacro.h), 至 lib/FreeRTOS 下
      3. 复制 FreeRTOS/Source/portable/Common 目录下所有文件(mpu_wrappers.c), 至 lib/FreeRTOS 下
      4. 复制 FreeRTOS/Source/portable/MemMang 目录下 heap_4.c, 至 lib/FreeRTOS 下
    4. 复制 FreeRTOSConfig.h, 至 lib/FreeRTOS/include 下
    5. 编写 src/main.c

    完成后目录结构应当为

    ├── 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
    

    方案二: 完整的FreeRTOS, 使用library.json

    1. 在 PlatformIO 中, Board 选择 Bluepill F103C8, Framework 选择 libopencm3, 创建项目
    2. 在项目的lib下新建目录 FreeRTOS
    3. 解压缩最新的 FreeRTOS
    4. 复制 FreeRTOS/Source/ 目录下所有文件至 lib/FreeRTOS 下
    5. 复制 FreeRTOSConfig.h, 至 lib/FreeRTOS 下
    6. lib/FreeRTOS 下添加 library.json
    7. 编写 src/main.c

    library.json 内容如下, 只包含需要的 c 文件, 用 flags 包含额外的include路径, issue #4148

    {
        "name": "FreeRTOS",
        "version": "10.4.6",
        "build": {
            "flags": [
                "-Iportable/GCC/ARM_CM3"
            ],
            "srcFilter": [
                "+<*.c>",
                "+<portable/GCC/ARM_CM3/port.c>",
                "+<portable/MemMang/heap_4.c>"
            ]
        }
    }
    

    如果是多核MCU, 需要再包含 mpu_wrappers.c, 对于 F103C8 就不需要了

    "+<portable/Common/mpu_wrappers.c>",
    

    完成后目录结构为

    ├── 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 函数名适配

    在 libopencm3/lib/cm3/vector.c 中, 定义了 .sv_call,.pend_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
    	}
    };
    

    这些函数名与 FreeRTOS 中的函数名不一致, 需要将其关联. 使用宏替换的方式比使用函数转发方式效率更高, 所以在 FreeRTOSConfig.h 中需要增加如下定义, 否则 FreeRTOS 不能正常工作

    /**
     * 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
    

    示例代码

    使用Queue的UART收发

    #include "FreeRTOS.h"
    #include "task.h"
    #include "queue.h"
    
    #include <libopencm3/stm32/rcc.h>
    #include <libopencm3/stm32/gpio.h>
    #include <libopencm3/stm32/usart.h>
    #include <libopencm3/cm3/nvic.h>
    
    #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 资料

    介绍

    1. FreeRTOS 作者 Richard Barry 2013年的演讲, 油管地址

    FreeRTOS 源码

    如果需要了解 FreeRTOS 源码, 首先在 PlatformIO 里把上面的环境搭好, 借助 IDE 的环境能极大提升阅读效率. 然后先熟悉一下 FreeRTOS 的数据结构, 提供的接口, 推荐看以下PDF

    1. Beginning STM32 - Developing with FreeRTOS, libopencm3 and GCC 这本书适合初学者, 有配图和详细的步骤说明, 也有一些深入的分析.
    2. Mastering the FreeRTOS Real Time Kernel - a Hands On Tutorial Guide
    3. FreeRTOS 源码解读 Nrush 这个文档基于V10.2.1和cortex-m3, 接近当前的FreeRTOS版本, 源码标注型解读, 清晰易懂.
  • 相关阅读:
    Hadoop(HDFS)
    Linux界的老古董
    Gin-swaggo为gin框架提供Swagger 文档
    解决SpringBoot 中Controller层加入RequestMapping导致HTML页面的静态文件路径变化问题
    Centos7环境下搭建Rabbitmq 集群
    NetApp与VMware和AWS合作,帮助客户实现云端企业工作负载的现代化和扩展
    R语言 pca主成分分析的主要方法
    3.字符串
    《算法通关村第一关——链表青铜挑战笔记》
    Perl区分文件换行符类型
  • 原文地址:https://www.cnblogs.com/milton/p/15888016.html