ARTICLE DETAIL

资讯详情

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

GD32H759I-EVAL上RT-Thread BSP移植到Keil5的完整实践指南

GD32H759I-EVAL上RT-Thread BSP移植到Keil5的完整实践指南 如果你正在用GD32H759I-EVAL开发板做RT-Thread的BSP移植并且目标IDE是Keil5那这篇文章大概率是你需要的。我这次从RT-Thread官方仓库拉取BSP到最终在Keil5里编译、下载、跑通串口控制台整个过程踩了不少坑——不是代码逻辑多难而是工具链配合和board目录下的配置细节太磨人。这篇文章我会按时间线把整个流程拆开把每一步会遇到的报错现象、排查思路和最终解决方案都写清楚适合正准备上手的同学直接照做也适合已经卡在某一步的兄弟对照排查。1. 为什么选GD32H759I-EVAL这块板子做RT-Thread移植1.1 这块硬件到底强在哪GD32H759I-EVAL是兆易创新GD32H7系列里非常典型的一块评估板。对于做嵌入式系统移植的人来说这块板子的意义在于它把“MCU能用到的高性能外设”基本都集成全了Cortex-M7内核带来的算力提升、大容量的片上Flash和SRAM、FMC可扩展外部SDRAM、多路USART/SPI/I2C还带以太网、USB、LCD等扩展接口。对于RT-Thread这种组件丰富的RTOS来说硬件资源越全越能体现BSP的价值——你可以在这个基础上验证文件系统、图形组件、网络协议栈等一堆东西。我当时选这块板子说白了就是看中它作为BSP移植对象的“典型性”。你要是能把GD32H759的BSP环境从零捋顺后面切换到同系列其他型号会省很多力气因为GD32H7系列的外设框架是高度统一的。而且和ST的同级产品相比GD32H7的性价比更突出很多项目选型都往这边靠提前把BSP这套玩法跑通对后续实际项目的评估也很有帮助。1.2 为什么直接基于RT-Thread官方BSP而不是自己从零写这里有个路线选择问题移植RT-Thread到一块新板子通常有两条路——第一从零开始自己搭启动文件、时钟树、板级初始化第二站在官方BSP的基础上裁剪和适配。我强烈建议你先看官方仓库里有没有对应型号的BSP哪怕只是相近型号的BSP也比你从零开始写要快得多。GD32H759I-EVAL在RT-Thread官方仓库的bsp目录下是有现成支持的我拿到手的版本已经包含了基础的启动文件、时钟初始化、串口驱动、GPIO驱动这些核心内容。直接基于它来改最大的好处是省掉了最痛苦的部分Cortex-M7的启动流程、GD32独有的时钟树配置、Keil分散加载文件怎么写。这些内容看起来简单但真自己写的时候会发现每一步都和硬件绑定得很死网上资料又少很难一次搞定。当然官方BSP不是拿来就能用的它默认的工程配置、宏开关、驱动代码版本和你本地环境之间多多少少会有差异。这正是后面几章要展开讲的内容。简单说你要抱着“官方代码只是起点”的心态来做而不是把官方BSP当成一个黑盒子。1.3 移植前需要确认的几个硬件细节在动手之前有几个硬件细节值得先确认一下这些细节会在后面某个环节突然冒出来影响你板载调试器是CMSIS-DAP还是别的方案决定了Keil5里Debugger选项怎么选默认调试串口是哪个USART引脚在板子上丝印是什么对应到数据手册的AF映射表是哪一个板载SDRAM的型号和容量这决定了后面如果要跑复杂应用内存布局怎么规划外部高速晶振是多少MHz因为有些RT-Thread BSP默认使用内部IRC倍频如果你的板子外部晶振和BSP配置不一致串口波特率可能会偏到完全无法通信。我在这次移植中一开始就忽略了板载调试器和外部晶振这两个点导致后面在Keil5下载阶段和串口输出阶段各卡了一次。这两个问题会在第4章和第5章详细讲这里先提个醒。2. 环境准备阶段最容易翻车的三件事仓库拉取、Pack安装与编译器版本2.1 用git clone而不是下载zip并初始化submodule很多同学拿到RT-Thread源码习惯性地在Gitee或GitHub页面点“Download ZIP”这个习惯在BSP移植场景下会给你埋雷。RT-Thread主仓库里有很多BSP是依赖子模块或者外部独立目录的如果直接下载zipbsp/gd32目录下的某些依赖文件可能不完整编译到一半出现找不到头文件的错误。我当时是用git拉的主仓库命令很简单git clone https://github.com/RT-Thread/rt-thread.git cd rt-thread git submodule update --init --recursive这样拉下来的仓库才是一个完整可用的状态。如果你已经下载了zip也别急去bsp/gd32目录下对照一下仓库线上文件列表把缺失的目录补齐尤其是libraries这种放固件库和驱动文件的地方。缺了它后面scons生成工程或者Keil编译时各种找不到文件、找不到符号的报错会让你怀疑人生。2.2 Keil5的芯片支持包必须装对否则设备列表直接没有这颗料Keil5和Keil4最大的区别之一就是MDK5把芯片支持从安装包内置变成了独立的Device Family PackDFP安装机制。也就是说你在Keil5的Device选择列表里能不能看到GD32H759取决于你有没有装对应的Pack。GD32系列在Keil5里对应的Pack名一般是GigaDevice.GD32H7xx_DFP之类的格式。装Pack有两个办法一个是打开Keil5后通过Pack Installer在线搜索安装另一个是从兆易创新官网或者Keil官方仓库下载.pack文件然后双击离线安装。在线安装慢的话建议用离线包省时间且版本可控。我当时遇到的报错是打开RT-Thread的Keil工程后弹窗提示“Device not found”或者编译时找不到芯片定义的头文件。排查下来就是Pack没装或者装了但版本太老。这里给个建议装Pack之前先看一眼你的Keil5 MDK版本如果版本太旧比如5.20以前的新出的GD32H7系列Pack可能根本装不上或者不识别这时候先升级MDK版本再装Pack别在旧版上死磕。2.3 ARM编译器版本默认AC5还是切AC6别一上来就纠结Keil5的ARMC编译器有AC5和AC6两代编译速度、C标准支持程度都不一样。RT-Thread近几个版本的BSP默认生成的Keil工程很多还在用AC5但新代码越来越依赖C99甚至更高版本标准的特性AC5编译时偶尔会冒出一些让人摸不着头脑的警告和错误。我的建议是直接用AC6。原因很简单AC6对现代C代码的支持更完整编译速度也更快最关键的是RT-Thread官方在持续跟进AC6的适配你遇到坑的概率比用AC5小。切换方法是在Keil5的Options for Target - Target - ARM Compiler里选“Use default compiler version 6”或者直接选V6.x版本。不过切换AC6之后要注意一个现象同一份代码AC5能编译过去AC6可能报一堆警告甚至错误主要集中在内联汇编语法、结构体零长度数组、隐式类型转换这些地方。遇到这种问题不要慌绝大多数是编译器语法兼容问题不是你的业务逻辑问题按编译提示修就行。真遇到RT-Thread源码里某个文件在AC6下报错可以去RT-Thread的issue区搜一下很多都已经有解决方案。3. 生成Keil工程与首次编译那些看似代码错误、实则是配置问题的报错3.1 用scons生成mdk5工程而不是手工新建RT-Thread的BSP工程推荐用scons工具链生成而不是在Keil里手工新建一个空工程然后手动添加源文件。手工新建的工程你大概率会漏掉rtconfig.h、链接脚本、启动文件的正确配置最后编译出来一堆奇怪的问题还很难排查。在BSP目录下通过RT-Thread官网提供的Env工具打开终端或者直接在命令行里进入bsp/gd32/arm/gd32h759i-eval目录执行scons --targetmdk5生成完毕后工程目录下会出现Keil5的project.uvprojx文件用Keil5打开即可。scons的作用不仅仅是生成工程它会把Kconfig配置生成rtconfig.h、把源码文件列表整理成工程结构这一步是RT-Thread的标准姿势能省掉后面大量源码管理的工作。如果你本地还没装scons和Env工具需要先去RT-Thread官网下载Env工具包或者用pip安装scons配合python环境使用。我个人建议直接用Env工具因为它自带了很多辅助脚本和配置工具后面做menuconfig配置时会方便很多。3.2 第一次编译报错的三种类型和对症下药我这边第一次编译报错大致可以归为三类每类的排查方向完全不同一类是缺少头文件报错大概率是“fatal error: xxx.h: No such file or directory”。这类问题多是路径问题去Options for Target - C/C - Include Paths里看有没有把必要的目录加进去。如果用的是scons生成的工程理论上不会缺路径除非你在后面的配置中删改过文件目录结构。二类是找不到芯片定义报错形如“cannot open source input file xxx.h”而且这个头文件明显是芯片厂商的固件库文件。这个就要回到第2.2节检查你的Device Pack是否安装以及工程里是否引用了正确的Device头文件路径。三类是编译通过但链接失败报错形如“undefined symbol SystemInit”或者“cannot find -lc”。这类问题通常和启动文件、链接脚本有关。SystemInit符号缺失说明启动文件里引用的系统初始化函数没有在源码中找到检查时钟初始化文件是否被正确加入工程链接不到C库多半是ARM Compiler版本切换后运行库配置变了去Linker页面确认勾选了“Use Memory Layout from Target Dialog”。我见过不少同学上来就死磕代码逻辑结果搞了半天是编译器路径或运行库配置问题。记住一个顺序先看工具链配置再看启动和链接最后才查代码逻辑。3.3 工程路径不能有中文和空格这个老坑一直没消失这个坑真的很老但真的还是很多人踩。Keil5的工程路径、源码路径如果包含中文字符编译阶段往往不会立刻报错但在链接阶段、调试阶段会突然出现各种诡异问题比如“cannot open file”或者下载后程序跑飞。我这次BSP移植一开始把仓库放在“D:\个人项目\RT-Thread\测试代码\”这种路径下面结果scons生成工程的时候就有警告编译到一半直接报文件路径解析错误。把所有目录名改成英文、去掉空格之后整个世界清净了。处理建议源码仓库就放在纯英文、无空格的路径下比如D:\rt-thread、C:\work\gd32h759_bsp这种。这也是嵌入式开发的底线习惯不只是RT-Thread和Keil很多编译工具链对中英文混排路径支持都不好早早养成习惯能避掉一堆无形的问题。4. 下载与串口输出CMSIS-DAP连接、Flash算法、乱码与无输出的完整排查4.1 下载失败三连Cannot access target、No Algorithm、Flash Download failed程序编译通过只是第一步真正开始和硬件打交道是从下载开始的。我这次遇到的第一类下载问题是“Cannot access Target”看起来像调试器连接不上芯片。排查链路是这样的先在Keil5的Options for Target - Debug页面把右侧的Use调试器选成你实际的调试器。GD32H759I-EVAL板载的调试器是CMSIS-DAP所以要选CMSIS-DAP Debugger而不是ST-Link或者J-Link。有些同学电脑上装了多个调试器驱动这里选错了或者驱动冲突就会出现连不上芯片的假象。如果这一步确认没问题接下来点旁边的Settings图标确认调试器已经被识别SW Device里能看到芯片ID。第二类问题是“No Algorithm found”或者下载时提示Flash算法缺失。这个问题的本质是Keil不知道用什么命令去擦写芯片内部的Flash。解决方案是在Flash Download页面添加对应的Flash算法点Add按钮选择GD32H7系列的内部Flash算法。如果你之前装了MDK的GigaDevice Pack这里应该能列出对应算法选对型号添加上去即可。第三类问题是“Flash Download failed - Target DLL has been cancelled”这个报错出现时往往前面几步都没问题。我遇到的原因是调试时钟速率太高加上板上有SDRAM等高速外设干扰CMSIS-DAP在高速下载时不稳定。解决方式是把Settings里Max Clock调到1MHz或500kHz下载速度虽然慢一点但稳定很多。另外如果你用了一根质量很差的USB线或者延长线也容易出现这种偶发失败换一根短线往往立竿见影。4.2 下载成功后串口无输出先排查硬件连接再看工程配置程序能下载但串口控制台一点输出都没有这个问题比下载失败更让人烦躁。我的排查顺序是这样第一步确认串口硬件接线正确。GD32H759I-EVAL这块板子上的调试串口一般会通过板载USB转串口芯片连到电脑你要确认用的是板上丝印标注的UART口而不是随便一个扩展引脚。杜邦线接错、USB转串口模块的TXD/RXD插反都会导致完全无输出。第二步检查串口终端工具的参数是否和BSP配置一致。RT-Thread BSP默认控制台波特率通常在rtconfig.h或board.h里定义常见的是115200 8N1。如果终端工具里设置的波特率不一样屏幕上大概率是乱码或者时有时无的字符。第三步确认BSP代码里默认的调试串口和实际板子的串口一致。RT-Thread的BSP在board.h里会有类似BSP_USING_UART0这样的宏定义对应的引脚映射需要和你的板子一致。如果官方BSP默认用的UART0但你实际连接的硬件串口是UART1那自然没有任何输出。这个问题的排查办法是查看板卡原理图确认调试串口接的是哪一个USART再回过来修改rtconfig.h和board.h里的配置。我这次的问题就比较奇葩BSP里定义的串口号和板子是对的波特率也是对的但就是没有输出。最后发现是板载调试器的驱动在Windows下被识别成了两个COM口我连的是错误的那个。在设备管理器里挨个试了一遍才找到真正的串口号。这个细节真的很容易被忽略强烈建议你下载完程序后先打开设备管理器确认串口枚举情况。4.3 串口乱码不一定是波特率问题可能是时钟配置和打印字符频率的锅乱码问题有两种常见原因。第一种是波特率不匹配这个大家都懂第二种是系统时钟配置不正确导致串口模块计算出的实际波特率和理论值偏差太大这种偏差在115200下可能表现为偶发乱码在更高波特率下几乎必现乱码。RT-Thread BSP默认的时钟配置在board.c或者驱动文件里如果你板卡的外部晶振频率和BSP默认值不一致串口输出就会乱。比如BSP代码假设外部高速晶振是25MHz你板子上实际是8MHz整个系统时钟就会跑偏串口模块拿到的外设时钟也偏了算出来的波特率自然不对。解决方式是确认板卡外部晶振频率然后在BSP的时钟配置文件里改掉对应宏。GD32H7系列的时钟树配置一般集中在board.c和驱动库的system_gd32h7xx.c里关键是找出外部晶振相关的定义比如EXT_OSC_VALUE之类改成你板子的真实频率然后重新编译下载。还有一个偏门原因如果板子上电后程序跑飞了、反复复位串口可能输出一串半截不半截的字符。这种情况要从程序稳定性去查比如D-Cache未初始化、SDRAM配置不对导致硬件异常而不是纠结串口配置。判断办法是看输出是否有规律完全乱码、无规律闪烁偏向时钟或硬件问题固定输出一串相同字符偏向程序复位循环。5. XIP、SDRAM与D-Cache让程序跑得更稳之前必须过的三关5.1 D-Cache与DMA的相爱相杀不解决就没有稳定通信GD32H7系列是Cortex-M7内核自带I-Cache和D-Cache这是这颗芯片性能优势的重要来源。但在RT-Thread这种RTOS环境下Cache的开启会引入一个非常经典的问题D-Cache和DMA之间的数据一致性。简单说DMA是内存和外设之间直接搬数据不经过CPU也不经过Cache但CPU读写内存时读会先查Cache写会先写Cache。如果DMA把外设数据搬到了内存而CPU读的时候Cache里存的还是旧数据就会读到脏数据反过来CPU写了数据到Cache但还没写回内存DMA去搬的时候搬走的是老数据。这就是典型的Cache一致性问题。RT-Thread官方BSP对GD32H7系列默认可能没有开启D-Cache但如果你为了性能在board初始化里打开了Cache那么所有使用DMA的外设驱动都要处理一致性。最简单稳妥的做法是在移植阶段先不打开D-Cache等系统跑稳了、业务逻辑调通了再考虑通过RT-Thread提供的cache接口去优化。别一上来就追求极致性能否则连最基础的串口收发都会因为在Cache和DMA之间反复横跳而出各种灵异问题。5.2 FMC外部SDRAM初始化失败会直接HardFaultGD32H759I-EVAL板载了外部SDRAM通过FMC接口访问。SDRAM对RT-Thread的意义在于它能扩出很大的内存空间用来跑GUI、文件系统、网络协议栈这类吃内存的应用。SDRAM配置的坑集中在时序参数上。FMC访问SDRAM时需要根据SDRAM芯片的数据手册配置合理的刷新周期、行激活延迟、CAS延时等参数。这些参数配错了SDRAM可能不报错但读取数据偶发错误或者初始化时直接进入HardFault异常。RT-Thread BSP通常在board.c里会提供SDRAM初始化的代码和内存堆声明你需要做的就是把SDRAM型号对应的参数核对一遍。如果官方BSP默认配置的SDRAM型号和你的板子一致这一步基本不用动如果不同就得改时序参数排查起来比较费劲最好用示波器或者逻辑分析仪辅助确认。就我个人的建议在BSP移植的前期阶段先不要急着把SDRAM纳入RT-Thread的内存堆管理先去验证SDRAM本身能正常读写再往系统里挂排查问题的难度会小很多。5.3 XIP从外部Flash执行代码不熟悉启动流程就别玩XIPExecute In Place是一种可以从外部Flash直接执行代码的工作模式在MCU上意味着代码不搬进SRAM直接在外部NOR Flash上运行。这个玩法在资源紧张、但又想跑大应用的项目里很有吸引力GD32H759的FMC也支持这种模式。但XIP的坑在于它牵动了启动流程、分散加载文件和Flash的等待状态配置。程序复位后CPU会从固定的地址取第一条指令如果0x08000000这个启动地址段没有对应到外部Flash的映射程序要么跑飞要么压根起不来。即便地址映射正确外部Flash的访问速度比内部Flash慢还需要配置好等待周期否则偶发取指错误会让系统不定期崩溃。我的建议是XIP留到BSP移植完全稳定之后再做当作进阶功能去挑战。前期老老实实从内部Flash启动把RT-Thread的线程调度、外设驱动、网络协议栈这些全都验证没问题了再研究XIP也不迟。否则一旦出问题你会发现启动流程、分散加载、Flash控制器三个层面同时出错根本没法定位。6. 移植完成后的一轮自查清单从拉取官方BSP到Keil5里跑通控制台整个过程走完一遍之后我建议你按下面这个清单过一遍能帮你确认这次移植是真的稳了而不只是能打印个Hello World用menuconfig重新配置一次BSP确认rtconfig.h能按你的选择正确生成这说明Kconfig配置链路是通的分别用调试模式下断点和全速运行两种方式确认程序稳定多按几次复位键看系统能不能每次都能正常启动而不只是第一次下载后能跑把串口波特率从115200改到460800或921600看输出是否稳定不乱码这能侧面验证时钟树是否正确跑一个创建多个线程的小Demo确认RT-Thread的调度器在你这个BSP上是真的正常工作而不是碰巧能打印打开D-Cache重跑一遍串口DMA收发确认Cache一致性已经处理到位。这些检查建议看着琐碎但任何一个环节出问题都代表你的BSP基础没打牢。基础不稳后续在上面跑文件系统、网络协议栈、GUI组件早晚会以某种诡异现象的形式还回来。我个人在这轮移植里最大的体会是BSP移植这件事70%的精力都耗在工具链配置、内存布局、Cache/时钟这类“非业务代码”上真正的应用代码反而改动很少。所以做这类工作时千万别急着改驱动逻辑先把工程环境、启动流程、内存划分这些基础设施捋顺后面的路才会越走越顺。
返回列表