完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
程序的编译分为四个步骤:预处理、汇编、编译、链接。在开发STM32时,我们只要在IDE中点击编译就能一次性完成这4个步骤,实际上IDE也是要经过这些步骤的,只不过IDE为我们屏蔽了很多细节。
首先我们需要了解一个image文件的构成。image即编译的产物,我们编译STM32生成的bin文件此处称之为image。一个image文件由RO段和RW段组成,RO段包含只读的代码段和常量,RW段包含可读可写的全局变量和静态变量。因为程序刚运行时,RW段还在FLASH中,需要一段程序将这些变量复制到RAM中,STM32的启动文件的__main函数帮我们完成了这一动作。RW段中初始值为0的段为ZI段,image文件无需包含ZI段,因为ZI段包含的是全局或静态初始值为0的变量,只要在程序运行后,将对应的RAM区域清零即可。 这里又涉及到另一个概念:加载地址和运行地址。加载地址是指读取程序的地址,运行地址是指程序运行的入口地址。STM32因为有XIP(executed in place)技术,加载地址和运行地址是一样的,都是0x08000000。简而言之,如果程序在FLASH中运行,加载地址和运行地址是相同的,例如STM32等单片机;如果程序存放在FLASH里,而运行是在RAM里,那么加载地址指向FLASH,运行地址指向RAM,例如跑Linux系统的一些芯片。上面的RW段,其加载地址指向FLASH,而运行地址指向RAM,因此需要拷贝。 如何指定各个C文件的编译产物(.o格式)在RO段的顺序?又如何确定程序的加载地址和运行地址呢?这都是靠一个脚本来完成的,即链接脚本。在Linux下,链接脚本为lds文件;在KEIL中,链接脚本为sct文件;在IAR中,链接脚本为icf文件。本文以KEIL下的sct文件为例,讲解链接脚本结构。 我们可以通过编写一个分散加载文件来指定 ARM 连接器在生成映像文件时如何分配 Code、RO-Data, RW-Data, ZI-Data 等数据的存放地址。称为分散加载文件实际上就是链接脚本,如果不修改KEIL的链接脚本,那么会使用默认的链接脚本,我们按照下图的操作方式来查看默认的链接脚本,方法为点击工程设置,找到Link选项,去掉“Use Memory Layout from Target Dialog”前面的勾选,然后点击Edit。 不使用KEIL的默认链接脚本 查看到的默认链接脚本如下,注:本例中使用的MCU型号为STM32F103RC,FLASH容量为256KB,RAM大小为64KB。 ; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* LR_IROM1 0x08000000 0x00040000 { ; 加载时域起始地址为0x08000000,大小为0x40000 ER_IROM1 0x08000000 0x00040000 { ; 第一个运行时域,运行地址为0x08000000,大小为0x40000 *.o (RESET, +First) ; RESET段最先链接,RESET段在启动文件中有声明 *(InRoot$$Sections) ; 链接__main函数,该函数用于RW段数据的拷贝和ZI段数据的清零 .ANY (+RO) ; 剩余的code、RO数据随意链接 } RW_IRAM1 0x20000000 0x0000C000 { ; 第二个运行时域,运行地址为0x20000000,大小为0xC000 .ANY (+RW +ZI) ; 存放所有的RW段数据和ZI段数据 } } 分散加载文件主要由一个加载时域和多个运行时域组成。 加载时域,顾名思义用于加载并存储数据,包括 Code、 RO-Data 和 RW-Data。 运行时域, 用于为运行时分配变量及代码映射空间, 包含 Code、 ZI-Data、 RW-Data。 分散加载文件的组成 分散加载有3条规则需要特别注意: 1、第一个运行时域的基址必须与加载域基址相同。 2、第一个运行时域存放的代码不会进行额外拷贝。 3、一个加载时域,有且仅有一个不拷贝的运行时域,FIXED关键字修饰除外。 分散加载的用途有很多,例如: 1、我们可以通过修改分散加载文件将部分代码或整个代码放到RAM中运行以提高运行速度。 2、可以将一组函数放在特定地址上,作为Firmware供app程序调用。 3、将程序分成boot和app,实现升级功能。实现这个功能可以不修改分散加载文件,直接在keil里设置即可。 4、定义section来灵活地存放特定的数据。 关于分散加载的更多知识,可以参考周立功写的一篇文档《keil分散加载文件浅释》,我已经传到百度网盘: 链接:https://pan.baidu.com/s/1heC-pLmi_eeqS_SU19dmIA 提取码:iguq 有时候我们需要在程序运行时知道各个段的起始地址、结束地址、大小等信息,这些信息链接器已经帮我们导出了,下面给出了一个使用的例子,这个例子实际上完成了__main的部分功能,即把FLASH中的RW段数据拷贝到RAM的运行地址上,并将RAM中的ZI段数据清零。 void RW_And_ZI_Init (void) { extern unsigned char Image$$ER_IROM1$$Limit; // 获取RW段在FLASH中的加载地址 extern unsigned char Image$$RW_IRAM1$$Base; // 获取RW段在RAM中的运行地址 extern unsigned char Image$$RW_IRAM1$$RW$$Limit; // 获取RW段在RAM中的结束地址 extern unsigned char Image$$RW_IRAM1$$ZI$$Limit; // 获取ZI段在RAM中的结束地址 unsigned char * psrc, *pdst, *plimt; psrc = (unsigned char *)&Image$$ER_IROM1$$Limit; pdst = (unsigned char *)&Image$$RW_IRAM1$$Base; plimt = (unsigned char *)&Image$$RW_IRAM1$$RW$$Limit; while(pdst < plimt) // 将FLASH中的RW段拷贝到RAM的RW段运行地址上 { *pdst++ = *psrc++; } psrc = (unsigned char *)&Image$$RW_IRAM1$$RW$$Limit; plimt = (unsigned char *)&Image$$RW_IRAM1$$ZI$$Limit; while(psrc < plimt) // 将RAM中的ZI段清零 { *psrc++ = 0; } } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1614 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1541 浏览 1 评论
970 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
682 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1592 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
644浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
515浏览 3评论
531浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
504浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-21 23:21 , Processed in 0.662351 second(s), Total 79, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号