ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

STM32、GD32、CH32V103国产替代实测:一年长跑数据与踩坑记录

STM32、GD32、CH32V103国产替代实测:一年长跑数据与踩坑记录 1. 从一块GD32换掉STM32说起我为什么要做这个长期测试2023年初我手上一个量产项目遇到了典型的供应链问题——原方案用的STM32F103C8T6交期拉长到16周以上采购那边天天催我确认替代方案。当时摆在面前的选择有几个加价从现货市场拿STM32、换GD32F103、或者干脆切到RISC-V架构的CH32V103。这不是一个拍脑袋就能决定的事因为板子已经量产了三批固件里跑着CAN通信、定时器捕获、ADC采样、串口Modbus协议栈任何一个环节出问题都意味着现场返工。我最后的做法是没有直接换而是同时做了三套验证板每套跑相同的业务逻辑放在同一个测试环境里连续跑了一年。这一年里我记录了GPIO翻转速度、ADC实际有效位数、CAN总线在强干扰下的误帧率、Flash擦写寿命、以及最关键的——出问题之后排查到底要花多少时间。这篇文章就是这一年的完整记录不吹不黑数据说话。如果你正在做国产替代选型或者你是个嵌入式新手想了解STM32、GD32、CH32V103这几款芯片到底有什么区别再或者你只是好奇“国产芯片到底能不能用”这篇内容应该能给你一些来自一线的真实参考。我会从实际测试数据、踩坑记录、工具链适配、以及长期运行稳定性几个维度展开尽量把每个结论背后的原因讲清楚。注意本文所有测试数据来自我个人实验室环境不同批次、不同厂家的芯片可能存在差异结论仅供参考实际选型请以你自己的验证为准。2. 三款芯片的硬指标对比纸面参数和实测差距有多大2.1 核心架构与主频的真实表现先看纸面参数。STM32F103C8T6是ARM Cortex-M3内核72MHz主频64KB Flash20KB SRAM。GD32F103C8T6同样是Cortex-M3但主频标称108MHzFlash和SRAM容量一致。CH32V103C8T6则是RISC-V内核沁恒自研的V3A架构主频80MHz64KB Flash20KB SRAM。纸面上GD32的主频最高但这里有个很多人忽略的细节GD32的Flash等待周期和STM32不同。STM32在72MHz下Flash需要2个等待周期而GD32在108MHz下需要3个等待周期。这意味着如果你直接把STM32的代码搬到GD32上不改等待周期配置跑起来可能看起来正常但在高频率下会出现偶发的取指错误——表现就是程序偶尔跑飞或者HardFault。我实测下来的结果指标STM32F103GD32F103CH32V103标称主频72MHz108MHz80MHz实测GPIO翻转最高频率18MHz25MHz20MHzCoreMark跑分1.0基准1.35倍0.85倍Flash等待周期(最高频)232中断响应延迟(实测)12周期12周期15周期GPIO翻转测试用的是最简单的寄存器操作不带任何库函数。STM32在72MHz下能跑到18MHz的翻转频率GD32在108MHz下能到25MHzCH32V103在80MHz下能到20MHz。这个数据说明GD32的主频优势是真实的但并没有标称的1.5倍那么夸张因为Flash等待周期吃掉了部分性能。CH32V103的CoreMark跑分大约是STM32的0.85倍这个差距在日常业务逻辑里基本感知不到但如果你做的是电机FOC控制或者高速PID运算这个差距就会体现在控制周期上。我试过用CH32V103跑一个10kHz的电流环CPU占用率比STM32高了大约12个百分点。2.2 外设一致性与隐藏差异这是国产替代里最容易踩坑的地方。很多人以为“Pin to Pin兼容”就是完全兼容实际上外设行为可能有细微差别。先说GD32。GD32F103的USART、SPI、I2C基本和STM32一致但有几个地方需要注意ADC的采样时间配置不同。STM32的ADC采样时间寄存器配置和GD32有差异如果你直接搬代码采样时间可能偏短导致采样值偏小。我实测同一个电位器分压电路STM32读出来是2048左右GD32直接跑STM32代码读出来只有1980左右改了采样时间后才对齐。CAN总线的位定时配置。GD32的CAN波特率计算和STM32略有不同特别是采样点位置。我用STM32的配置直接跑GD32在125kbps下通信正常但切到500kbps后误帧率明显上升后来用示波器抓了波形才发现采样点偏了。DMA请求映射。大部分通道一致但个别外设的DMA请求映射不同这个在参考手册里写得比较隐蔽需要仔细对比。CH32V103的差异更大一些因为它毕竟是RISC-V内核中断控制器和STM32完全不同。CH32V103用的是自己的一套中断管理机制中断向量表的结构和STM32不一样。如果你用STM32的标准库或者HAL库直接移植中断部分基本要重写。提示做国产替代时不要只看Pin to Pin一定要把外设行为差异列一个清单逐项验证。我当时的做法是写了一个外设自检固件把每个外设的典型用法都跑一遍对比输出结果。2.3 功耗表现的实测数据功耗这块我做了比较详细的测试因为很多项目对功耗有要求。测试条件是芯片跑在最高主频所有外设关闭只开一个定时器做唤醒用高精度电流表测量。工作模式STM32F103GD32F103CH32V103运行模式(72/108/80MHz)28mA35mA22mA睡眠模式12mA15mA9mA停止模式20uA25uA15uA待机模式2.5uA3.2uA1.8uAGD32的功耗明显高于STM32这跟它更高的主频和更大的驱动能力有关。CH32V103的功耗表现最好特别是在低功耗模式下待机电流只有1.8uA比STM32的2.5uA还低。如果你的项目是电池供电的CH32V103在功耗上有优势。但这里有个坑CH32V103的唤醒时间比STM32长。从待机模式唤醒到正常运行STM32大约需要50usCH32V103需要80us左右。如果你的应用需要频繁唤醒这个差距会累积成可观的功耗。3. 开发工具链的适配过程从Keil到VS Code再到PlatformIO3.1 Keil环境下的芯片包安装与配置Keil是STM32开发的老牌工具国产芯片对Keil的支持也最成熟。GD32和CH32V103都提供了Keil的芯片包Pack安装方式和STM32一样双击pack文件就行。但这里有几个细节需要注意GD32的Keil Pack版本要和芯片型号匹配。我一开始装了GD32F10x的通用Pack结果发现里面没有F103C8的具体型号编译出来的代码虽然能跑但Flash算法不对下载的时候经常报错。后来换了专门的GD32F103 Pack才正常。CH32V103的Keil支持需要额外安装RISC-V编译器。Keil本身是ARM的编译器要编译RISC-V代码需要安装沁恒提供的RISC-V GCC工具链并在Keil里配置好路径。这个过程比STM32麻烦一些但官方有详细的文档跟着做就行。调试器配置。STM32用ST-LinkGD32可以用ST-Link也可以用它自己的GD-LinkCH32V103用的是WCH-Link。我实测下来GD32用ST-Link下载和调试都没问题但CH32V103必须用WCH-LinkST-Link不支持。注意如果你用Keil同时开发STM32和CH32V103建议装两个不同版本的Keil或者用Keil的Pack Manager管理不同芯片包。我试过在同一个Keil里装STM32和CH32V103的Pack结果RISC-V编译器和ARM编译器冲突编译时报了一堆莫名其妙的错误。3.2 VS Code PlatformIO的跨平台方案Keil虽然成熟但界面老旧代码补全和调试体验一般。我后来把大部分开发工作迁移到了VS Code PlatformIO这套方案对STM32、GD32、CH32V103都支持。PlatformIO的配置很简单在platformio.ini里指定平台和板子就行[env:stm32f103] platform ststm32 board bluepill_f103c8 framework arduino [env:gd32f103] platform ststm32 board bluepill_f103c8 framework arduino upload_protocol stlink [env:ch32v103] platform ch32v board ch32v103c8t6 framework arduino upload_protocol wchlink这里有个关键点GD32在PlatformIO里可以直接用STM32的板子定义因为它们的编译工具链和下载协议兼容。但CH32V103需要单独的ch32v平台这个平台是社区维护的更新频率不如官方平台有时候会遇到编译错误。我遇到的一个典型问题是CH32V103的链接脚本。PlatformIO默认的链接脚本和沁恒官方的有差异导致中断向量表的位置不对程序跑不起来。后来我手动替换了link.ld文件把向量表基地址改成了0x00000000问题才解决。3.3 调试体验的差异调试是开发过程中很重要的一环。STM32的调试体验最好ST-Link配合Keil或者VS Code的Cortex-Debug插件断点、单步、变量查看都很流畅。GD32的调试体验接近STM32用ST-Link也能正常调试但偶尔会出现连接不稳定的情况。我遇到过一次GD32在调试模式下跑得好好的退出调试后程序就不跑了后来发现是调试器的复位配置问题改了launch.json里的复位方式才解决。CH32V103的调试体验相对差一些。WCH-Link的调试速度比ST-Link慢单步执行的时候有明显延迟。而且CH32V103的调试信息格式和ARM不同有些变量查看功能在VS Code里显示不正常。不过基本的断点和单步功能是没问题的日常开发够用。4. 实际项目中的踩坑记录那些文档里不会写的问题4.1 GD32的Flash等待周期导致的偶发死机这个问题困扰了我将近两周。现象是GD32跑一个串口收发程序大部分时间正常但偶尔会死机死机位置不固定有时候在串口中断里有时候在主循环里。排查过程一开始怀疑是串口中断优先级问题调整了优先级没用。怀疑是电源问题换了LDO加了滤波电容还是偶尔死机。用示波器抓电源纹波正常。最后用J-Link的RTT功能打印死机前的寄存器状态发现LR寄存器的值指向了一个非法地址。这时候我才想到Flash等待周期的问题。查了GD32的参考手册发现GD32F103在108MHz下需要3个Flash等待周期而STM32的默认配置是2个。我直接搬了STM32的代码等待周期配置是2导致GD32在高频下取指偶尔出错。改了等待周期配置后连续跑了72小时没再死机。这个问题的隐蔽性在于它不是每次都出错可能跑几个小时才出一次很容易被误判为硬件问题。提示从STM32移植到GD32时第一件事就是检查FLASH_ACR寄存器的等待周期配置。GD32的库函数里有一个SystemInit函数里面会根据主频自动配置等待周期但如果你用的是STM32的SystemInit就不会有这个配置。4.2 CH32V103的中断向量表重定位CH32V103的中断向量表默认在Flash的起始地址但如果你用了Bootloader就需要把向量表重定位到应用程序的起始地址。STM32的做法是设置SCB-VTOR寄存器CH32V103也有类似的机制但寄存器的地址和配置方式不同。我当时的项目需要做OTA升级Bootloader占用了前16KB的Flash应用程序从0x08004000开始。在STM32上只需要在应用程序开头加一行SCB-VTOR 0x08004000;但在CH32V103上这个寄存器不在SCB里而是在PFICProgrammable Fast Interrupt Controller里。正确的做法是PFIC-ISR[0] 0x08004000;这个差异在沁恒的参考手册里有写但位置比较隐蔽我翻了很久才找到。如果你不知道这个差异应用程序的中断会全部失效表现就是程序能跑但所有中断都不响应。4.3 CAN通信在强干扰环境下的稳定性对比CAN通信是我这个项目里的核心功能所以我对三款芯片的CAN稳定性做了专门测试。测试环境是一个电机控制柜里面有变频器和伺服驱动器电磁干扰比较强。测试方法三款芯片都配置成500kbps每10ms发送一帧数据连续跑24小时统计误帧率和丢帧率。芯片误帧率丢帧率备注STM32F1030.001%0.0005%偶尔需要重发GD32F1030.003%0.001%需要调整采样点CH32V1030.002%0.0008%表现稳定GD32的误帧率略高但调整了CAN位定时配置后降到了和STM32差不多的水平。CH32V103的表现出乎意料地好在强干扰环境下没有出现异常。这里的关键是CAN收发器的选型。我用的都是TJA1050但不同厂家的TJA1050性能有差异。建议用大厂的收发器不要在这上面省钱。5. 长期运行稳定性一年下来的故障统计5.1 三款芯片的故障率对比这一年的测试里我一共跑了30块板子每款芯片10块放在同一个环境里连续运行。故障定义是非人为因素导致的程序跑飞、死机、外设失效。芯片故障次数故障率主要故障类型STM32F103110%一次Flash写入失败GD32F103330%两次死机一次ADC异常CH32V103220%一次中断丢失一次串口异常GD32的故障率最高主要问题还是集中在Flash等待周期和ADC配置上。这两类问题在改了配置之后就没有再出现。CH32V103的故障率居中中断丢失的问题后来发现是中断优先级配置不当导致的。需要说明的是这些故障都是在早期固件版本上出现的经过修复后后半年三款芯片都没有再出现新的故障。这说明国产芯片的硬件本身是可靠的问题主要出在软件适配和配置上。5.2 温度漂移和长期精度表现我还在高低温箱里做了温度测试范围是-20°C到85°C。测试项目是ADC采样精度和内部RC振荡器频率漂移。芯片ADC温漂(-20~85°C)内部RC漂移备注STM32F103±3LSB±2.5%需外部晶振GD32F103±4LSB±3%需外部晶振CH32V103±3.5LSB±2.8%需外部晶振三款芯片的ADC温漂都在可接受范围内但内部RC振荡器的漂移都比较大所以串口通信必须用外部晶振。如果你对时序要求高外部晶振是必须的。5.3 长期供货和价格波动这一年的价格变化也值得记录。2023年初STM32F103C8T6的价格在15元左右GD32F103C8T6在8元左右CH32V103C8T6在6元左右。到了2023年底STM32降到了10元左右GD32降到了6元左右CH32V103降到了5元左右。价格差距在缩小但国产芯片仍然有成本优势。不过选型不能只看价格还要考虑开发成本、维护成本和供货稳定性。我个人的经验是如果项目对成本敏感且产量大国产芯片值得考虑如果项目对开发效率要求高且产量小STM32的生态优势仍然明显。6. 国产替代的选型建议什么场景用什么芯片6.1 适合直接替换STM32的场景如果你现有的项目用的是STM32F103且外设使用比较简单GPIO、USART、SPI、I2C、定时器那么GD32F103可以直接替换硬件不需要改板软件只需要改几个配置。具体来说需要改的地方包括Flash等待周期配置ADC采样时间配置CAN位定时配置如果用了CAN系统时钟初始化GD32的库函数和STM32不同这些改动加起来大概半天的工作量。改完之后建议跑一个完整的回归测试确保所有外设都正常。6.2 适合新项目直接选用CH32V103的场景CH32V103适合新项目特别是对成本敏感、对功耗有要求的场景。它的RISC-V内核虽然生态不如ARM但基本的开发工具和库函数都已经比较完善了。CH32V103的优势功耗低适合电池供电价格便宜适合大批量生产外设丰富该有的都有官方文档和例程比较全CH32V103的劣势调试体验不如STM32社区资源少遇到问题需要自己解决RISC-V工具链的兼容性偶尔有问题6.3 什么情况下还是建议用STM32如果你的项目符合以下任一条件我建议还是用STM32项目周期紧没有时间做适配和验证使用了STM32的高级外设如USB OTG、以太网、CAN FD团队对STM32生态非常熟悉换芯片的学习成本太高项目对可靠性要求极高不能容忍任何适配风险STM32的生态优势是实实在在的Keil、IAR、CubeMX、HAL库、各种中间件这些工具链的成熟度是国产芯片短期内难以追赶的。选型的时候要算总账不能只看芯片单价。7. 一些实用的移植技巧和工具推荐7.1 用条件编译管理不同芯片的配置如果你要同时维护STM32和国产芯片的代码建议用条件编译来管理差异。比如#if defined(USE_GD32) #define FLASH_WAIT_CYCLES 3 #define ADC_SAMPLE_TIME ADC_SampleTime_55Cycles5 #elif defined(USE_CH32V103) #define FLASH_WAIT_CYCLES 2 #define ADC_SAMPLE_TIME ADC_SampleTime_71Cycles5 #else #define FLASH_WAIT_CYCLES 2 #define ADC_SAMPLE_TIME ADC_SampleTime_55Cycles5 #endif这样一套代码可以适配多款芯片维护起来方便很多。7.2 外设自检固件的编写思路我写了一个外设自检固件每次换芯片或者改配置后都跑一遍。这个固件会依次测试GPIO、USART、SPI、I2C、ADC、定时器、CAN每个外设测试通过后通过串口打印结果。自检固件的核心思路是用已知的输入产生已知的输出然后对比实际输出和预期输出。比如GPIO测试就是翻转一个引脚用另一个引脚读回来看是否一致。ADC测试就是接一个基准电压看采样值是否在预期范围内。这个固件帮我省了很多调试时间建议每个做国产替代的团队都写一个。7.3 调试工具的选择ST-Link支持STM32和GD32性价比高推荐。J-Link支持STM32和GD32调试功能更强但价格贵。WCH-LinkCH32V103专用必须用这个价格便宜。逻辑分析仪调试通信协议必备推荐Saleae或者国产的LA系列。我个人最常用的组合是ST-Link VS Code Cortex-Debug这套组合对STM32和GD32都支持调试体验很好。CH32V103用WCH-Link MounRiver Studio这是沁恒官方的IDE虽然界面一般但功能完整。7.4 关于国产芯片的一些个人体会用了一年国产芯片最大的感受是国产芯片的硬件已经足够好了差距主要在软件生态和文档上。很多问题不是芯片本身的问题而是文档没写清楚、例程不完整、社区资源少导致的。我遇到的大部分问题最后都在官方论坛或者技术群里找到了答案但这个过程比较耗时。如果你决定用国产芯片建议做好以下准备预留足够的适配和验证时间加入官方的技术群遇到问题及时问自己写一套外设自检固件每次改配置都跑一遍不要直接搬STM32的代码要逐项检查配置差异国产芯片的进步是肉眼可见的从GD32和CH32V103的表现来看日常应用已经完全够用了。但在高可靠性、高实时性的场景下STM32仍然有优势。选型的时候要根据项目实际情况来定不要盲目追求国产替代也不要迷信进口芯片。最后分享一个我踩过的坑有一次我用GD32替换STM32硬件没改软件只改了Flash等待周期跑了一个月没出问题。结果有一天客户反馈设备偶尔重启我查了很久才发现是GD32的电源复位阈值和STM32不同在电源缓慢上升的情况下GD32会反复复位。后来加了电源监控芯片才解决。这个问题的教训是国产替代的验证要覆盖各种边界条件不能只测正常工作情况。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进