ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

STM32芯片迁移实战:从C8T6到RCT6的Keil工程配置指南

STM32芯片迁移实战:从C8T6到RCT6的Keil工程配置指南 最近把一批老项目的芯片从 STM32F103C8T6 换成了 STM32F103RCT6。原因很现实C8T6 的 64KB Flash 和 20KB RAM 到了极限程序优化到把字符串常量都抠出来存外部 Flash 还是差几 KB再加上这颗料的交期和价格越来越不友好。换到 RCT6 之后Flash 直接翻了四倍到 256KBRAM 也升到 48KB整个项目一下子松快了。很多人一听“换芯片”就觉得是大工程但实际上像 C8T6 换 RCT6 这种同系列、同内核都是 Cortex-M3的迁移并没有想象中那么可怕。不过它也不是把 Device 一改就完事启动文件、宏定义、Flash 下载算法这些配置如果没跟着改轻则编译报错重则烧录后直接跑飞。这篇文章我就把这次迁移的完整过程、底层原理和踩过的坑全部摊开讲给正准备做同样迁移的朋友一份可以直接照着操作的参考。不管你现在用的是标准外设库还是 HAL 库只要工程是 Keil MDK 环境这篇都应该能帮到你。1. 为什么要把C8T6工程迁到RCT6场景、收益与风险1.1 什么情况下才值得做这个迁移先泼一盆冷水不是所有 C8T6 工程都应该迁到 RCT6。RCT6 的单价、封装尺寸都比 C8T6 大如果你只是做个简单的温湿度采集、LED 控制之类的小产品代码量连 20KB 都用不到那完全没有必要折腾。但我遇到过下面这几种情况迁移的收益就非常明显第一类是 Flash/RAM 告急的项目。比如产品迭代过程中不断加功能Code 区已经逼近 64KB 红线编译时开始频繁报 L6220E 这类溢出错误。这种时候与其继续做代码瘦身不如直接用 RCT6 把硬件资源上限提上去后续加功能也不用再抠抠搜搜。第二类是 C8T6 供应出问题。这两年芯片供应链波动最受伤的就是这类热门型号。如果手头项目交期卡得紧而 C8T6 又拿不到货用 RCT6 替代是很常见的选择毕竟同系列的引脚定义和软件兼容性相对好处理。第三类是产品需要扩展外设。C8T6 只有 3 个 USART、可支配 GPIO 也有限如果用着用着发现串口不够用、按键和灯挤在同一个引脚上抢复用功能那换 RCT6 就是顺着现有代码平滑过渡到更大资源池的办法。1.2 C8T6和RCT6到底差在哪先搞清楚两个芯片的本质区别。从型号命名就能看出一些端倪C8T6 中的 C 代表 LQFP48 封装8 代表 64KB FlashRCT6 中的 R 代表 LQFP64 封装C 代表 256KB Flash。两者都是 STM32F103 系列、Cortex-M3 内核主频上限也都是 72MHz这是迁移可行性的基础。对比项STM32F103C8T6STM32F103RCT6封装LQFP48LQFP64Flash64KB256KBSRAM20KB48KB串口资源USART1/2/3USART1/2/3 UART4/5定时器基础型号常用 4 个相比 C8T6 更多含高级/通用定时器GPIO 数量较少约 37 个可用 IO更多约 51 个可用 IO启动文件startup_stm32f10x_md.sstartup_stm32f10x_hd.s标准库宏STM32F10X_MDSTM32F10X_HDHAL 库宏STM32F103xBSTM32F103xEFlash 编程算法STM32F10x Med-density FlashSTM32F10x High-density Flash这里面最核心的三个差异就是启动文件、宏定义和存储容量配置。很多人在迁移时只改了 Device 型号就急着编译结果报一堆莫名其妙的错就是因为这三个东西没有同步调整。1.3 迁移的整体难度和风险点说“无缝转换”其实是标题党一点的说法更准确地说应该是“低成本转换”。纯软件层面的改动非常小就是几个配置项的调整真正要小心的是硬件引脚这块。因为 LQFP48 和 LQFP64 的引脚不完全一一对应你要重新核对原理图上用到的引脚在新芯片上是否仍然存在、复用功能是否一致。如果原工程只是把 C8T6 接到了最小系统板或者自定义板卡上那引脚核对这一关就必须认真做。2. 迁移前必须弄明白的三件事启动文件、宏定义与存储配置2.1 启动文件md 和 hd 到底差在哪很多新手容易忽略启动文件的重要性觉得它就是个上电跑通的“模板文件”随便放哪个都行。实际上这个文件决定了芯片上电后第一条指令从哪里执行、堆栈怎么初始化、中断向量表怎么建立以及 SystemInit 和 main 怎么被调用。STM32F103 系列根据 Flash 容量大小分成低密度LD、中密度MD、高密度HD等不同等级。C8T6 属于中密度器件对应startup_stm32f10x_md.sRCT6 属于高密度器件对应startup_stm32f10x_hd.s。这两个启动文件最明显的区别在于中断向量表长度HD 版的中断向量表比 MD 版长得多多出来的部分用于 UART4、UART5、TIM5、TIM6、TIM7、TIM8 这些在 HD 器件上才有的外设中断。如果你只换了 Device 型号却还保留 MD 启动文件程序在绝大多数情况下也能编译通过、也能烧录甚至简单的点灯、串口打印都可能正常。但一旦你用到了 UART4 这类新增外设或者某个外设中断触发程序就会跳到不存在的中断向量地址结果是进 HardFault 还是静默复位全看当时的状态。这类问题排查起来非常隐蔽因为它的表现不是稳定的编译错误而是偶发性的功能异常。所以迁移的第一步就是要记住RCT6 必须配startup_stm32f10x_hd.s。这不是可选项是必选项。2.2 宏定义STM32F10X_MD 换成 STM32F10X_HD 的原因标准外设库StdPeriph Driver也就是老项目里最常见的stm32f10x.hstm32f10x_conf.h这套在编译时会根据一个预处理宏来裁剪代码。当你定义STM32F10X_MD时库认为自己运行在中密度芯片上有些外设的寄存器定义、中断号、Flash 大小等都会按 MD 器件的规格来换成STM32F10X_HD后库才会把高密度器件特有的外设资源编译进去。这个宏是在 Keil 的 Options for Target - C/C - Define 里配置的。典型的标准库工程里Define 一般是USE_STDPERIPH_DRIVER, STM32F10X_MD迁移到 RCT6 后需要改成USE_STDPERIPH_DRIVER, STM32F10X_HD如果你用的是 HAL 库或者是通过 STM32CubeMX 生成的工程宏定义又不太一样。C8T6 对应的是STM32F103xBRCT6 对应的是STM32F103xE。总之这一步要结合你工程实际用的库来判断核心思路是让底层库知道“我现在跑在高密度芯片上”。2.3 IROM1 和 IRAM1容量变了地址和大小都要改Keil 里 Options for Target - Target 选项卡中有两个关键参数IROM1 和 IRAM1。前者定义了代码区的起始地址和大小后者定义了内部 RAM 的起始地址和大小。C8T6 的 Flash 是 64KB对应 IROM1 的 Size 就是0x1000065536 字节RAM 20KB 对应 IRAM1 Size 是0x500020480 字节。换到 RCT6 后Flash 变成 256KBSize 要改成0x40000RAM 变成 48KBSize 要改成0xC000。这里有个常见的坑在 Keil 5 里直接切换 Device 型号到 STM32F103RC 后Target 选项卡里的 IROM1 和 IRAM1 理论上会自动刷新但有些旧版本、有些从 Keil 4 迁移过来的工程或者修改过 Target 配置的工程并不会自动更新。如果你编译时发现代码超过 64KB 仍然报溢出错误十有八九就是这里没改。另外如果你的工程里用了 IAP Bootloader或者自定了 Flash 分区IROM1 的起始地址不是默认的0x08000000而是类似0x08008000这种偏移地址。换芯片后要一并核对这些地址是否合理防止 Bootloader 区和 App 区重叠。3. Keil工程转换实操手把手改配置3.1 更换Device芯片型号这一步最简单也是大多数人迁移时唯一会做的一步。打开 Keil 工程点击魔法棒图标Options for Target进入 Device 选项卡。在左侧的芯片列表里依次展开 STMicroelectronics - STM32F1 Series - STM32F103找到 STM32F103RC选中即可。选中后右侧会显示这颗芯片的基本信息包括内核、主频、Flash 容量等可以顺手确认一下型号没错。这里有一个细节Keil 的芯片列表是基于你安装的 Device Pack 的如果列表里找不到 STM32F103RC说明你的 STM32F1xx_DFP 没有装全得去 Pack Installer 里把 STM32F1 系列的支持包更新或补齐。3.2 修改C/C宏定义切换完芯片型号后切到 C/C 选项卡找到 Define 输入框。标准库工程把USE_STDPERIPH_DRIVER, STM32F10X_MD改成USE_STDPERIPH_DRIVER, STM32F10X_HD如果你的工程里还有其他宏比如_DEBUG、USE_FULL_ASSERT之类的保留不动只动容量相关的宏。这里要特别提醒如果你确认自己用的标准库但 Define 里没有STM32F10X_MD也没有STM32F10X_HD那这个宏可能是在别的地方定义的。我见过一些老工程会把宏写在 stm32f10x.h 文件里直接#define STM32F10X_MD或者用编译器命令行参数传入。这种时候你就要去对应位置改不能只盯着 Keil 的 Define 输入框。3.3 替换启动文件与检查下载算法接下来是启动文件。在 Keil 左侧 Project 面板里找到工程树上的启动文件通常叫startup_stm32f10x_md.s可能单独放在一个 Startup 分组下也可能和系统文件放在一起。右键选择 Remove from target把它从工程里移除但不要直接删除磁盘上的原始文件。然后右键 Source Group选择 Add Existing Files找到startup_stm32f10x_hd.s添加进来。问题来了这个 hd 启动文件去哪里找最常见的途径有三个从 Keil 的安装目录找。一般路径类似C:\Keil_v5\ARM\Startup\ST\STM32F10x\里面 ld、md、hd 的启动文件都有。从 ST 官方标准外设库的模板工程里找。标准外设库压缩包解压后Project\STM32F10x_StdPeriph_Template\下面就有对应不同密度器件的工程模板。直接从任何一个基于 RCT6 的现成工程里复制。这个方法最省事只要确保对方的启动文件是原厂的就可以。添加完成后建议再去 Target 选项卡里确认一下 Startup 文件是否正确关联了 hd 版本。有些时候文件虽然加进了工程树但 Target 设置里选的还是旧文件这种错误编译时才暴露。还有一个容易忽略的地方Flash 下载算法。点击 Utilities 选项卡 - Settings或者 Debug 选项卡 - Settings两者进的是同一个下载配置界面- Flash Download你会看到当前选择的 Programming Algorithm。C8T6 对应的算法是STM32F10x Med-density FlashRCT6 应该使用STM32F10x High-density Flash。如果这里没改下载时很容易报出芯片 ID 不匹配、Wrong Flash Algorithm 之类的错误。3.4 编译、链接、下载全流程配置改完后执行一次重新编译Rebuild建议不要只用 Build因为改了宏和启动文件后增量编译可能漏掉一些依赖变化。按 F7 或点击重建按钮即可。第一次完整编译通过后观察 Output 窗口里的 Program Size 信息Program Size: Code46820 RO-data3840 RW-data1216 ZI-data48200如果这一行能正常输出说明链接没出问题。这里我自己习惯做个简单记录把 Code、RW-data、ZI-data 这几个数字记到本子上等下载完程序后跟实际运行情况做个比对尤其是看 ZI-data 是否超过了 48KB超了说明 RAM 还是不够。接下来是下载。连接好 ST-LINK、DAP-Link 或 J-Link确认 Debug 选项卡里的下载器型号选对点击 Download 按钮。下载成功后直接按复位键运行。如果一切正常程序应该和之前在 C8T6 上表现一致至少灯会按预期闪烁、串口会输出日志这些基础功能不会出现异常。到这里工程层面的迁移就算完成了。但先别急着欢呼接下来要做的硬件引脚的复核和功能回归测试才是真正决定这次迁移是否“无缝”的关键。4. 转换后无法回避的硬件差异引脚与外设4.1 LQFP48到LQFP64引脚变化怎么对应这是迁移过程中最需要认真的一个环节。C8T6 是 48 脚封装RCT6 是 64 脚封装多出来的 16 个引脚带来了更多 GPIO 和外设复用能力但同时也要面对一个问题你原来在 C8T6 上用的引脚在 RCT6 上是否还存在、是否还在原来的位置。好在绝大多数情况下C8T6 上原有的功能引脚在 RCT6 上都能找到比如 PA9/PA10 串口 1、PB6/PB7 的 I2C1、PA5/PA6/PA7 的 SPI1 等这些基本沿用。但有个经验是必须强调的不要凭“印象”或者“感觉”来判断引脚兼容性。我见过有人把旧板子的原理图直接丢给厂家打样结果忽略了几根引脚在 64 脚封装上被其他功能占用板子回来后才发现问题。正确的做法是把你工程代码里所有用到的 GPIO 列成一张表对照 RCT6 的数据手册引脚定义图和你的新板子原理图逐脚核对。特别注意那几个特殊的引脚PB2 同时也是 BOOT1 引脚RCT6 上依然是 BOOT1不能随意当普通 IO 用除非确定 BOOT 电路不会干扰。PB3、PB4、PA15 默认复用为 JTAG 引脚如果当成普通 IO 使用需要在代码里关闭 JTAG 功能。在 C8T6 上部分引脚因为封装限制没有引出比如某些 ADC 通道、某些定时器通道换到 RCT6 后这些资源反而变得可用了。如果你原来的板子用的是 C8T6 的最小系统板想换成 RCT6 的板子还要注意两者的最小系统电路基本一致但 Boot 引脚的电路设计不一定相同上电前先测一下 BOOT0 的电平状态。4.2 新增外设的收益UART4/UART5、更多定时器迁移之后最直接的硬件收益就是外设资源变多了。C8T6 只有 USART1/2/3RCT6 在此基础上多了 UART4 和 UART5。对很多需要多路串口通信的产品来说这几乎就是从“接口不够用”到“接口富余”的质变。我这次迁移的项目恰好就受益于这一点。原先在 C8T6 上三路串口分别用于调试日志、主控通信和外部模块数据接收再要接一个传感器就得用软串口既占 CPU 又容易丢数据。换到 RCT6 后直接把传感器接到 UART4代码里用标准库初始化一遍就行性能稳定多了。代码里新增 UART4 的初始化方式和 USART1 大同小异只是要记得开启的时钟是RCC_APB1Periph_UART4引脚用 PC10/PC11 或 PD8/PD9 等对应复用脚。如果你实际用到了这些新增外设那就再次验证了前面说的启动文件必须换 HD 版本——不换的话这些外设的中断向量在 MD 启动文件里根本不存在中断一触发就出问题。定时器方面也类似。RCT6 提供了更多可用的 TIM 外设如果后续产品需要增加 PWM 输出通道、输入捕获通道或者做更复杂的定时任务不用再像以前那样多个功能抢一个定时器了。4.3 使用FreeRTOS时的容量收益如果你在 C8T6 上移植过 FreeRTOS应该能体会到 20KB RAM 的局促任务栈、消息队列、信号量、互斥锁这些都要从这 20KB 里分配系统任务都不敢多建几个堆大小更是要精打细算。换到 RCT6 后48KB RAM 让 FreeRTOS 的配置空间大了不少。以我手头这个工程为例之前 C8T6 上只敢开 3 个任务每个任务栈 256 字节再加一个 1KB 的定时器服务任务RAM 基本见底。迁移到 RCT6 后我重新配置了 FreeRTOSConfig.h把 heap 从 4KB 提到 16KB任务加到 5 个每个栈给到 512 字节或 1024 字节系统跑起来游刃有余。Flash 方面的收益同样明显。如果你的产品需要保存字库、音频片段、图片资源或者比较复杂的日志记录之前的 64KB 基本是捉襟见肘现在 256KB 可以随意挥霍。不过要注意FreeRTOS 本身不直接涉及 Flash 分区问题但如果你在代码里直接定义了const大数组或者用__attribute__((at()))指定存储位置换芯片后要检查这些地址是否还在合法范围内。5. 常见问题与实战排查5.1 下载报错算法不匹配、BOOT脚、DAP连接迁移后最容易碰到的问题就是下载阶段报错。如果你把编译好的程序下载到 RCT6 时Keil 弹出类似Wrong Flash Algorithm或者Flash Download failed - Cortex-M3的提示第一步先检查刚才说的 Programming Algorithm 是不是还是 Med-density。还有一种可能是算法没错但算法文件里指定的器件 ID 和实际芯片不符。这种情况多半是信号线接触不良或者供电不稳导致下载器读不到正确的 ID。可以尝试降低 SWD 通信速率在 Debug 选项卡的 Settings 里把 Max Clock 从 4MHz 降到 1MHz很多莫名其妙的下载失败都能这样解决。如果下载器完全连接不上还要检查 BOOT0 引脚。正常程序运行模式下 BOOT0 必须是低电平有些开发板或者自制板子上 BOOT0 的跳线帽位置变动过或者被误接到高电平就会导致芯片上电后进入系统存储器模式而不是主 Flash 启动程序看起来“烧不进去”或者“烧进去跑不了”。这也正好对应了你在网上看到的那些关于 DAP 下载失败、BOOT1 电平的讨论。5.2 编译链接报错 L6220E / L6221E编译报错中最典型的是L6220E: Execution region ... size exceeds limit。这句话翻译过来就是代码或者数据的大小超过了链接脚本规定的区域上限。如果你迁移后还报这个错误先别怀疑芯片没换成功大概率是 Target 选项卡里的 IROM1 Size 还是0x10000甚至是更小。把它改成0x40000保存重新编译问题自然消失。如果改完之后还报那就要检查你的工程里是不是有多个 Target 配置。有些工程会建立多个 Target 分别对应不同芯片你改的是当前 Target但实际编译的可能不是这个。这种时候用 Manage Project Items在 Project 菜单下查看一下当前活动 Target 是哪一个是很有必要的。另一个容易让人困惑的链接错误是 L6221E提示某个 Execution Region 放不下指定的数据。这个通常和 RAM 有关检查 IRAM1 是否已经改到0xC000同时看看是不是有什么大数组或者堆分配把 RAM 挤爆了。5.3 代码功能异常中断不进、串口乱码代码烧录成功后如果出现“整个程序不跑”或者“某个功能时好时坏”的问题优先要想到启动文件。举一个非常典型的例子用标准库编程代码初始化过程中突然进入了 HardFault 中断检查了一圈逻辑都没问题。最后发现启动文件还是旧的 md 版本中断向量表里根本没有对应外设的中断入口于是一触发就跳飞了。这种情况在迁移到 RCT6 后尤其常见因为 RCT6 上的外设比 C8T6 多一旦代码里初始化了 UART4 或新增定时器旧启动文件根本不知道这些中断应该去哪里。串口乱码问题也要分情况看。如果换芯片前串口是好的换之后才乱码先看代码里串口初始化时用的是哪个引脚——确认这个引脚在 RCT6 封装上对应的是不是同一个 USART 的同一个引脚。比如有些板子上 USART1 的 TX/RX 用的不是 PA9/PA10而是做了重映射到 PB6/PB7C8T6 和 RCT6 上虽然都有这两个引脚的复用功能但如果你用的不是标准引脚必须检查代码里是否调用了 GPIO_PinRemapConfig 使能了重映射。如果这个配置丢了串口数据就不对。5.4 问题速查表最后把这次迁移过程中可能遇到的问题整理成一张速查表方便你在实际操作时快速定位。现象可能原因解决办法编译报 L6220E代码超过 64KIROM1 Size 未改成 256KOptions for Target - TargetIROM1 Size 改为 0x40000编译报 L6221EIRAM1 Size 未改成 48K 或 RAM 不够IRAM1 Size 改为 0xC000或优化大数组/堆分配下载报 Wrong Flash AlgorithmFlash 编程算法仍是 Med-density在 Flash Download 中添加 STM32F10x High-density Flash 算法下载器找不到芯片BOOT0 被拉高、接线不良或 SWD 速率过高确认 BOOT0 为低电平降低 SWD Clock检查 SWDIO/SWCLK程序烧录后不运行未复位、BOOT 脚错误或电源异常按复位键、检查 BOOT 电路、确认 3.3V 供电稳定中断不触发或进 HardFault启动文件仍是 md 版本替换为 startup_stm32f10x_hd.s串口乱码或收发异常引脚复用配置丢失或时钟初始化不对核对代码中 GPIO 引脚与重映射设置检查 RCC 时钟使能使用 UART4/UART5 无反应需要 HD 启动文件 标准库 HD 宏确认启动文件和 Define 宏都已切换到 HDFreeRTOS 任务创建失败堆 RAM 配置过小增大 FreeRTOSConfig.h 中的 configTOTAL_HEAP_SIZERCT6 下可到 16KB 以上表格里列的每一条都是我在实际项目中确认过的基本覆盖了这次迁移的大部分坑点。当然每个人工程的具体情况不同如果你遇到的问题不在表里也别慌按“编译配置 - 启动文件 - 下载算法 - 硬件引脚”这个顺序逐层排查一般都能找到问题所在。最后再分享一个我自己的实操习惯迁移完成后不要只跑一遍点灯就宣布成功一定要做一次完整的功能回归把每个外设、每个 GPIO、每路串口都过一遍。我这次迁移的程序里就有个坑当时觉得引脚肯定没问题直接忽略了代码里一处 USART1 重映射配置结果功能测试时串口完全哑了。后来把重映射代码补上才恢复正常。如果当时偷懒很可能交付到客户那边才暴露问题。所以配置改完之后给自己留足时间做测试比什么都有用。
返回列表