☰
STM32芯片迁移实战:从C8T6到RCT6的Keil工程配置指南
2026/10/6 13:14:24 网站建设 项目流程

最近把一批老项目的芯片从 STM32F103C8T6 换成了 STM32F103RCT6。原因很现实:C8T6 的 64KB Flash 和 20KB RAM 到了极限,程序优化到把字符串常量都抠出来存外部 Flash 还是差几 KB,再加上这颗料的交期和价格越来越不友好。换到 RCT6 之后,Flash 直接翻了四倍到 256KB,RAM 也升到 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 Flash;RCT6 中的 R 代表 LQFP64 封装,C 代表 256KB Flash。两者都是 STM32F103 系列、Cortex-M3 内核,主频上限也都是 72MHz,这是迁移可行性的基础。

对比项STM32F103C8T6STM32F103RCT6
封装LQFP48LQFP64
Flash64KB256KB
SRAM20KB48KB
串口资源USART1/2/3USART1/2/3 + UART4/5
定时器基础型号常用 4 个相比 C8T6 更多,含高级/通用定时器
GPIO 数量较少(约 37 个可用 IO)更多(约 51 个可用 IO)
启动文件startup_stm32f10x_md.sstartup_stm32f10x_hd.s
标准库宏STM32F10X_MDSTM32F10X_HD
HAL 库宏STM32F103xBSTM32F103xE
Flash 编程算法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.s;RCT6 属于高密度器件,对应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.h+stm32f10x_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 对应的是STM32F103xB,RCT6 对应的是STM32F103xE。总之,这一步要结合你工程实际用的库来判断,核心思路是让底层库知道“我现在跑在高密度芯片上”。

2.3 IROM1 和 IRAM1:容量变了,地址和大小都要改

Keil 里 Options for Target -> Target 选项卡中有两个关键参数:IROM1 和 IRAM1。前者定义了代码区的起始地址和大小,后者定义了内部 RAM 的起始地址和大小。

C8T6 的 Flash 是 64KB,对应 IROM1 的 Size 就是0x10000(65536 字节),RAM 20KB 对应 IRAM1 Size 是0x5000(20480 字节)。换到 RCT6 后,Flash 变成 256KB,Size 要改成0x40000;RAM 变成 48KB,Size 要改成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 Flash,RCT6 应该使用STM32F10x High-density Flash。如果这里没改,下载时很容易报出芯片 ID 不匹配、Wrong Flash Algorithm 之类的错误。

3.4 编译、链接、下载全流程

配置改完后,执行一次重新编译(Rebuild,建议不要只用 Build,因为改了宏和启动文件后,增量编译可能漏掉一些依赖变化)。按 F7 或点击重建按钮即可。

第一次完整编译通过后,观察 Output 窗口里的 Program Size 信息:

Program Size: Code=46820 RO-data=3840 RW-data=1216 ZI-data=48200

如果这一行能正常输出,说明链接没出问题。这里我自己习惯做个简单记录:把 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/3,RCT6 在此基础上多了 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/PB7,C8T6 和 RCT6 上虽然都有这两个引脚的复用功能,但如果你用的不是标准引脚,必须检查代码里是否调用了 GPIO_PinRemapConfig 使能了重映射。如果这个配置丢了,串口数据就不对。

5.4 问题速查表

最后把这次迁移过程中可能遇到的问题整理成一张速查表,方便你在实际操作时快速定位。

现象可能原因解决办法
编译报 L6220E,代码超过 64KIROM1 Size 未改成 256KOptions for Target -> Target,IROM1 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 宏都已切换到 HD
FreeRTOS 任务创建失败堆 RAM 配置过小增大 FreeRTOSConfig.h 中的 configTOTAL_HEAP_SIZE,RCT6 下可到 16KB 以上

表格里列的每一条都是我在实际项目中确认过的,基本覆盖了这次迁移的大部分坑点。当然,每个人工程的具体情况不同,如果你遇到的问题不在表里,也别慌,按“编译配置 -> 启动文件 -> 下载算法 -> 硬件引脚”这个顺序逐层排查,一般都能找到问题所在。

最后再分享一个我自己的实操习惯:迁移完成后不要只跑一遍点灯就宣布成功,一定要做一次完整的功能回归,把每个外设、每个 GPIO、每路串口都过一遍。我这次迁移的程序里就有个坑,当时觉得引脚肯定没问题,直接忽略了代码里一处 USART1 重映射配置,结果功能测试时串口完全哑了。后来把重映射代码补上才恢复正常。如果当时偷懒,很可能交付到客户那边才暴露问题。所以,配置改完之后,给自己留足时间做测试,比什么都有用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询