☰
STM32F103C8T6迁移到RCT6:Keil工程启动文件、Flash算法与内存配置全攻略
2026/10/6 13:14:24 网站建设 项目流程

先抛一个问题:你的项目在STM32F103C8T6上跑得正欢,突然要加屏幕、加Modbus、加日志,Flash只剩几个KB,内存也天天告警,怎么办?很多人第一反应是“换个大芯片”,然后打开Keil,把Device从STM32F103C8T6改成STM32F103RCT6,点编译。结果要么烧不进去,要么上电就HardFault。

C8T6和RCT6虽然都叫STM32F103,内核也都是Cortex-M3,但它们在存储容量、封装脚位、外设数量、启动文件、Flash烧写算法这几个维度上,确实不是简单“替换型号”就能完事的东西。所谓的“无缝转换”,我的理解是工程层面平滑切换,而不是你拿C8T6的PCB直接焊一颗RCT6上去用。这篇就记录我把一个Keil工程从C8T6迁到RCT6的完整操作,包含Device选择、启动文件替换、宏定义修改、Target内存配置、下载算法调整,还有实际踩过的坑。

1. 换芯之前先看明白:C8T6和RCT6到底差在哪

1.1 芯片资源差异表

做转换之前,先把两颗芯片的关键差异列出来,心里才有数。

项目STM32F103C8T6STM32F103RCT6
内核Cortex-M3 @72MHzCortex-M3 @72MHz
Flash64KB(0x10000)256KB(0x40000)
SRAM20KB(0x5000)48KB(0xC000)
封装/引脚LQFP48LQFP64
定时器TIM1、TIM2/3/4TIM1/TIM8、TIM2/3/4/5、TIM6/7
串口USART1/2/3USART1/2/3 + UART4/5
ADCADC1/2ADC1/2/3
密度等级Medium DensityHigh Density

别看主频一样,Flash和RAM直接翻了几倍。更重要的是,RCT6从“中密度”跨到了“高密度”,这个密度差异会牵扯到Keil工程里三样关键东西:启动文件、预处理宏、Flash下载算法。这三样没对齐,后面一定会出事。

1.2 密度差异为什么是核心矛盾

STM32F103家族在标准外设库和CMSIS里,会按Flash和SRAM容量把芯片分成低密度、中密度、高密度。C8T6属于中密度,RCT6属于高密度。这个分类不只是容量问题,还会影响中断向量表、内置Flash编程的页大小、部分外设的数量和寄存器布局。

举个例子,高密度芯片的中断向量表里比中密度多了TIM5、TIM6、TIM7、TIM8、UART4、UART5、ADC3、SDIO等中断入口。你如果工程里还挂着中密度的启动文件,这些新增中断入口根本不存在,程序一旦触发这些中断,就会跳进一个默认的弱定义里,表现就是HardFault或者功能完全没反应。所以我把“替换启动文件”这件事排在所有修改里最前面,你后面就明白为什么了。

2. Keil工程切换:从Device到链接脚本的核心配置

2.1 在Device选项卡里更换目标芯片

打开Keil工程,点菜单栏的“Project -> Options for Target”,或者直接点工具栏那个“魔术棒”图标,切到“Device”选项卡。在芯片树里找STMicroelectronics -> STM32F1 Series -> STM32F103 -> STM32F103RCT6,选中,点OK。

这一步看起来简单,但有几个细节需要确认。第一,你的MDK版本和Pack包要支持RCT6。建议用Keil MDK 5.37以上版本,并且在Pack Installer里把STM32F1系列的Device Family Pack装好。如果Pack没装好,Device列表里找不到RCT6,或者选中后会弹一堆编译错误。第二,选中RCT6后,Device页面下方会显示这颗芯片的Flash和RAM描述,先确认显示的是256KB Flash、48KB RAM再继续,这是第一道保险。

选完Device后,如果你直接编译,大概率会报错误或者警告,因为工程里很多配置还是老C8T6的。接下来才是重点。

2.2 替换启动文件与预处理宏

换完Device,Keil并不会自动替你删掉旧的启动文件。打开工程树,找到STARTUP文件夹下的startup_stm32f10x_md.s,这就是中密度启动文件。右键“Options for Group”或者直接“Manage Project Items”,把它从工程里移除,然后添加新的startup_stm32f10x_hd.s。

添加HD启动文件的方式,我推荐直接从标准外设库或者STM32Cube固件包里拿。标准外设库的路径一般在:

  • Libraries/CMSIS/Device/ST/STM32F10x/startup/arm/startup_stm32f10x_hd.s

如果你用的是STM32CubeF1固件包,路径类似:

  • Drivers/CMSIS/Device/ST/STM32F10x/Source/Templates/arm/startup_stm32f10x_hd.s

添加完启动文件后,还要同步修改预处理宏。如果工程是标准外设库写的,打开“C/C++”选项卡,在Define里把STM32F10X_MD改成STM32F10X_HD。之前定义的USE_STDPERIPH_DRIVER等宏保留。这一步非常重要,因为stm32f10x.h就是靠STM32F10X_HD来识别高密度芯片,然后决定Flash页大小、外设地址、中断号等定义。

不少人改完Device就忘了改宏,结果编译能过,但调用RCT6新增外设时要么编译不过,要么运行异常。这种问题最隐蔽,我建议你把“换启动文件”和“换宏”当成一组操作,谁做都别拆开。

2.3 修改Target内存参数(含分散加载文件)

选完器件和宏,下一步是检查“Target”选项卡里的内存范围。很多人忽略这个,然后遇到链接错误。打开“Options for Target -> Target”,在Memory Areas区域做两处修改:

  • IROM1:Start保持0x08000000,Size从0x10000改成0x40000。
  • IRAM1:Start保持0x20000000,Size从0x5000改成0xC000。

如果你的工程勾选了“Use Memory Layout from Target Dialog”,改这里就够了。但如果你的工程用了自定义的分散加载文件(.sct),那还得同步修改sct文件。一个典型的旧sct文件长这样:

LR_IROM1 0x08000000 0x00010000 { ER_IROM1 0x08000000 0x00010000 { *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00005000 { .ANY (+RW +ZI) } }

换成RCT6后,需要改成:

LR_IROM1 0x08000000 0x00040000 { ER_IROM1 0x08000000 0x00040000 { *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x0000C000 { .ANY (+RW +ZI) } }

改完编译后,最好打开工程目录下生成的.map文件,看最前面的“Memory Map of the image”,确认ROM和RAM范围已经变成0x40000和0xC000。这一步是验证Target配置有没有生效的最直接方法。

2.4 更新Flash下载算法

这是很多人烧不进代码的元凶。C8T6默认用的下载算法是“STM32F10x Medium-density Flash 64K”,但RCT6的Flash是256KB,必须换成“STM32F10x High-density Flash 256K”。

操作路径:Options for Target -> Debug -> 选择调试器(ST-Link、J-Link、DAP等) -> Settings -> Flash Download。在Programming Algorithm列表里,把旧的Medium-density算法删掉,点Add,选择“STM32F10x High-density Flash 256K”。

有朋友会问,我明明用的是ST-Link,为什么还要关心Flash算法?因为Flash算法是由MDK和调试器的调试驱动管理的,跟你用什么调试器品牌关系不大。算法里包含了芯片内部Flash的扇区大小、擦除命令、编程时序,选错了算法,轻则烧录失败,重则擦写到一半卡死。

如果你使用旧版MDK,Flash Download可能在Utilities选项卡里,路径不同,但逻辑一样。反正就是找到当前调试器设置里的Flash Download或Programming Algorithm,把算法改成High-density。

3. 标准外设库和HAL库的转换差异

3.1 SPL库:宏定义与内部Flash编程

如果你的工程用的是标准外设库,前面的宏修改已经覆盖了主要问题。这里再单独强调一下Flash编程。如果项目里做bootloader或者参数存储,会调用Flash擦写函数。在stm32f10x_flash.c里,标准外设库会按STM32F10X_HD或STM32F10X_MD来选择Flash页大小和擦除逻辑。中密度和高密度的Flash页大小不一样,中密度通常按1KB页处理,高密度通常按2KB页处理。宏还是MD的话,擦写Flash时可能只擦一半,或者地址计算错误,导致参数区数据损坏。这种问题不到现场很难抓。

所以如果你在C8T6工程里写过内部Flash,换到RCT6之后,除了把宏改成STM32F10X_HD,还要把你调用Flash擦写时的页大小参数重新核对一遍。别小看这几行函数,项目一旦跑起来,数据损坏就是灾难。

3.2 HAL库:CubeMX重生成还是手动改宏

如果你用的是STM32CubeMX生成的HAL工程,最省事的办法是打开CubeMX,把芯片型号从STM32F103C8Tx改成STM32F103RCTx,然后重新生成代码。生成的工程会自动把预处理宏设置成STM32F103xE,也会自动更新启动文件和Flash算法,省掉不少麻烦。

如果你不想动CubeMX,也可以手动改。C8T6的HAL工程里,宏定义通常是STM32F103xB,需要改成STM32F103xE。这个宏会影响stm32f1xx_hal_conf.h里的外设开关,以及stm32f1xx.h里的芯片规格定义。如果宏不改,RCT6上的TIM8、UART4/5、ADC3这些外设,在HAL层可能没有对应的驱动实例,强行调用会编译报错或者运行时访问非法地址。

另外,如果原来的C8T6工程里跑着FreeRTOS,换了RCT6之后,记得把FreeRTOSConfig.h里的configTOTAL_HEAP_SIZE调大一点。比如从2KB调到12KB,反正内存翻了不止一倍,堆大小可以大大方方扩容。用heap_4的话,内存管理效率也会更高。

3.3 中断向量与外设驱动的同步

不管你是用SPL还是HAL,换到高密度芯片后,都要去看一下中断服务函数文件。C8T6工程里的stm32f10x_it.c可能只写了中密度设备的中断处理,比如USART1、TIM2之类。RCT6新增的中断源,比如TIM5、UART4、UART5、ADC3,可能一个处理函数都没有。

如果你打算用到这些新外设,必须自己补对应的IRQHandler函数,同时在启动文件里确认这些中断入口已经存在。很多时候你照着手册配置好了UART4,但中断就是不触发,查到最后发现是启动文件里根本没有UART4_IRQHandler这个入口。所以我的习惯是:换完启动文件后,打开文件搜一下IRQHandler,看看我要用到的外设中断名在不在里面,再做下一步。

4. 引脚变化与外围设计核对清单

4.1 LQFP48与LQFP64的IO差异

C8T6是LQFP48,RCT6是LQFP64,物理封装不一样,引脚数量也不一样。C8T6的GPIO大概37个,RCT6大概是51个。多出来的主要是PC0~PC12这些C口引脚,以及PD2等D口引脚。48脚封装上PC13、PC14、PC15是有的,但PC0~PC12大部分没有引出来。所以原来在C8T6上不敢用的PC口资源,到了RCT6就能光明正大地用了。

这里有个很典型的例子:USART3的重映射。C8T6是48脚封装,PC10和PC11没有引出来,所以如果你想把USART3重映射到PC10/PC11,根本做不到。换到RCT6的64脚封装后,PC10/PC11就出来了,USART3的重映射选项变得完全可用。这种情况下,代码层面可能只需要改一下GPIO初始化和AFIO重映射,但前提是你的芯片封装真的把这些引脚引出来了。

4.2 新增引脚和外设需要注意的复用关系

换到RCT6后,可以用的外设多了,但也不是所有引脚都能随意复用。GPIO的复用映射在STM32F103的数据手册里有一张表,里面写了每个引脚可以复用成哪些外设功能。比如TIM1_CH1通常是PA8,但同时也可以从PE9复用过来,但PE9在64脚封装上不一定有。同样,ADC3的某些通道可能在PA口上,也可能在PF口上,PF口在64脚封装里基本没引出来,所以能用到的ADC3通道有限。

我的建议是,配置新外设之前,先打开对应封装的引脚图,把你打算用的引脚和外设功能对照一遍。不要只看芯片是RCT6就以为所有资源都能用,封装会把一部分功能“锁”在芯片肚子里。这是STM32F103选型时最容易踩的坑。

4.3 硬件层面的电源、地、BOOT检查

RCT6因为是64脚封装,会有一些C8T6没有的引脚,比如VREF+、VREF-。在大多数开发板上,VREF+会接到3.3V,VREF-接到GND。如果你是自己画板子,记得把这些参考电压引脚处理好,不然ADC精度会受影响。NRST、BOOT0、BOOT1这些引脚的接法和C8T6基本一致,BOOT0拉低到GND走正常FLASH启动,这个不用多改。

如果你是想在C8T6原有的PCB上“无损替换”,这里必须泼盆冷水:C8T6和RCT6封装不同,引脚不是一一对应的,直接互换是不可能的。想硬件原位升级,48脚封装应该选STM32F103CBT6,但它的RAM依然是20KB,只是Flash变成128KB。所以真正的跨档升级,还是要换64脚或100脚的板子。

5. 编译烧录中的“显眼包”错误与排查实录

5.1 下载时提示“No Flash Device”或“Could not find device”

这个现象在ST-Link和DAP下载器上都很常见。我排查过的项目里,90%是因为Flash Download算法没有换成High-density。去Debug -> Settings -> Flash Download里看一眼,如果列表里还是“STM32F10x Medium-density Flash 64K”,直接换成“STM32F10x High-density Flash 256K”。换完重新上电下载,一般就好了。

如果算法换了还是报No Flash Device,再检查BOOT0是不是拉低、复位电路是否正常、SWDIO和SWCLK有没有接对。有时候是调试器驱动问题,重新拔插一次USB也能解决。

5.2 程序跑起来就HardFault,尤其是一开新外设

这种问题我见过太多次。最常见的原因是启动文件还是旧的MD版本。工程树里如果挂着startup_stm32f10x_md.s,即使你在C/C++里把宏改成了STM32F10X_HD,中断向量表里也不会自动多出UART4、TIM5、ADC3这些入口。一旦程序配置了这些外设并开启了中断,CPU就会在中断响应时找不到入口,最终跳进HardFault_Handler。

排查方法很简单:先把启动文件换成HD版本,再查看map文件里是否有对应中断处理函数的地址。如果函数存在且地址落在向量表区域,HardFault大概率就消失了。

5.3 编译报“L6220E: Load region LR_IROM1 size exceeds limit”

这个错误是链接器说你的代码超出了IROM1区域,说白了就是Target里的Flash大小还没改。IROM1 Size还是0x10000,而你的工程因为用了RCT6可能编译出来的代码量更大,自然就超了。把IROM1 Size改成0x40000,IRAM1 Size改成0xC000,重新编译,基本能解决。

类似的还有“L6283E: RW area ... exceeds boundary”,这通常是RAM大小没改。你把Target里的IRAM1改大之后,这种错误也会消失。

5.4 其他几个容易混的坑

有一个坑和芯片关系不大,但切换型号后特别容易出现:Device Pack版本太旧。老工程用旧版CMSIS Pack,在Keil新版本里打开会提示out of date,不更新的话,编译可能报一堆奇奇怪怪的错误。建议到Pack Installer里把STM32F1系列DFP更新到新版本,同时把“C/C++”里的C99 Mode打开。很多隐式声明的报错,开了C99就没了。

还有一个是调试器设置。换了芯片型号之后,Debug选项卡里的调试器有时候会被重置。比如你原来用DAP,打开调试时却提示“No ULINK Device found”,一看原来是Device切回去没把调试器选对。重新在Debug下拉框里选回你的调试器,再进Settings确认一下,问题就没了。

5.5 常见问题速查表

现象主要原因解决办法
下载失败/Flash算法错误下载算法未改为High-densityDebug->Settings->Flash Download换成High-density 256K
编译提示启动文件找不到工程还引用MD启动文件替换为startup_stm32f10x_hd.s
外设中断不响应启动文件中断向量没有入口换HD启动文件
链接超ROM/RAMTarget内存范围没改IROM1改成0x40000,IRAM1改成0xC000
SPL库Flash操作异常宏仍是STM32F10X_MD宏改为STM32F10X_HD
HAL库无法使用UART4/5宏仍是STM32F103xB宏改为STM32F103xE
Debug报No ULINK Device调试器设置被重置Debug选项卡里重新选择对应调试器

6. 转换过一次后我的几点体会

这种“换大芯片”的操作,我做过不止一次。第一次以为只要改一下CPU型号就行,结果在Flash算法和启动文件上卡了小半天。后来学乖了,每次做迁移前,都会先备份一份原工程,然后把启动文件、宏、Target内存、下载算法这四个点按顺序过一遍,全部确认无误再编译。

还有一个经验是,换到RCT6之后,不要急着把C8T6时代为了省资源而写的“抠门代码”全部重写。先跑通原来的业务逻辑,再逐步启用新外设。比如原来用软件定时器节省硬件定时器,现在有了TIM5、TIM6、TIM7,可以把软件逻辑迁回硬件定时器,但一次只迁一个功能,测稳了再迁下一个。

最后分享一个能提高效率的小技巧:如果你是用标准外设库写的工程,切换后想用UART4/5这些新串口,别从零写驱动。先翻一下官方标准外设库里针对High Density芯片的示例代码,把GPIO配置和USART初始化代码复制过来,再根据实际引脚改一下复用映射,基本半小时就能跑通。毕竟RCT6的资源比C8T6宽裕得多,官方例程覆盖的外设也更全,站在这些现成代码上改,比硬自己搭框架省心不少。

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

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

立即咨询