Stm32单片机开发KEIL启动文件汇编语言详解
文章目录
简介 启动文件汇编代码相关指令 堆栈空间的定义 初始化中断向量表 复位中断函数 中断函数的弱(WEAK)声明 用户栈和堆初始化简介
我们在做单片机编程的时候,大部分都是用KEIL自带的启动文件来使程序进入C语言main函数,然后进行C语言编程开发的工作。那么这个启动文件到底做了什么呢?相信朋友们肯定和我一样好奇,想弄明白启动文件到底都干了些什么。那么本文就来介绍下,本文介绍stm32启动文件汇编代码,对应文件名startup_stm32f10x_hd.s。其他Cortex-M3内核的单片机都是大同小异的。
其实启动文件存在的目的就是构建可以供C语言代码运行的工作环境,比如传递参数时需要的栈空间初始化,动态分配内存时的堆初始化,一些初始化为0的变量空间的初始化等等。如果这些没有配置好,无法达到C语言代码运行的工作环境,那么后面的C语言代码执行的结果就是不对的,也会导致总个系统无法工作。所以启动文件很重要,也正是因为我们觉得它重要,所以才想搞懂它。
startup_stm32f10x_hd.s启动文件中的汇编代码主要做了下面5个工作。
1.堆栈空间的定义;
2.初始化中断向量表;
3.复位中断函数(Reset_Handler){系统初始化,然后进入main函数};
4.中断函数的弱(WEAK)声明
5.用户栈和堆初始化
再介绍这5个部分的详细代码前,这里已经先总结了启动文件中用到的汇编代码与编译器相关的指令,下面就先来介绍下这些指令。
启动文件汇编代码相关指令
启动文件代码主要由ARM指令代码和与编译器相关的汇编指令组成,下表罗列了启动文件中用到的相关汇编指令。
上面只是对指令做了简要的说明,后面代码用到时我们再一一讲解。我们也可以通过在代码中选中指令,按F1按键调出帮助说明,查看具体指令的相关介绍。
接下来我们就对代码做详细分析吧。
一、堆栈空间的定义
首先这里使用指令EQU定义了一个数值常量符号Stack_Size指明栈大小为0x00000400即1K,这个值可以根据实际需求更改。使用QEU定义常数,类似与C语言的#define定义常数。
然后这里又使用指令AREA定义了一个未初始化,可以读写并要求8字节边界对齐的段,段名,为STACK。这里可以为段选择任何段名。但是,以一个数字开始的名称必须包含在竖杠号内,否则会产生一个缺失段名错误。例如, |1_DataArea|。有些名称是习惯性的名称。例如,|.text| 用于表示由 C 编译程序产生的代码段,或用于以某种方式与 C 库关联的代码段。这里的NOINIT表示数据段是未初始化的或初始化为零。其只包含零初始化的空间保留命令 SPACE 或 DCB, DCD, DCDU, DCQ, DCQU, DCW 或 DCWU 。可以决定在链接时 AREA 是未初始化的,还是零初始化的,后面一条指令是SPACE,所以这里要初始化为0,READWRITE指明段可以读写,ALIGN=3指明段要在2^3=8字节边界上对齐。
再然后使用SPACE定义了一个初始化为0的存储块Stack_Mem,可以理解为存储块是归别到段里的。标号__initial_sp紧挨着SPACE语句放置,表示栈的结束地址,即栈顶地址,栈是由高向低生长的。
同样的堆也是这样定义的,只是这里先是指明了堆开始__heap_base(堆起始地址),再指明堆存储块,最后指明__heap_limit(堆终止地址)。堆是由低向高生长的,跟栈的生长方向相反。堆主要用来动态内存的分配,像malloc()函数申请的内存就在堆上面。这里堆默认大小为0x00000200即512字节,一般的程序中我们很少用到malloc函数,所以这里也就不做过多更改,如果要使用malloc函数,需要将此处堆大小定义的值根据需求改大。
后面的PRESERVE8,指明当前文件的堆栈按照8字节对齐。
二、初始化中断向量表
THUMB指示汇编器将随后的指令解释为16位的Thumb指令。Cortex-M3使用的是Thumb-2指令集,是一种介于Thumb指令集和ARM指令集。ARM指令集全部是32位的,Thumb指令集全部是16位的,Thumb-2指令集是即有部分16位Thumb指令的也有部分32位的ARM指令。
后面定义一个只读数据段RESET,用于保存中断向量表,和三个标号__Vectors(向量表开始)、__Vectors_End(向量表结束)和__Vectors_Size(向量表大小)并使用EXPORT指明其具有全局性。这样可以使在其他文件中访问此文件中的这三标号。
DCD 命令分配一个或多个字的存储器,在四个字节的边界上对齐,并定义存储器的运行时初值。
__Vectors DCD __initial_sp ; Top of Stack
这里就指示了段的开始为向量表的开始,标号__Vectors(向量表开始)编译器会根据不同单片机为其指定值,比如stm32单片机就是0x08000000,然后我们定义的RESET段就被分在了0x08000000开始的地址处,其结束位置就是从0x08000000开始依次加4个字节,因为这里每个DCD命令占存储器4个字节,这样一直到__Vectors_End(向量表结束),__Vectors_Size(向量表大小)就是这个RESET段所占大小。比如复位的时候,复位中断来了,就从这个段的第二个存储地址0x08000004处对应的值0x08000144作为复位函数Reset_Handler的地址。
三、复位中断函数
这里先使用AREA定义了一个只读代码段。这里的标号Reset_Handler就代表了复位函数的入口地址(函数名),使用PROC标记函数入口,使用ENDP标记函数结束。
EXPORT Reset_Handler [WEAK]
这里EXPORT声明Reset_Handler是一个全局性的。WEAK表示其他地方没有定义Reset_Handler函数时,就将此处作为Reset_Handler函数的实例。IMPORT用于指示如果在当前汇编代码中未找到其引用,则不导入该名称,很显然,下面有用到__main和SystemInit。
从上那些代码我就就知道,程序上电后,从0x08000000地址处加载SP,上电复位从0x08000004处加载PC,0x08000004处的地址就是复位函数的地址,然后复位函数里面先调用SystemInit函数来初始化系统的各种时钟,再调用__main函数(由编译器实现)。
复位函数中用到的Thumb-2指令介绍如下:
LDR从存储器中加载字到一个寄存器中
BL跳转到由寄存器/标号给出的地址,并把跳转前的下条指令地址保存到LR
BLX跳转到由寄存器给出的地址,并根据寄存器的LSE确定处理器的状态,还要把跳转前的下条指令地址保存到LR
BX跳转到由寄存器/标号给出的地址,不用返回
四、中断函数的弱(WEAK)声明
这里定义了各种中断函数,使用PROC表示函数开始,ENDP表示函数结束,EXPORT说明函数的全局性,WEAK说明如果其他地方没有定义这个函数,那么就把此处作为函数的实例。这里函数的代码都是B . 。这里的B表示跳转到一个标号,这里跳转到一个'.',即表示无限循环,所以我们在写C语言程序时如果没有写中断函数,那么对应的中断来了会运行这里到中断函数,即B .那么将无限循环在此。
ALIGN命令通过用零或空指令NOP填充,来使当前位置与一个指定的边界对齐。使用ALIGN来确保数据和代码对齐到适当的边界上。这里使用了默认为字对齐方式。
五、用户栈和堆初始化
栈和堆初始化部分,这里IF、ELSE、ENDIF是条件编译。
先判断是否定义了__MICROLIB ,如果定义了则赋予标号__initial_sp(栈顶地址)、__heap_base(堆起始地址)、__heap_limit(堆结束地址)全局属性,可供外部文件调用,这样我们使用到molloc函数申请的空间就是从这里有关堆的两个标号之间的内存中申请的。如果没有定义(实际的默认情况就是我们没定义__MICROLIB)则通过 IMPORT __use_two_region_memory 表明使用双段模式,即一部分储存区用于栈空间,其他的存储区用于堆空间,堆区空间可以为0,但是,这样就不能调用malloc()内存分配函数。然后使用__user_initial_stackheap标号处的代码用于初始化用户堆栈,这部分由编译器提供的__main来调用。
END 命令指示汇编器,已到达一个源文件的末尾。
关于STM32单片机的Keil启动文件汇编代码就讲解完了,大家有没有看明白呢,欢迎评论交流,如果觉得我这篇文章写到很好到话,就转发出去分享给更多到朋友吧。最后欢迎大家点赞评论转发收藏,跟多好文章欢迎关注我——单片机嵌入式爱好者 。
硬件工程师:单片机编程,要学习汇编,还是学习C语言,我用C语言
首先解释一下什么是单片机。
单片机也被称为微控制器,英文为Microcontroler,它最早被用在工业控制领域。“单片机由芯片内仅有CPU的专用处理器发展而来。最早的设计理念是通过将大量外围设备和CPU集成在一个芯片中,使计算机系统更小,更容易集成进复杂的而对提及要求严格的控制设备当中。”早期的单片机是4位或者8位。其中最成功的是INTEL的8031。
单片机有一个很重要的优势,就是可编程,通过程序可以实现各种各样的逻辑功能,修改更灵活,更能减少硬件成本,尤其单片机已经发展到16位,32位,片上集成了各种丰富的片上资源后,单片机已经渗透到了我们生活的各个领域、各个方面。
编程
控制单片机执行逻辑功能,就需要编程。编程需要用到汇编语言或者是C语言。
1.汇编语言
汇编语言,英文为assembly language,是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符(Mnemonics)代替机器指令的操作码,用地址符号(Symbol)或标号(Label)代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。普遍地说,特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间移植较为困难。早期单片机编程汇编比较普遍。
汇编
2. C语言
C语言是一门通用计算机编程语言,应用广泛。比较汇编,C语言是高级语言,可移植性好,颗结构化编程。使用标准C语言的程序,几乎都可以不作改变移植到不同的微机平台上,对于嵌入式等的微控制芯片,属于标准C语言的部分也很少需要修改,而且程序很容易读懂。
C语言
现在单片机编程,C语言已经很普及了,汇编用的少了,从写代码、读代码的角度来讲,C语言的优势太明显了。
你用C语言,还是用汇编,欢迎评论。更多精彩,请关注我的头条号“玩转嵌入式 ”。谢谢。
精彩推荐:
如何才能成为一名出色的硬件工程师?我有三点建议
搞技术,一定不要眼高手低
对于程序员来说写代码并不是最难的事情!
相关问答
汇编语言 和 单片机 的区别?软件和硬件。单片机是硬件,汇编是语言,是软件。单片机从诞生到现在,经过了将近70年的发展。众所周知,单片机是可以通过编写程序实现产品的功能,这么多年来...
单片机汇编语言 指令中,条件转移指令JBC与JC的区别是什么?JC是判断C进位标志是否为1,为1则跳转到指定位置。JBC是判断可位寻址区域内指定位是否为1,为1则跳转到指定位置,并同时清除该位(置0)。JC是判断C进位标志是否...
51 单片机汇编语言 中的“$”代表什么意思?代表当前的指令所在的地址举个例子来说,在0050H处定义了几个字节数据0050H:01H,02H,03H,04H那么下一条指令的地址应该为0054H,若下一条语句为COUNTEQU$-00...
单片机 C51的 汇编语言 编程pragma只是用于之间潜入asm代码,不是变了相的混合编程技术,它不能直接调用其他文件(注意是文件)中的函数。真正意义上的多模块编程,每个模块之间都...
单片机 1秒钟定时 汇编语言 怎么编?可以有两种方法,设晶振为12M,则第一种方法:用延时的方法实现DELAY:MOVR3,#10;延时子程序,延时1秒D1:MOVR4,#200D2:MOVR5,#250D3:DJ...
单片机 可以用 汇编 讲吗?单片机当然可以用汇编语言编程。实际上,在20年前,单片机的主流开发语言就是汇编语言。大致介绍一下单片机编程语言的历史吧。第一阶段:机器语言单片机最开...
单片机 , 汇编语言 (2)(1)8255的端口地址为:();(2)8253的端口...[最佳回答]8255的端口地址为80H,82H,84H,86H8253的端口地址为90H,92H,94H,96H8259的端口地址为A0H,A2H,8251的端口地址为B0H,B2H...
单片机 三个按键独立控制三个led灯 汇编语言 ?51单片机的IO口接按键,实现外部触发(外部中断,高低电平,上下降沿),触发后再通过IO控制LED的开关即可。51单片机的IO口接按键,实现外部触发(外部中断,高低电平...
单片机汇编语言 中斜杠是什么意思通常表示除法运算符。它用于执行两个数的除法操作,并将结果存储在指定的寄存器或内存位置中。在汇编语言中,除法操作通常使用特定的指令来执行,例如"DIV"。指...
单片机汇编语言 求解(1)把R0的内容送到R1(2)内部RAM10H单元的...[最佳回答]1.mova,r0movr1,a2.mova,10h3.movdptr,#1000hmovxa,@dptrmovr1,a4.movdptr,#2000hmovca,@dptr...