最近项目上需要用到STM32F103VET6芯片。之前一直使用的是8年前的库,决定更新为最新版的固件库。在建立新工程编译时出现了以下错误:“..\OBJ\NH3N STM32.sct(7): error: L6236E: No section matches selector - no section to be FIRST/LAST.”
2.1 问题定位
错误出现在“xxxx.sct”文件,sct文件,全名scatter file,中文名分散加载文件,是ARM程序链接时的输入参数。默认设置下,Keil会自动生成.sct文件。出错的的“NH3N STM32.sct”文件就是keil自动生成的。
2.2 分散加载机制(sct文件)
分散加载机制允许为链接器指定映像的存储器映射信息,可实现对映像组件分组和布局的全面控制。分散加载区域分两类:
加载区:该映像文件开始运行前存放的区域,即当系统启动或加载时应用程序存放的区域。
执行区:映像文件运行时的区域,即系统启动后,应用程序进行执行和数据访问的存储器区域。
2.3 本工程sct文件分析
对于本工程生成的.sct文件来说。
6 ER_IROM1 0x08000000 0x00080000 { ; load address = execution address
加载区,指定了程序映像在存储区存放的起始地址:0x08000000,总共的大小为0x00080000。这与图3中的存放代码的Flash区域地址一致。上电后,从此地址处加载代码。
11 RW_IRAM1 0x20000000 0x00010000 { ; RW data
执行区,指定了运行时临时存放代码的地方。起始地址:0x20000000,总共的大小:0x00010000。这与图3中的SRAM区域地址一致。上电后,程序在此区域内运行。
上边说了Keil在编译工程时会自动生成.sct文件。那么Keil是怎样知道加载区(ROM)起始地址以及执行区(RAM)起始地址的呢?
其实我们新建工程时第一步就是选择芯片的具体型号,Keil会根据我们设置的芯片型号加载默认的ROM、RAM起始地址和存储区大小。
在“Options for Target…”的“Target”选项卡可以看到默认的设置。下图即是我的工程keil的默认设置。从图中可知IROM1就是程序映像存储区参数,IRAM1就是运行时临时代码存放区参数。两个名称前边都加了“I”,代表内部存储区的意思,对应图标“on-chip”。如果系统有外部扩展的ROM和RAM,还需要在“off-chip”对应的区域设置存储区起始地址和大小。“Startup”单选框用于选择系统启动起始区域。通过和生成的.scf文件对比,此默认设置和.sct文件中的代码表述一致。
另外,可以设置多个IROM、ROM、IRAM和RAM区,这和.sct文件支持多个不连续的加载区和运行区相对应。但是启动加载区只能选择一个。
报错的代码行是:
7 *.o (RESET, +First)
这一行代码指定了启动代码的首次执行地址。RESET标号表示的地址就是启动地址。其中First属性符表示把RESET代表的代码放在最开始处,也就是指定RESET代表的地址为启动地址。
2.4 报错原因
报错信息中的“no section to be FIRST/LAST”也就是说没有找到FIRST或者LAST对应的区域,也就是说没有找到RESET标号对应的代码。RESET标号对应的代码也就是单片机复位之后执行的代码。因此可以判断keil没有找到STM32的启动文件。
3.1 启动文件存放位置
通过以上的分析,找到STM32的启动文件,并加载到工程中就可以解决问题了。
在官方库文件中可以找到启动文件,文件存放路径:STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm。
在此路径下总共有8个启动文件。
3.2 启动文件适用环境
这8个启动文件分别适用于什么场合?对于本工程应该选择哪一个启动文件?
仔细查看可知这8个文件的文件名都是startup_stm32f10x_xx.s 格式的。这些启动文件分别适用于不同类型及flash大小的器件。
文件名 | 适用类别 | 适用型号 |
---|---|---|
startup_stm32f10x_cl.s | 互联型的器件 | STM32F105xx,STM32F107xx |
startup_stm32f10x_hd.s | 大容量器件 | STM32F101xx,STM32F102xx,STM32F103xx |
startup_stm32f10x_hd_vl.s | 大容量器件 | STM32F100xx |
startup_stm32f10x_ld.s | 小容量器件 | STM32F101xx,STM32F102xx,STM32F103xx |
startup_stm32f10x_ld_vl.s | 小容量器件 | STM32F100xx |
startup_stm32f10x_md.s | 中容量器件 | STM32F101xx,STM32F102xx,STM32F103xx |
startup_stm32f10x_md_vl.s | 中容量器件 | STM32F100xx |
startup_stm32f10x_xl.s | Flash在512K到1024K字节器件 | STM32F101xx,STM32F102xx,STM32F103xx |
3.3 STM32 内部Flash容量等级划分规则
那么STM32内部Flash是按照什么样的标准划分大、小、中容量的?
每种系列的器件的划分标准是不太一样的。可能从芯片数据手册中找到划分标准。对于STM32F103xx系列的划分标准如下图所示。
3.4 找到合适的启动文件
由数据手册中的选型部分可知STM32F103VET6的Flash大小为512Kbytes,属于大容量系列,因此应该选择启动文件:startup_stm32f10x_hd.s 。
使用notepad++打开此启动文件,启动文件采用汇编语言编写,主要完成系统底层初始化并找到程序main函数执行入口。在启动文件中可以找到如下代码:
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
可以看到这就是.scf文件要找的RESET标号对应的代码。
3.5 往工程中添加启动文件
(以下操作以我自己的工程为例)
把启动文件startup_stm32f10x_hd.s拷贝到工程文件里的CMSIS文件夹里。然后在keil工程树中右击“CMSIS”,在弹出菜单中选择“Manage Project Items”,在弹出的“Manage Project Items”窗口中,在“CMSIS”group中点击“Add Files…”添加刚才的拷贝的启动文件。
3.6 编译验证
【参考】
1、Keil sct分散加载文件 http://blog.csdn.net/kobesdu/article/details/38258449
2、关于arm启动代码的启动流程 https://www.cnblogs.com/blackeyes/articles/4742264.html
3、STM32F10x 启动代码文件选择 https://wenku.baidu.com/view/263f7f5c0066f5335b81215f.html