完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
电子发烧友论坛|
本帖最后由 eggeggice 于 2015-3-30 11:00 编辑
前几个月一直忙,也没有时间学ARM,这几天花了点时间看了苏大的教材和视频,学到了不少东西,对单片机工作的原理也有了新的认识,所以来论坛分享下。 在用keil新建工程的时候,编译器会提示是否将startup_MKL25Z4.s文件复制到工程中,选择确定,这样新建的工程就会拥有第一个文件。 这个文件是什么,有什么作用,我一直没搞懂。直到最近学了一点汇编,然后配合万能的度娘,才慢慢了解了这个文件的作用。 以下针对的是1.4版本的startup_MKL25Z4.s文件,不同版本的启动文件会有一些微小区别,但是基本内容应该是相同的。 首先说一下KL25Z上电复位开始工作的一些基本知识:上电复位后,单片机的第一个动作是从地址0x0000_0000(flash)处读取一个值,作为堆栈指针SP的复位值;第二个动作是偏移四个字节,从0x0000_0004处读取一个值,作为程序指针PC的复位值;之后单片机用PC指针中的值(也就是存在flash的0x0000_0004处的值)作为地址,找到该地址对应的程序代码,执行第一条指令也就是复位指令。 然后说几个基本的汇编知识: 1、分号在汇编里表示注释,类似于//在c语言里的作用 2、汇编每一行只有一句代码,基本格式是“标号 操作码 操作数 操作数 ...” 以第一行有效代码为例,
其中Stack_Size表示这一行的标号,一般是用来定位代码,有点类似C语言的goto(其实c语言的goto和子程序就是用这些标示实现的)。但是这里的标号因为后面跟着的是伪指令EQU,所以这里表示别的意思。 伪代码EQU有点类似于C语言里的#define(也是用来编译的,编译完了就没了,不占用内存空间)。所以这句话的意思用C理解就是
大家碰到具体的指令可以去百度,比如AREA新建一段空间,SPACE初始化为0,EXPORT是声明一个可以用EXTERN引用的全局标号(类似于C的外部变量)。其实汇编也没有那么难。 阅读这个文件可以了解到,文件实现了下列功能 1、配置了堆和栈,包括指定堆和栈的大小(EQU),定义段(AREA)并定义分配了空间,初始化堆和栈为0(SPACE和DCD),保持堆栈8字节对其(PRESERVE8,意思差不多就是SP的最低三位是0,所以sp指针每次加一就相当于加了8;SP默认是四字节对奇,4*8=32,很容易理解的)。 2、指示编译器为THUMB指令 3、给出了复位上电后应该首先访问的地址,
4、声明了几个用来定位中断向量的全局标号,并给中断向量表赋了初值。可以看出,向量表第一个32位地址储存栈顶,第二个储存pc复位值(文件后面给出了该值对应的地址的内容),第三个是非屏蔽中断,第四个是硬件故障中断,等等。
5、一些对片上flash的配置指令 6、定义代码段(之前都是数据段)
7、第一段代码就是mcu上电复位的操作。这段代码非常重要,代码将从这里开始运行。 一句一句地讲,如果有问题请指正:
Reset_Handler:标号,标志上电复位指令的开始。 PROC:即FUNCTION,标函数的开始(这里是复位函数)。
EXPORT Reset_Handler:声明可全局引用的标号Reset_Handler。 [WEAK]:声明这个标号是弱的,如果别的地方存在对Reset_Handler的其他声明(且没有WEAK声明),那么编译器将用那个标号声明的内容作为上电复位函数的内容,这个标号的内容就被抛弃了。(是这样吗?)
引用别的文件中的全局标号SystemInit,因为后面没有跟[WEAK],所以这个引用是强制的。如果没有在别的文件定义这个标号,那么编译器就会报错,我第一次编译程序就被这个问题折腾了很久:
方法有很多,比如在程序的某个地方将这个函数(C语言的函数名就对应汇编里的一个标号)用一个空函数定义出来:
接下来的一行代码:
与上一句类似,引用了一个其他文件的全局标号__main。很明显表示这个标志是通向主函数的。为什么前面要加两个下划线,这个肯定是有原因的。我百度了一下,__main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()函数。我们也能直接进入主函数,但是那样就需要自己做一些初始化堆栈之类的工作了。
这里的LDR是作为伪指令使用的。LDR还能做加载指令,是用来传数据的。这里的操作数前面有等号,说明是伪指令。LDR作伪指令有两种用法,一种是将直接数0x123存入寄存器
另一种就和这里一样,等号后面是个标示符,意思是将SystemInit的地址存入寄存器R0。
BLX是跳转指令,有四条跳转指令:B,简单的跳转; BL,跳转之前将PC指针存入R14,可以在稍后将R14值重新写入PC寄存器,继续原来的代码执行; BX,跳转,同时指令集发生变化; BLX,跳转,同时指令集发生变化,PC指针入R14寄存器。 因为SystemInit指向了系统初始化函数,上面两条指令合起来的意思就是调用系统初始化函数SystemInit()。至于SystemInit函数需要执行的具体内容,应该和具体的项目有关,我现在也不懂,相信学着学着慢慢就能理解了。
这两句话的意思就是转向__main()函数。上面已经提到了,__main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()函数。所以这里的代码其实不需要再返回了,所以用的是BX指令。 下一句标志代码执行结束。
8、接下来的代码是其他中断的定义。默认是空的。不过我不理解的是为什么硬件错误中断与众不同地跟了一个斜线标志。希望有大神能帮我解答。
9、最后是用户初始化堆栈函数,将堆栈的一些信息存入R0、R1、R2、R3,并跳转到寄存器LR的地址(BX LR即MOV PC, LR)。而LR保存的是进入初始化堆栈子程序前的PC值,即调用结束。 如果用的是小库(micro lib,在target options里面可以设置,能加快一些小程序的运行),则只会给几个关于堆栈的标号全局声明。 |
|
|
相关推荐
|
|
只有小组成员才能发言,加入小组>>
2139个成员聚集在这个小组
加入小组我的项目我做主,使用GN+Ninja来完成构建系统(VSCode开发RT106X)
37409 浏览 0 评论
NXP IMX8应用处理器快速入门必备:技巧、使用、设计指南
6802 浏览 1 评论
7831 浏览 1 评论
7665 浏览 0 评论
NXP i.MX6UL开发板(linux系统烧录+规格+硬件+模块移植)使用手册
5089 浏览 0 评论
1584浏览 2评论
关于NINA-W132 Wi-Fi模块SPI通信遇到的疑问求解
1224浏览 2评论
如何在MPC PowerPC MCU上首次刷写后禁用BDM?
769浏览 1评论
将HFREFR和LFREFR寄存器值设置为错误的值来将故障注入CMU,但CMU_ISR值始终为零,为什么?
760浏览 1评论
将SPSDK for FRDM-MCX-W71 SDK与VS一起安装时出现hidapi构建错误怎么解决?
701浏览 1评论
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-14 09:38 , Processed in 0.625606 second(s), Total 39, Slave 31 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖