1. 开发环境准备
Unrealfeathers/hello_led
GD32F103VET6 Linux 开发和调试环境部署 | Unrealfeathers’ Blog
GitHub 仓库中有示例代码,Linux 下的开发环境搭建请参考我的另一篇文章。
2. 获取 FreeRTOS Kernel
FreeRTOS/FreeRTOS-Kernel
在项目根目录下的 lib 目录中创建 FreeRTOS 目录,用于存放 FreeRTOS Kernel 源代码,目录结构如下:
1 2 3 4 5 6 7 8 9
| hello_led/ ├── ...... ├── lib/ # 第三方库 / 厂商固件库 │ ├── CMSIS/ # ARM Cortex 核心文件及 GD32 启动代码/系统文件 │ ├── FreeRTOS/ # FreeRTOS Kernel 源代码 │ │ ├── include/ │ │ └── source/ │ └── GD32F10x_standard_peripheral/ # GD32 标准外设库 └── ......
|
include 目录下,拷贝 FreeRTOS Kernel 仓库 include 目录下所有的 .h 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| include/ ├── FreeRTOS.h ├── StackMacros.h ├── atomic.h ├── croutine.h ├── deprecated_definitions.h ├── event_groups.h ├── list.h ├── message_buffer.h ├── mpu_prototypes.h ├── mpu_syscall_numbers.h ├── mpu_wrappers.h ├── newlib-freertos.h ├── picolibc-freertos.h ├── portable.h ├── projdefs.h ├── queue.h ├── semphr.h ├── stack_macros.h ├── stream_buffer.h ├── task.h └── timers.h
|
source 目录下,拷贝 FreeRTOS Kernel 仓库根目录下所有的 .c 文件,以及 portable 目录下的对应文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| include/ ├── portable/ │ ├── GCC/ARM_CM3/ │ │ ├── port.c │ │ └── portmacro.h │ └── MemMang/ │ └── heap_4.c ├── croutine.c ├── event_groups.c ├── list.c ├── queue.c ├── stream_buffer.c ├── tasks.c └── timers.c
|
3. 准备 FreeRTOSConfig.h
FreeRTOS/FreeRTOS
可以从 FreeRTOS 源码的 FreeRTOS/Demo 目录找到相似型号的示例从中拷贝一份,例如:CORTEX_STM32F103_GCC_Rowley/。
我的示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| #ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H
#include "gd32f10x.h"
extern uint32_t SystemCoreClock;
#define configUSE_PREEMPTION 1 #define configUSE_TIME_SLICING 1 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ ( SystemCoreClock ) #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) #define configMAX_PRIORITIES ( 5 ) #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) #define configMAX_TASK_NAME_LEN ( 16 ) #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 48 * 1024 ) ) #define configCHECK_FOR_STACK_OVERFLOW 2
#define configUSE_TRACE_FACILITY 1
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
#define configUSE_MUTEXES 1 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configQUEUE_REGISTRY_SIZE 8
#define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
#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
#define configPRIO_BITS 4
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler
#endif
|
4. 映射系统中断
FreeRTOS 需要接管 Cortex-M3 的三个核心系统中断来实现任务上下文切换和系统时钟:SVC_Handler、PendSV_Handler 和 SysTick_Handler。
为了避免去修改启动汇编文件或标准库文件,最优雅的做法是在 FreeRTOSConfig.h 的末尾添加以下宏定义,将裸机的默认中断名强行映射到 FreeRTOS 的处理函数上:
1 2 3
| #define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler
|
如果裸机工程(通常在 gd32f10x_it.c 文件中)已经定义了这三个函数的空实现,必须将它们注释掉或删除,否则链接器会报重复定义的错误。
5. 编译测试
根据自己的目录结构更新 CMakeLists.txt。
在 main.c 中编写测试程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| #include "gd32f10x.h" #include "gd32f10x_rcu.h" #include "gd32f10x_gpio.h"
#include "FreeRTOS.h" #include "task.h"
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { while(1); }
void gpio_config() { rcu_periph_clock_enable(RCU_GPIOC);
gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13); }
void vTaskLedBlink(void *pvParameters) { while (1) { gpio_bit_reset(GPIOC, GPIO_PIN_13); vTaskDelay(pdMS_TO_TICKS(1000));
gpio_bit_set(GPIOC, GPIO_PIN_13); vTaskDelay(pdMS_TO_TICKS(1000)); } }
int main() { gpio_config();
xTaskCreate(vTaskLedBlink, "LED_BLINK", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
vTaskStartScheduler();
while(1); }
|
按下 F5 编译、烧录后,查看 LED 闪烁是否正常。