从汇编编程的角度上看,STM32从复位到主程序的过程要比MCS稍显复杂,一个比较主要的原因是CortexM3用很多伪指令来描述启动过程,会让我这样的菜鸟感到云遮雾罩。
说到底,CortexM3也好,MCS51也罢,启动过程都是大同小异的,无非是PC指针初始化,执行完用户的复位程序之后取出主程序地址,跳到主程序上。
对于MCS51来说,这个过程非常好理解:
ORG 0000H
LJMP Reset
ORG 0003H
;配置各个中断服务程序的入口
………………
ORG 0100H
Reset:
………………
;初始化及主程序段
51单片机复位,PC=0000H指向首地址,取指令和操作数,转0100H执行初始化及主程序,先定义堆栈指针SP,然后再干别的。
这个过程说明MCS51程序存储器是这样分区的:
复位入口 0000H-0002H。放置越过中断向量程序段,直达主程序段的跳转指令。
中断向量 0003H-00BBH。放置中断服务程序向量。有些增强型51单片机的中断向量已经用到了00BBH。
复位程序 从最后一个中断向量操作数之后的地址开始。通常是先配置堆栈指针SP。
主程序段 主程序代码区。
但对于CortexM3来说,它的复位过程有点儿特殊:如果BOOT0配置成从FLASH区启动,那首先是PC=0x08000000,先把这个地址开始的4个字节数值赋值给主栈指针MSP,然后再把0x08000004开始的4个字节赋值给PC,转到复位程序。
实际上,复位时PC=0x00000000,但BOOT0=0时,0x00000000会被映射到0x08000000上,所以说PC是从0x08000000开始也是可以的。
CortexM3程序存储器分区是如下:
MSP赋值 0x08000000—0x08000003。当PC指向这个数据块时,4字节数自动赋值给MSP。
复位入口 0x08000004—0x08000007。4个字节,放置复位程序的入口地址。
中断向量 在0x08000007之后,每个向量的入口地址可以定义。每个中断向量地址可以浮动。
复位程序 从最后一个中断向量操作数之后的地址开始。
主程序段 主程序代码区。
归结起来,两者跳转的方式有所不同,MCS51是通过入口放置指令,而CortexM3则是通过入口处放置地址来实现跳转。
为了深入探究一下CortexM3的复位机制,我编一个简单的程序,力图通过HEX和LST文件理清脉络。
环境如下:
IDE:Keil 4.12
硬件:STM32F103VCT6,BOOT0=0
程序:汇编语言
LST程序如下:
(1) 初始化程序Initialization.LST
00000000 20005000 MSP_Top EQU 0x20005000
00000000 THUMB
00000000 AREA Reset, Data, ReadOnly
00000000 EXPORT __Vectors
00000000 EXPORT __Vectors_End
00000000 EXPORT __Vectors_Size
00000000 __Vectors
00000000 20005000 DCD MSP_Top
00000004 00000000 DCD Reset_Handler
00000008 00 00 00
00 00 00
00 00 SPACE 0x8
00000010 __Vectors_End
00000010 00000010 __Vectors_Size EQU __Vectors_End - __Vectors
00000010 AREA |.text|, Code, Readonly
00000000 Reset_Handler PROC
00000000 EXPORT Reset_Handler [WEAK]
00000000 IMPORT Main
00000000 4800 LDR R0,=Main
00000002 4700 BX R0
00000004 ENDP
00000004 ALIGN
00000004 END
主栈配置到SRAM区0x20005000,暂时未定义主栈大小。暂时未定义其它中断向量。
(2) 主程序Main.LST
INCLUDE Stm32F103REG.h
00000000 Main PROC
00000000 EXPORT Main
00000000 4801 LDR R0,=RCC_CR
00000002 6801 LDR R1,[R0]
00000004 E7FE B Main
00000006 ENDP
00000006 END
Stm32F103REG.h是自行编制的寄存器定义程序。
上述程序编译之后打开工程HEX文件和两个程序段的LST文件并整理一下,删除一些不重要的数据,结果如下:
(3)工程HEX文件
:020000040800F2
:100000000050002011000008000000000000000067
:10001000004800471900000801480168FCE700009B
:04002000001002408A
:0400000508000019D6
:00000001FF
看起来有些乱七八糟,整理一下。
数据长度 地址偏移 数据类型 数据块 校验和
1byte 2bytes 1byte n bytes 1byte
02 00 00 04 08 00 F2
10 00 00 00 00 50 00 20 11 00 00 08 00 00 00 00 00 00 00 00 67
10 00 10 00 00 48 00 47 19 00 00 08 01 48 01 68 FC E7 00 00 9B
04 00 20 00 00 10 02 40 8A
04 00 00 05 08 00 00 19 D6
00 00 00 01 FF
数据长度好理解,是说本行中数据块有几个字节。
地址偏移是指当前行第一个数据相对于基址的位置。
查了一下资料,数据类型可分为5种类型:
00=这一行的数据块里全是数据;
01=HEX文件结束符,所在行数据块为空;
02=数据块里的数据是扩展段地址;
03=数据块里的数据是段地址;
04=数据块里的数据是线性地址高位;
05=数据块里的数据是线性地址。
看起来,数据类型可分为三大类:数据类(00)、地址类(02~05)和编译标识类(01)
第一行数据类型为04,说明后面的0800和地址偏移0000构成了一个基址0x08000000,这是STM32F103 FLASH区的首址,后序行的数据存储地址都可以根据这个基址加偏移量计算出来。
第二行数据类型为00,偏移量为0000,也就是说,数据块的第一个00被存放在地址0x08000000中,这就是单片机复位时从FLASH区启动的首址。
按照前面的说明,首址之后的4个字节应当是准备赋给MSP的值,也就是程序指定的0x20005000,可现在却是00 50 00 20,看了半天恍然大悟,突然想起CortexM3数据格式是所谓的小端模式Little-endian,所以这4个字节的32位数据得按字节倒过来读成20 00 50 00。但当数据类型为地址类时,数据块数据就可以正着读了,哈哈。
00 50 00 20之后又是一个地址,倒过来是08 00 00 11,这就是复位程序入口地址。其后的数据是8个字节的00,这就是SPACE 0x8的功劳了。 按照流程,复位地址赋值给PC之后会从地址0x08000011开始运行,0x08000011单元的数据是48,对照一下Initialization.LST,对应的是它的第一条可执行语句'LDR R0,=Main',只不过这条指令的代码48 00也存储成了小端模式。
以此类推,逐一阅读后面的数据,过程就出来了,Main的段地址是第三行的那个大红圈里的数据0x08000019,从它以后就是Main的指令代码了。但这里有一个问题,Main.LST表明‘B Main’指令的代码是E7 FE,可HEX里却是E7 FC,这又是怎么回事儿呢?不知道哪位前辈能告诉我。
还有一个问题:从0x08000020开始的数据明显是RCC_CR的地址,它怎么会出现在这儿呢?
用汇编写STM32程序是苦中有乐,有人说我这是自找麻烦,但我不这么认为。与使用固件库编程相比,无论是面向寄存器编程还是直接用汇编,编程难度都要大很多,但是,程序透明度也比前者大得多,绝对不会出现神龙见首不见尾的感觉,尤其是对于我这种不太会C语言的硬件工程师就更是如此。
【必读】版权免责声明
1、本主题所有言论和内容纯属会员个人意见,与本论坛立场无关。2、本站对所发内容真实性、客观性、可用性不做任何保证也不负任何责任,网友之间仅出于学习目的进行交流。3、对提供的数字内容不拥有任何权利,其版权归原著者拥有。请勿将该数字内容进行商业交易、转载等行为,该内容只为学习所提供,使用后发生的一切问题与本站无关。 4、本网站不保证本站提供的下载资源的准确性、安全性和完整性;同时本网站也不承担用户因使用这些下载资源对自己和他人造成任何形式的损失或伤害。 5、本网站所有软件和资料均为网友推荐收集整理而来,仅供学习用途使用,请务必下载后两小时内删除,禁止商用。6、如有侵犯你版权的,请及时联系我们(电子邮箱1370723259@qq.com)指出,本站将立即改正。
|