ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

FreeRTOS移植到GD32实战指南:从STM32迁移到国产芯片的完整教程

FreeRTOS移植到GD32实战指南:从STM32迁移到国产芯片的完整教程 1. 为什么要把FreeRTOS搬到GD32上搞嵌入式的人多少都动过这个念头STM32用腻了想换国产芯片试试GD32是个绕不开的选择。引脚兼容、价格友好、供货稳定这几个理由随便拎一个出来都够让人心动了。但问题来了——原来在STM32上跑得好好的FreeRTOS工程直接烧到GD32上大概率跑不起来HardFault跟吃饭一样频繁。这不是FreeRTOS的锅也不是GD32不行而是两者之间隔着一层“移植”的窗户纸。FreeRTOS本身设计得足够抽象内核代码跟硬件相关的部分被压缩到了极小的范围内主要就是端口层portable layer和内存堆管理这两块。GD32虽然内核也是Cortex-M系列但启动流程、时钟树、中断向量表偏移这些底层细节跟STM32有差异不把这些对齐系统调度器一启动就翻车。这篇内容适合两类人看一类是刚接触RTOS、想在GD32上跑个多任务练手的初学者另一类是从STM32转GD32、手里有现成FreeRTOS工程需要迁移的开发者。我会从零开始把整个移植流程拆开揉碎包括工程模板搭建、内核文件裁剪、端口层适配、堆栈配置、中断优先级处理以及调试过程中那些让人抓狂的坑。每一步都给出具体操作和背后的原因你照着做就能跑通。注意本文基于Keil MDK环境芯片型号以GD32F303系列为例FreeRTOS版本为V10.4.6。其他GD32型号和FreeRTOS版本思路一致细节参数需要对照手册调整。2. 移植前的整体思路与方案选型2.1 移植到底在移什么很多人一听到“移植”两个字就觉得是个大工程其实FreeRTOS的移植工作量远比想象中小。FreeRTOS的代码结构分三层内核核心层tasks.c、queue.c、list.c等、端口层portable/目录下跟编译器与架构相关的文件、配置文件FreeRTOSConfig.h。内核核心层是纯C代码跟硬件无关直接拿来用就行。真正需要动的只有端口层和配置文件。端口层里最关键的是port.c和portmacro.h这两个文件。port.c里面实现了任务上下文切换的汇编代码、启动第一个任务的函数、SysTick中断处理程序等。portmacro.h定义了跟架构相关的宏比如临界区进出、栈增长方向、数据类型宽度等。对于Cortex-M3内核的GD32F303来说FreeRTOS官方已经提供了portable/RVDS/ARM_CM3/这个端口理论上可以直接用。那为什么还会出问题因为GD32跟STM32在启动文件、系统时钟配置、中断向量表这几个地方有差异。启动文件决定了堆栈的初始位置和中断向量的入口地址系统时钟决定了SysTick的计数频率中断向量表决定了FreeRTOS的PendSV和SVC中断能不能被正确响应。这三样东西不对齐内核调度器就跑不起来。2.2 方案选型官方端口 vs 自己写有人可能会想既然GD32跟STM32都是Cortex-M3那我直接拿STM32的FreeRTOS工程改个芯片型号不就行了理论上可行但实际操作中会遇到几个问题。第一GD32的Flash等待周期跟STM32不同主频跑高了之后如果不调整等待周期取指会出错。第二GD32的中断优先级分组默认配置可能跟STM32不一样FreeRTOS对中断优先级有硬性要求配置错了会导致调度器异常。第三GD32的SysTick校准值寄存器可能跟STM32有细微差别影响时间片精度。所以我的建议是用FreeRTOS官方端口层但启动文件和时钟配置用GD32官方固件库提供的。这样既保证了内核调度的正确性又保证了底层硬件的适配性。具体来说FreeRTOS那边只需要port.c和portmacro.hGD32这边用官方固件库的启动文件startup_gd32f30x_hd.s和系统初始化代码system_gd32f30x.c。2.3 工程目录结构规划在动手之前先把工程目录结构规划好后面加文件的时候才不会乱。我习惯用这样的结构GD32_FreeRTOS_Project/ ├── CMSIS/ # 内核头文件 ├── GD32F30x_standard_peripheral/ # GD32固件库 ├── FreeRTOS/ │ ├── Source/ │ │ ├── include/ # 内核头文件 │ │ ├── portable/ │ │ │ └── RVDS/ │ │ │ └── ARM_CM3/ # 端口层文件 │ │ ├── tasks.c │ │ ├── queue.c │ │ ├── list.c │ │ ├── timers.c │ │ └── ... │ └── Config/ │ └── FreeRTOSConfig.h # 配置文件 ├── User/ │ ├── main.c │ ├── gd32f30x_it.c # 中断服务函数 │ └── ... └── Project/ └── GD32_FreeRTOS.uvprojx # Keil工程文件这个结构的好处是FreeRTOS的代码跟GD32的代码完全分离后面升级FreeRTOS版本或者换GD32型号的时候只需要替换对应的目录就行不会互相干扰。3. 手把手搭建GD32标准工程模板3.1 获取必要的文件先要把手头的“原材料”备齐。你需要这几样东西GD32F30x固件库从官网下载、FreeRTOS源码包从官网或GitHub获取、Keil MDK安装好并激活。固件库里我们主要用到的文件包括启动文件、系统初始化文件、外设驱动文件。FreeRTOS源码包里我们主要用Source目录下的内容。把GD32固件库解压后找到Firmware/CMSIS/GD/GD32F30x/Source/目录下的system_gd32f30x.c和startup_gd32f30x_hd.s这两个是启动相关的核心文件。再找到Firmware/GD32F30x_standard_peripheral/目录这里面是外设驱动我们至少需要Source/gd32f30x_gpio.c、gd32f30x_rcu.c、gd32f30x_usart.c这几个其他的按需添加。FreeRTOS源码包解压后Source目录下有一堆.c文件我们不需要全部加到工程里。必须添加的是tasks.c、queue.c、list.c、timers.c、event_groups.c、stream_buffer.c、croutine.c。portable目录下只需要RVDS/ARM_CM3/port.c内存管理选择MemMang/heap_4.c。头文件目录include整个加进去。3.2 在Keil中创建工程打开Keil MDK新建一个工程选择芯片型号。这里要注意如果你的Keil器件库里有GD32的包就直接选没有的话选一个Cortex-M3内核的通用型号也行比如STM32F103因为编译器和内核是一样的。但下载算法要选对不然烧录会失败。GD32F303的Flash算法在Keil的器件支持包里通常有如果没有就去GD官网下载Keil支持包安装。工程建好后按照前面规划的目录结构添加分组。在Keil里右键Target选择Manage Project Items创建这几个GroupCMSIS、GD32_Lib、FreeRTOS_Core、FreeRTOS_Port、User。然后把对应的文件添加到各个分组里。添加文件的时候有个细节要注意startup_gd32f30x_hd.s这个启动文件要放在CMSIS分组或者单独一个Startup分组里它是整个工程的入口不能漏掉。system_gd32f30x.c也放在CMSIS分组。GD32的外设驱动文件放在GD32_Lib分组。FreeRTOS的内核文件放在FreeRTOS_Core分组port.c和heap_4.c放在FreeRTOS_Port分组。main.c和中断文件放在User分组。3.3 配置头文件搜索路径文件加完了还不够编译器得知道去哪里找头文件。打开Options for Target切换到C/C选项卡在Include Paths里添加这些路径../CMSIS内核头文件路径../GD32F30x_standard_peripheral/Include外设驱动头文件路径../FreeRTOS/Source/includeFreeRTOS内核头文件路径../FreeRTOS/Source/portable/RVDS/ARM_CM3端口层头文件路径../FreeRTOS/Config配置文件路径../User用户代码路径路径添加的顺序有讲究FreeRTOS的include目录要放在GD32的include目录前面因为有些头文件名字可能冲突比如gd32f30x.h和FreeRTOS的某些头文件都定义了类似的数据类型顺序不对会报重复定义。3.4 添加全局宏定义在C/C选项卡的Define框里添加这几个宏GD32F30x_HD, USE_STDPERIPH_DRIVERGD32F30x_HD告诉固件库当前用的是大容量型号USE_STDPERIPH_DRIVER启用标准外设驱动。这两个宏不加的话编译会报找不到寄存器定义或者函数声明。实操心得如果你用的是GD32F303C8T6这种64KB Flash的型号宏要改成GD32F30x_MD。型号容量等级搞错了链接的时候会报Flash溢出或者启动文件里的堆栈大小不对。4. FreeRTOS内核文件裁剪与配置4.1 哪些文件必须加哪些可以砍FreeRTOS的源码文件不少但并不是每个工程都需要全部加进去。加多了浪费Flash空间加少了功能不够用。我一般按这个原则来裁剪必须加的文件tasks.c任务调度核心、queue.c队列和信号量、list.c内核链表、timers.c软件定时器、port.c端口层、heap_4.c内存管理。按需加的文件event_groups.c事件组不用可以不加、stream_buffer.c流缓冲区不用可以不加、croutine.c协程基本没人用了可以不加。对于大多数项目来说前六个文件就够了。heap_4.c支持内存碎片合并比heap_2.c更实用比heap_5.c更简单是性价比最高的选择。4.2 FreeRTOSConfig.h的逐项配置FreeRTOSConfig.h是整个移植过程中最需要花心思的文件每一项配置都直接影响系统的行为和性能。这个文件不是从模板复制过来就完事得根据GD32的实际参数来算。先看跟时钟相关的配置。configCPU_CLOCK_HZ要填GD32的实际主频比如GD32F303跑120MHz这里就填120000000。configTICK_RATE_HZ是系统节拍频率一般填1000也就是1ms一个节拍。这两个值决定了SysTick的重装载值填错了会导致时间片长度不对。#define configCPU_CLOCK_HZ ( 120000000UL ) #define configTICK_RATE_HZ ( ( TickType_t ) 1000 )再看跟中断优先级相关的配置。Cortex-M3的中断优先级寄存器是8位的但GD32只实现了高4位所以优先级范围是0到15数值越小优先级越高。FreeRTOS要求configMAX_SYSCALL_INTERRUPT_PRIORITY以上的优先级不能调用FreeRTOS的API这个值一般设为5也就是优先级0到4的中断不受FreeRTOS管理优先级5到15的中断可以安全调用FreeRTOS的API。#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) )堆大小的配置也很关键。configTOTAL_HEAP_SIZE决定了FreeRTOS能管理的内存总量GD32F303有48KB SRAM我一般给FreeRTOS分10KB到20KB剩下的留给全局变量和栈。如果开了太多任务或者任务栈设得太大堆不够用会导致pvPortMalloc返回NULL任务创建失败。#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 15 * 1024 ) ) #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )configMINIMAL_STACK_SIZE是空闲任务的栈大小单位是字word128字就是512字节。这个值不能太小空闲任务里会做一些清理工作栈太小会溢出。4.3 钩子函数与调试选项FreeRTOS提供了一些钩子函数可以在特定事件发生时执行用户代码。最常用的两个是vApplicationIdleHook和vApplicationStackOverflowHook。前者在空闲任务里被调用可以用来做低功耗处理后者在检测到栈溢出时被调用可以用来打印出错的任务名。#define configUSE_IDLE_HOOK 1 #define configUSE_TICK_HOOK 0 #define configCHECK_FOR_STACK_OVERFLOW 2 #define configUSE_MALLOC_FAILED_HOOK 1configCHECK_FOR_STACK_OVERFLOW设为2表示使用更严格的栈溢出检测方法会在任务切换时检查栈指针是否越界。这个方法会增加一点上下文切换的开销但在调试阶段非常值得开。注意configASSERT宏一定要定义它能在参数错误时立刻触发断言比等到系统跑飞了再查要高效得多。定义方式如下#define configASSERT( x ) if( ( x ) 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }5. 端口层适配与中断处理5.1 port.c里的关键函数port.c是FreeRTOS跟硬件之间的桥梁里面有几个函数是移植的核心。pxPortInitialiseStack负责初始化任务的栈帧让新任务第一次被调度时能正确进入任务函数。xPortStartScheduler启动调度器配置SysTick和PendSV中断然后触发SVC异常启动第一个任务。vPortSVCHandler、xPortPendSVHandler、xPortSysTickHandler这三个是中断服务函数分别处理SVC异常、PendSV异常和SysTick中断。对于Cortex-M3来说这三个中断处理函数的实现是标准的汇编代码FreeRTOS官方已经写好了直接用就行。但要注意的是这三个函数的名称必须跟启动文件里的中断向量表对应上。GD32的启动文件里SVC中断的入口是SVC_HandlerPendSV是PendSV_HandlerSysTick是SysTick_Handler。而FreeRTOS的port.c里定义的函数名是vPortSVCHandler、xPortPendSVHandler、xPortSysTickHandler。名字对不上中断就进不去。解决办法有两种一种是在FreeRTOSConfig.h里用宏定义把名字映射过去#define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler另一种是在gd32f30x_it.c里把这三个中断处理函数删掉让FreeRTOS的端口层直接接管。我推荐第一种方法改动最小不容易出错。5.2 中断优先级配置的坑GD32的中断优先级配置跟STM32有一个很大的不同点GD32的优先级分组默认是全部4位都用于抢占优先级没有子优先级。而STM32的默认分组是2位抢占优先级加2位子优先级。这个差异会导致FreeRTOS的中断优先级判断出错。FreeRTOS的configMAX_SYSCALL_INTERRUPT_PRIORITY是基于抢占优先级的如果GD32的优先级分组不对实际生效的抢占优先级可能跟预期不符。解决办法是在系统初始化的时候调用nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0)把优先级分组设为4位抢占优先级、0位子优先级。nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);这一句要放在main函数的最开始在创建任何任务之前调用。忘了加这一句后面中断嵌套的时候会出现莫名其妙的问题比如串口接收中断里调用xQueueSendFromISR导致系统卡死。5.3 SysTick配置的细节FreeRTOS用SysTick来产生系统节拍port.c里的vPortSetupTimerInterrupt函数负责配置SysTick。它根据configCPU_CLOCK_HZ和configTICK_RATE_HZ计算重装载值然后启动SysTick定时器。对于GD32F303跑120MHz、节拍1000Hz的情况重装载值是120000-1。这里有一个容易忽略的点GD32的SysTick时钟源可以是HCLK的8分频或者HCLK直接输入。FreeRTOS默认用的是HCLK直接输入也就是120MHz。如果你在system_gd32f30x.c里把SysTick时钟源改成了8分频那实际频率就变成了15MHz节拍时间会变成8ms所有跟时间相关的操作都会慢8倍。实操心得检查SysTick时钟源的方法很简单在vPortSetupTimerInterrupt函数里打个断点看看SysTick-LOAD的值是不是120000-1。如果是15000-1说明时钟源被改了需要改回来。6. 内存管理方案的选择与堆栈计算6.1 heap_1到heap_5怎么选FreeRTOS提供了5种内存管理方案从heap_1.c到heap_5.c各有各的适用场景。heap_1.c最简单只支持分配不支持释放适合那些任务和队列在系统启动时就全部创建好、运行过程中不再动态创建的项目。heap_2.c支持释放但不支持碎片合并长时间运行后内存会碎片化。heap_4.c支持释放和碎片合并是最常用的方案。heap_5.c在heap_4.c的基础上支持多个不连续的内存区域适合SRAM分块的芯片。对于GD32F303这种单块SRAM的芯片heap_4.c是最佳选择。它用一个链表来管理空闲内存块分配时找最合适的块释放时把相邻的空闲块合并。虽然合并操作会增加一点开销但避免了碎片化导致的内存分配失败。6.2 任务栈大小的计算方法任务栈大小是移植过程中最容易设错的地方。设小了栈溢出设大了浪费内存。栈的大小取决于任务里调用了哪些函数、有多少局部变量、有多少层函数嵌套。一个粗略的估算方法是先给一个较大的值比如512字然后在调试的时候查看栈的使用情况。FreeRTOS提供了uxTaskGetStackHighWaterMark函数返回任务运行过程中栈剩余的最小值。如果这个值接近0说明栈快溢出了需要加大如果这个值很大说明栈设大了可以适当减小。UBaseType_t watermark uxTaskGetStackHighWaterMark(NULL); printf(Stack watermark: %u\n, watermark);对于大多数任务来说256字1KB的栈够用了。如果任务里用了printf、浮点运算、或者大的局部数组栈要加到512字甚至1024字。printf函数特别吃栈因为它内部有很多层函数调用和缓冲区。6.3 堆空间不足的排查configTOTAL_HEAP_SIZE设小了创建任务或队列的时候会返回失败。xTaskCreate返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORYxQueueCreate返回NULL。排查方法是调用xPortGetFreeHeapSize查看剩余堆空间或者调用xPortGetMinimumEverFreeHeapSize查看历史最小剩余堆空间。size_t free_heap xPortGetFreeHeapSize(); size_t min_free_heap xPortGetMinimumEverFreeHeapSize(); printf(Free heap: %u, Min free heap: %u\n, free_heap, min_free_heap);如果min_free_heap接近0说明堆曾经几乎耗尽需要加大configTOTAL_HEAP_SIZE。GD32F303有48KB SRAM一般给FreeRTOS分15KB到20KB剩下的给全局变量和主栈。7. 常见问题与排查技巧实录7.1 系统启动就进HardFault这是移植后最常见的问题表现是一上电就卡在HardFault_Handler里出不来。原因通常有这几个中断向量表偏移不对、栈指针初始化错误、时钟配置错误。排查步骤首先检查system_gd32f30x.c里的VECT_TAB_OFFSET是不是0x0如果不是说明中断向量表被偏移了FreeRTOS的中断处理函数找不到入口。其次检查启动文件里的堆栈大小Stack_Size和Heap_Size不能太小建议Stack_Size至少0x400Heap_Size至少0x200。最后检查系统时钟配置如果主频跟configCPU_CLOCK_HZ不一致SysTick中断频率会出错导致调度器异常。7.2 任务创建成功但调度器不切换任务创建返回pdPASS但只有第一个任务在跑其他任务得不到执行。这种情况通常是中断优先级配置有问题。检查configMAX_SYSCALL_INTERRUPT_PRIORITY的值跟实际中断优先级的关系。如果SysTick中断的优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITYSysTick中断里调用的FreeRTOS API会触发断言或者直接失败。GD32的SysTick中断优先级默认是0最高需要把它改成最低优先级15。在vPortSetupTimerInterrupt函数里FreeRTOS会自动设置SysTick和PendSV的优先级为最低但如果你的启动文件或者固件库在别的地方改了优先级就会覆盖掉FreeRTOS的设置。7.3 串口中断里调用FreeRTOS API导致死机在中断服务函数里调用xQueueSendFromISR、xSemaphoreGiveFromISR这些API时如果中断优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY会触发configASSERT失败系统卡死。解决办法是把串口中断的优先级设为5到15之间也就是不高于configMAX_SYSCALL_INTERRUPT_PRIORITY。nvic_irq_enable(USART0_IRQn, 6, 0);这里第一个参数是中断号第二个参数是抢占优先级第三个参数是子优先级。抢占优先级6大于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY5所以是安全的。7.4 栈溢出检测触发vApplicationStackOverflowHook被调用说明有任务的栈溢出了。排查方法是查看传入的任务名找到对应的任务然后加大它的栈。如果加大栈之后还是溢出说明任务里有递归调用或者大的局部数组需要优化代码。一个容易被忽略的点是printf函数在Keil的MicroLIB和标准库下的栈消耗差别很大。MicroLIB的printf栈消耗小标准库的printf栈消耗大。如果用了标准库的printf任务栈至少要512字。7.5 常见问题速查表现象可能原因排查方法解决方案上电进HardFault向量表偏移错误检查VECT_TAB_OFFSET设为0x0调度器不切换SysTick优先级过高查看SysTick-SHP设为最低优先级中断里调API死机中断优先级过高检查NVIC优先级设为5到15之间栈溢出任务栈太小查看HighWaterMark加大栈或优化代码堆分配失败堆空间不足查看FreeHeapSize加大configTOTAL_HEAP_SIZE时间片不准时钟频率不匹配查看SysTick-LOAD修正configCPU_CLOCK_HZ避坑技巧移植完成后先跑一个最简单的LED闪烁任务确认调度器正常工作。然后再逐步添加队列、信号量、软件定时器等功能每加一个功能就测试一次。这样出了问题容易定位不会一堆问题搅在一起。8. 调试与验证让系统真正跑起来8.1 用Keil的调试工具查看任务状态Keil MDK有一个很好用的功能在调试模式下可以查看FreeRTOS的任务列表。需要安装FreeRTOS的Keil插件或者手动在FreeRTOSConfig.h里开启configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS然后调用vTaskList函数打印任务状态。#define configUSE_TRACE_FACILITY 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 1在任务里调用vTaskList传入一个字符缓冲区就能得到所有任务的名称、状态、优先级、剩余栈空间等信息。这个输出对调试非常有用可以一眼看出哪个任务栈快满了、哪个任务一直在阻塞。char task_list[512]; vTaskList(task_list); printf(%s\n, task_list);8.2 用GPIO翻转测量任务执行时间在没有示波器的情况下可以用GPIO翻转来粗略测量任务的执行时间。在任务开始和结束的地方各翻转一次GPIO用逻辑分析仪或者示波器看脉冲宽度就是任务的执行时间。gpio_bit_set(GPIOB, GPIO_PIN_0); // 任务代码 gpio_bit_reset(GPIOB, GPIO_PIN_0);这个方法虽然粗糙但对于判断任务是否超时、中断是否过于频繁已经够用了。GD32的GPIO翻转速度很快在120MHz主频下翻转一次只需要几十纳秒对任务执行时间的影响可以忽略。8.3 系统稳定性测试移植完成后不要急着上业务代码先跑一段时间的稳定性测试。创建一个高优先级任务和一个低优先级任务高优先级任务每隔100ms翻转一次LED低优先级任务每隔1s通过串口打印一条消息。让系统连续跑24小时观察是否出现死机、重启、任务卡死等问题。如果24小时没问题再把串口中断、定时器中断、ADC中断这些外设中断加进来测试中断跟任务之间的交互。中断里用FromISR版本的API发送消息给任务任务收到消息后处理。这个过程中要特别注意中断优先级的配置确保所有调用FreeRTOS API的中断优先级都不高于configMAX_SYSCALL_INTERRUPT_PRIORITY。实操心得我在实际项目中遇到过一个问题系统跑几个小时就死机查了很久才发现是串口中断的优先级设成了4高于configMAX_SYSCALL_INTERRUPT_PRIORITY导致中断里调用xQueueSendFromISR时偶尔触发断言。把优先级改成6之后就再也没出现过。这种问题在短时间测试中很难发现一定要跑长时间稳定性测试。8.4 从GD32F303迁移到其他GD32型号如果你后面要换GD32F103或者GD32F450移植的思路是一样的但有几个地方需要调整。启动文件要换成对应型号的system_gd32f30x.c要换成对应系列的FreeRTOSConfig.h里的configCPU_CLOCK_HZ要改成新型号的主频configPRIO_BITS要确认新型号的中断优先级位数。GD32F103的中断优先级也是4位跟F303一样。GD32F450的中断优先级是4位但主频可以跑到200MHzconfigCPU_CLOCK_HZ要改成200000000。另外F450的SRAM更大configTOTAL_HEAP_SIZE可以适当加大。移植完成后同样要先跑LED闪烁测试确认调度器正常然后再逐步添加功能。不要一次性把所有功能都加进去出了问题不好定位。8.5 关于FreeRTOS版本的选择FreeRTOS的版本更新比较频繁V10.x和V11.x在API上有一些差异。V10.4.6是一个比较稳定的版本网上资料多遇到问题容易找到答案。V11.x引入了一些新的API和配置项但跟旧版本的兼容性做得好大部分V10的代码可以直接在V11上编译。如果你用的是Keil MDK建议用V10.4.6因为Keil的FreeRTOS插件对V10的支持最好。如果你用的是VSCode加EIDE插件V11.x也没问题EIDE对FreeRTOS的支持比较灵活可以自己指定源码路径。我个人在实际操作中的体会是移植FreeRTOS到GD32这件事难点不在FreeRTOS本身而在GD32的底层配置。启动文件、时钟树、中断优先级这三样东西对齐了FreeRTOS跑起来就是水到渠成的事。反过来如果这三样里有一样不对就会出各种莫名其妙的问题查起来非常痛苦。所以我的建议是在移植之前先把GD32的标准工程模板搭好确认LED能闪、串口能打印然后再把FreeRTOS加进来。这样出问题的时候你可以确定是FreeRTOS的问题还是GD32底层的问题排查范围缩小一半。
返回列表