目录
2.KEIL 中代码中的中文显示正常,但复制出来后就显示乱码.
3.1 解释1(CODE、RO、RW、ZI Data域及堆栈空间)
3.2 解释2(CODE、RO、RW、ZI Data域及堆栈空间)
1.KEIL使用教程
https://blog.csdn.net/ybhuangfugui/article/details/51510482
2.KEIL 中代码中的中文显示正常,但复制出来后就显示乱码.
根据情况修改编码格式
3. MDK的编译过程及文件类型全解
这章的目录,详见野火官网、文档:《[野火]i.MX RT库开发实战指南——基于i.MXRT1052》
https://doc.embedfire.com/mcu/i.mxrt/i.mxrt1052/zh/latest/doc/chapter39/chapter39.html?highlight=scf#
39. MDK的编译过程及文件类型全解
39.1. 编译过程
39.2. 程序的组成、存储与运行
39.3. 编译工具链
39.4. MDK工程的文件类型
39.5. sct分散加载文件的格式与添加方式
39.6. 通过修改分散加载文件实现定制存储空间
39.7. Keil版本工程模板分散加载文件简述
3.1 解释1(CODE、RO、RW、ZI Data域及堆栈空间)
说明1:
以下内容摘录自《[正点原子]STM32F4开发指南-HAL库版本_V1.2.pdf》167页
可以看到没有错误,也没有警告。 从编译信息可以看出,我们的代码占用 FLASH 大小为:4384 字节(3942+442),所用的 SRAM 大小为: 1920 个字节(1896+24)。这里我们解释一下,编译结果里面的几个数据的意义:
Code:表示程序所占用 FLASH 的大小(FLASH)。
RO-data:即 Read Only-data, 表示程序定义的常量(FLASH)。
RW-data:即 Read Write-data, 表示已被初始化的变量(SRAM)。
ZI-data:即 Zero Init-data, 表示未被初始化的变量(SRAM)有了这个就可以知道你当前使用的 flash 和 sram 大小了,所以,一定要注意的是程序的大小不是.hex 文件的大小,而是编译后的 Code 和 RO-data 之和。接下来, 大家就可以下载验证了。
如果有 STLINK,则可以用 STLINK 进行在线调试(需要先下载代码),单步查看代码的运行, STM32F4 的在线调试方法介绍,参见: 3.4.2 节。
说明2:RT-Thread 程序内存分布 中有介绍关于KEIL 编译RT-Thread 后的Flash RAM 分布
一般 MCU 包含的存储空间有:片内 Flash 与片内 RAM,RAM 相当于内存,Flash 相当于硬盘。编译器会将一个程序分类为好几个部分,分别存储在 MCU 不同的存储区。
Keil 工程在编译完之后,会有相应的程序所占用的空间提示信息,如下所示:
linking...
Program Size: Code=48008 RO-data=5660 RW-data=604 ZI-data=2124
After Build - User command \#1: fromelf --bin.\\build\\rtthread-stm32.axf--output rtthread.bin
".\\build\\rtthread-stm32.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed: 00:00:07
上面提到的 Program Size 包含以下几个部分:
1)Code:代码段,存放程序的代码部分;
2)RO-data:只读数据段,存放程序中定义的常量;
3)RW-data:读写数据段,存放初始化为非 0 值的全局变量;
4)ZI-data:0 数据段,存放未初始化的全局变量及初始化为 0 的变量;
编译完工程会生成一个. map 的文件,该文件说明了各个函数占用的尺寸和地址,在文件的最后几行也说明了上面几个字段的关系
Total RO Size (Code + RO Data) 53668 ( 52.41kB)
Total RW Size (RW Data + ZI Data) 2728 ( 2.66kB)
Total ROM Size (Code + RO Data + RW Data) 53780 ( 52.52kB)
1)RO Size 包含了 Code 及 RO-data,表示程序占用 Flash 空间的大小;
2)RW Size 包含了 RW-data 及 ZI-data,表示运行时占用的 RAM 的大小;
3)ROM Size 包含了 Code、RO-data 以及 RW-data,表示烧写程序所占用的 Flash 空间的大小;
程序运行之前,需要有文件实体被烧录到 STM32 的 Flash 中,一般是 bin 或者 hex 文件,该被烧录文件称为可执行映像文件。
如下图左边部分所示,是可执行映像文件烧录到 STM32 后的内存分布,它包含 RO 段和 RW 段两个部分:其中 RO 段中保存了 Code、RO-data 的数据,RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在映像文件中。STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零。
其中动态内存堆为未使用的 RAM 空间,应用程序申请和释放的内存块都来自该空间。
如下面的例子:
rt_uint8_t* msg_ptr;
msg_ptr = (rt_uint8_t*) rt_malloc (128);
rt_memset(msg_ptr, 0, 128);//复制错误复制成功
代码中的 msg_ptr 指针指向的 128 字节内存空间位于动态内存堆空间中。
而一些全局变量则是存放于 RW 段和 ZI 段中,RW 段存放的是具有初始值的全局变量(而常量形式的全局变量则放置在 RO 段中,是只读属性的),ZI 段存放的系统未初始化的全局变量,如下面的例子:
#include <rtthread.h>
const static rt_uint32_t sensor_enable = 0x000000FE;
rt_uint32_t sensor_value;
rt_bool_t sensor_inited = RT_FALSE;
void sensor_init()
{
/* ... */
}//复制错误复制成功
sensor_value 存放在 ZI 段中,系统启动后会自动初始化成零(由用户程序或编译器提供的一些库函数初始化成零)。sensor_inited 变量则存放在 RW 段中,而 sensor_enable 存放在 RO 段中。
3.2 解释2(CODE、RO、RW、ZI Data域及堆栈空间)
https://doc.embedfire.com/mcu/i.mxrt/i.mxrt1052/zh/latest/doc/chapter39/chapter39.html?highlight=scf
CODE、RO、RW、ZI Data域及堆栈空间
在工程的编译提示输出信息中有一个语句“Program Size:Code=xx RO-data=xx RW-data=xx ZI-data=xx”,它说明了程序各个域的大小,编译后,应用程序中所有具有同一性质的数据(包括代码)被归到一个域,程序在存储或运行的时候,不同的域会呈现不同的状态,这些域的意义如下:
- Code:即代码域,它指的是编译器生成的机器指令,这些内容被存储到ROM区。
- RO-data:Read Only data,即只读数据域,它指程序中用到的只读数据,这些数据被存储在ROM区,因而程序不能修改其内容。例如C语言中const关键字定义的变量就是典型的RO-data。
- RW-data:Read Write data,即可读写数据域,它指初始化为“非0值”的可读写数据,程序刚运行时,这些数据具有非0的初始值,且运行的时候它们会常驻在RAM区,因而应用程序可以修改其内容。例如C语言中使用定义的全局变量,且定义时赋予“非0值”给该变量进行初始化。
- ZI-data:Zero Initialie data,即0初始化数据,它指初始化为“0值”的可读写数据域,它与RW-data的区别是程序刚运行时这些数据初始值全都为0,而后续运行过程与RW-data的性质一样,它们也常驻在RAM区,因而应用程序可以更改其内容。例如C语言中使用定义的全局变量,且定义时赋予“0值”给该变量进行初始化(若定义该变量时没有赋予初始值,编译器会把它当ZI-data来对待,初始化为0);
- ZI-data的栈空间(Stack)及堆空间(Heap):在C语言中,函数内部定义的局部变量属于栈空间,进入函数的时候从向栈空间申请内存给局部变量,退出时释放局部变量,归还内存空间。而使用malloc动态分配的变量属于堆空间。在程序中的栈空间和堆空间都是属于ZI-data区域的,这些空间都会被初始值化为0值。编译器给出的ZI-data占用的空间值中包含了堆栈的大小(经实际测试,若程序中完全没有使用malloc动态申请堆空间,编译器会优化,不把堆空间计算在内)。
3.3 程序的存储与运行
RW-data和ZI-data它们仅仅是初始值不一样而已,为什么编译器非要把它们区分开?这就涉及到程序的存储状态了,应用程序具有静止状态和运行状态。静止态的程序被存储在非易失存储器中,如RT1052的外部FLASH,因而系统掉电后也能正常保存。但是当程序在运行状态的时候,程序常常需要修改一些暂存数据,由于运行速度的要求,这些数据往往存放在内存中(RAM),掉电后这些数据会丢失。因此,程序在静止与运行的时候它在存储器中的表现是不一样的,见图 39‑5。
图 39‑5 应用程序的加载视图与执行视图
图中的左侧是应用程序的存储状态,右侧是运行状态,而上方是RAM存储器区域,下方是ROM存储器区域。
程序在存储状态时,RO节(RO section)及RW节都被保存在ROM区。当程序开始运行时,内核直接从ROM中读取代码,并且在执行主体代码前,会先执行一段加载代码,它把RW节数据从ROM复制到RAM, 并且在RAM加入ZI节,ZI节的数据都被初始化为0。加载完后RAM区准备完毕,正式开始执行主体程序。
编译生成的RW-data的数据属于图中的RW节,ZI-data的数据属于图中的ZI节。是否需要掉电保存,这就是把RW-data与ZI-data区别开来的原因,因为在RAM创建数据的时候,默认值为0,但如果有的数据要求初值非0,那就需要使用ROM记录该初始值,运行时再复制到RAM。
STM32的RO区域不需要加载到SRAM,内核直接从FLASH读取指令运行。计算机系统的应用程序运行过程很类似,不过计算机系统的程序在存储状态时位于硬盘,执行的时候甚至会把上述的RO区域(代码、只读数据)加载到内存,加快运行速度,还有虚拟内存管理单元(MMU)辅助加载数据,使得可以运行比物理内存还大的应用程序。而STM32没有MMU,所以无法支持Linux和Windows系统。
当程序存储到RT1052芯片的外部部FLASH时(即ROM区),它占用的空间是Code、RO-data及RW-data的总和,所以如果这些内容比RT1052芯片的FLASH空间大,程序就无法被正常保存了。当程序在执行的时候,需要占用内部SRAM空间(即RAM区),占用的空间包括RW-data和ZI-data。应用程序在各个状态时各区域的组成见表 39‑2。
在MDK中,我们建立的工程一般会选择芯片型号,选择后就有确定的FLASH及SRAM大小,若代码超出了芯片的存储器的极限,编译器会提示错误,这时就需要裁剪程序了,裁剪时可针对超出的区域来优化。
4.KEIL编译速度优化
优化1:
如下图,去掉勾选可以改善编译速度,但是会影响写代码时的自动引用,和goto 跳转到函数或者宏的功能。
优化2:
这里改成 6,建议在工程一开始就选择,否则中途变更可能出现 5 能编译通过,6会出现一些奇怪问题。
5.KEIL中将输入的TAB转为空格设置
keil中将输入的TAB转为空格edit->configuration 下方有Tab size,默认4c/c++ file 下 勾选 insert spaces for tab
6.KEIL 中文警告,代码中有出现中文编译会出现警告
最近开发的项目工程一般都是用AC6编译器,好处是编译速度快,优化等级高,但是AC6对于中文支持不好,项目文件目录一定不要有中文路径,否则不能使用gotodef跳转功能。
如果项目内有使用中文字符串,则AC6编译过程会弹出警告信息,虽然不影响使用,但是十分烦人。
可以填入 -Wno-invalid-source-encoding 将该警告信息屏蔽。