用 IAR C/C++ For ARM 编写嵌入式应用的启动细节

时间:2021-07-17 19:29:43

一个朋友前几天对我说,想使用 ARM11 内核的 MCU 做一样东西,能不能帮他找个支持的开发工具,我记得 IAR 的支持 ARM11 ,就让他试试看,结果这位老兄试了一下严重不适应,问了很多细节,结果我也不太明白用 IAR C/C++ For ARM 编写嵌入式应用的启动细节,所以特地下载了一个 IAR EW For ARM 的学习版研究一下,有 32K 代码限制,不过这不影响我们的学习用 IAR C/C++ For ARM 编写嵌入式应用的启动细节

安装好软件后,先创建一个只有 main 函数的工程,随便选了一款 MCU,编译后模拟运行,居然就直接跳到 main 里面了,这是很奇怪的事情,我对 Keil 的 MDK 要熟悉一点,不管是什么 MCU ,都应该有个硬件初始化的启动文件才对啊?应该先执行初始化硬件、向量表、堆栈等等的工作,然后才能跳转到 main  执行,所以我也晕了。

然后仔细看手册,原来系统有默认设置,模拟仿真时是直接运行 main 处才停下来,将这个默认选项去掉(在项目的 option 里面,debug 栏的 Run to),这样才能真正从复位开始运行。

用 IAR C/C++ For ARM 编写嵌入式应用的启动细节

再次仿真用单步执行,发现在 main 之前,系统做了很多工作,包括硬件初始化、向量设置、堆栈和堆的创建和初始化,但仅仅是在反汇编窗口看到代码,却没有源文件的代码同步显示,奇哉怪哉?拜托!这是我的项目哎,我还没写这些咚咚,你给我瞎添什么代码呀?难怪我的那位老友严重不适应,我也严重不适应用 IAR C/C++ For ARM 编写嵌入式应用的启动细节。 只好耐着性子再看手册,直接用仿真跟踪到的那些标号来搜索,找到了启动这个方面的描述。

原来和 keil 不同,keil 是在创建项目的时候,给你添加一个启动文件,这个文件直接列在项目表上,让你一目了然。而 IAR 使用了“弱函数”的概念,即系统事先定义好一些标号,作为启动的选项。如果你用这些标号编写了自己的启动函数,则编译器就会使用你的启动函数来初始化系统,如果你没有写,则系统有提供默认的函数源代码来帮你完成初始化。

这样的好处是对于嵌入式的初学者来说,先跳过麻烦的硬件初始化,直接让C程序运行起来,就像傻瓜相机,不用调光圈和焦距,直接按下快门也能得到一张说的过去的照片。而且,对专业工程师来说,使用“弱函数”的概念就可以避免傻瓜相机不能细调的尴尬,它提供了你自定义启动的能力,一样可以做到很专业。原来如此,怪咱们没有耐心认真去看手册。用 IAR C/C++ For ARM 编写嵌入式应用的启动细节

不过,在这点上,我认为 keil 要贴心一点,它将默认的启动文件显示地拷贝到项目中,让你比较清楚启动流程。而 IAR 将这点隐藏了,不认真看手册,就会让使用者有些糊涂。不过,这个老外的做事性格有关系,我们风风火火的风格估计老外也没预计到,他们可能认为使用者应该去仔细阅读手册用 IAR C/C++ For ARM 编写嵌入式应用的启动细节

---------------------------------------------------------------------------------------------

下面介绍一下几个启动文件,来自 IAR 手册中的描述:

本章节描述了在运行环境活动期间,你的程序的启动和终止。程序代码的启动和终止是位于源文件 cstartup.s、cmain.s、cexit.s 和 low_level_init.c 中,它们位于arm\ src\ lib目录下。

而对于Cortex-M内核,下列其中一个文件是用来代替 cstartup.s:thumb\cstartup_M.s 或 thumb\cstartup_M.c。有关如何自定义系统启动代码的信息,请参阅76页的 - 自定义系统初始化。

在系统启动过程期间,一个初始化序列会在进入 main 之前执行。这个序列对目标硬件和C/C++ 环境的需要进行必要的初始化。

1、 CPU在复位时跳到程序的入口点:即系统启动代码中的标号 __iar_program_start 处。
 2、异常堆栈指针是初始化到每个相应部分的尾部
 3、堆栈指针是初始化到CSTACK块的尾部
 4、如果你定义了 __iar_program_start 函数,则它被调用,这提供了一个机会来执行早期的初始化。
注意:对于 Cortex-M 设备,上面列表中的第二条是不正确的。第一条和第三条的处理也稍微有点不同。在复位后,CPU初始化PC和SP的值来自于向量表( __vector_table ),它们定义在 cstartup_M.c 文件中。

你可能需要自定义系统的初始化代码。例如,你的应用程序可能需要初始化内存映射特殊功能寄存器(SFRs),或省略掉 cstartup 中默认的数据初始化部分。

为此,您可以提供一个定制版本的程序 __low_level_init,即在 cmain.s 部分被调用之前,对数据部分实施初始化。应当避免直接修改cstartup文件。处理系统启动的代码位于源文件 cstartup.s 和 low_level_init.c 中,在 arm\src\lib 目录下。

产品中提供了两个框架的低级别初始化文件:一个C源文件 low_level_init.c 和一个替代的汇编程序源文件 low_level_init.s。后者是属于预置运行环境的一部分。对使用C源代码版本的唯一限制是在文件中不能使用静态初始化变量,在变量初始化这点上尚未完成。

--------------------------------------------------------------------------------------------------

由上可以看出,在创建一个新项目后,系统就默认使用了上面几个文件,但是并没有显示地添加到项目表中。所以可以将上面几个文件拷贝到你的项目目录中,然后再手工添加到你的项目中,此时就可以修改它们的内容,来定制你自己的初始化流程。注意:千万不要将 IAR 安装目录下的这几个源文件加到项目中,而是必须用拷贝的方式来复制这些文件。免得不小心修改了源文件的内容,导致其它项目使用这些默认文件时产生错误。

上面提到的这些文件在学习版中可能没有 C 语言的版本,反正我没看到,但是提供了一些汇编的版本,这已经够用了。在仔细看完 IAR 的手册之后,感觉这套工具很不错,尤其对学习嵌入式的人来说,这个 IAR 的学习版提供了绝佳的学习环境。而对于我们这些 DIY 爱好者来说,它虽然有 32K 代码的限制,却也能制作出很棒的东西了。