PlatformIO: libopencm3 + FreeRTOS
以下步骤基于常见的 Bluepill STM32F103C8T6, 也适用于其它 libopencm3 支持的MCU型号
方案一: 只复制需要的文件
- 在 PlatformIO 中, Board 选择 Bluepill F103C8, Framework 选择 libopencm3, 创建项目
- 在项目的lib下新建目录 FreeRTOS
- 解压缩最新的 FreeRTOS
- 复制 FreeRTOS/Source/ 目录下, 除 portable 目录以外其它全部文件和目录, 至 lib/FreeRTOS 下
- 复制 FreeRTOS/Source/portable/GCC/ARM_CM3 目录下所有文件(port.c, portmacro.h), 至 lib/FreeRTOS 下
- 复制 FreeRTOS/Source/portable/Common 目录下所有文件(mpu_wrappers.c), 至 lib/FreeRTOS 下
- 复制 FreeRTOS/Source/portable/MemMang 目录下 heap_4.c, 至 lib/FreeRTOS 下
- 复制 FreeRTOSConfig.h, 至 lib/FreeRTOS/include 下
- 编写 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
- 在 PlatformIO 中, Board 选择 Bluepill F103C8, Framework 选择 libopencm3, 创建项目
- 在项目的lib下新建目录 FreeRTOS
- 解压缩最新的 FreeRTOS
- 复制 FreeRTOS/Source/ 目录下所有文件至 lib/FreeRTOS 下
- 复制 FreeRTOS/Source/portable/GCC/ARM_CM3 目录下 portmacro.h 至 lib/FreeRTOS 下, 因为 library.json 还不支持多个 include 路径
- 复制 FreeRTOSConfig.h, 至 lib/FreeRTOS 下
- lib/FreeRTOS 下添加 library.json
- 编写 src/main.c
library.json 内容如下, 只包含需要的 c 文件
{
"name": "FreeRTOS",
"version": "10.4.6",
"build": {
"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
│ │ ├── portmacro.h
│ │ ├── queue.c
│ │ ├── stream_buffer.c
│ │ ├── tasks.c
│ │ └── timers.c
│ └── README
├── platformio.ini
├── README.md
├── src
│ └── main.c
└── test
└── README
FreeRTOSConfig.h 函数名适配
在 libopencm3/lib/cm3/vector.c 中, 定义了 .sv_call,.pend_sv and .systick 的处理函数,
#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 不能正常工作
#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;
void vApplicationStackOverflowHook(
TaskHandle_t xTask __attribute__((unused)),
char *pcTaskName __attribute__((unused))) {
for (;;);
}
void usart1_isr(void) {
uint8_t data;
if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) &&
((USART_SR(USART1) & USART_SR_RXNE) != 0)) {
data = usart_recv(USART1);
xQueueSendFromISR(uart_txq, &data, NULL);
}
}
static void gpio_setup(void) {
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);
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);
}
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);
uart_txq = xQueueCreate(256,sizeof(char));
}
static void uart_puts(const char *s) {
for ( ; *s; ++s ) {
xQueueSend(uart_txq,s,portMAX_DELAY);
}
}
static void uart_task(void *args __attribute__((unused))) {
char ch;
for (;;) {
if ( xQueueReceive(uart_txq,&ch,500) == pdPASS ) {
while ( !usart_get_flag(USART1,USART_SR_TXE) )
taskYIELD();
usart_send(USART1,ch);
}
gpio_toggle(GPIOB,GPIO12);
gpio_toggle(GPIOC,GPIO13);
}
}
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));
}
}
int main(void) {
rcc_clock_setup_in_hse_8mhz_out_72mhz();
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;
}
|