玩转STM32(16)理解复位函数

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/caimouse/article/details/94898882

前面搞定了栈顶的问题,接着下来,可以从复位矢量表里,看到如下:

__Vectors       DCD     __initial_sp               ; Top of Stack

                DCD     Reset_Handler              ; Reset Handler

                DCD     NMI_Handler                ; NMI Handler

从向量表里看到第二项,就是复位函数的入口地址,Reset_Handler是复位向量的过程函数,它被编译之后,可以看到它表示的值是:

Reset_Handler                            0x0800019d   Thumb Code     8  startup_stm32f40_41xxx.o(.text)

前面说过CPU会在复位之后,就加载Reset_Handler到PC寄存器,也就是加载地址0x0800019d,然后就会执行复位过程函数Reset_Handler,这时就开始运行复位过程函数的代码了。复位过程函数到底做了些什么工作,才可以进入C语言的入口函数main执行呢?要回答这个问题,我们先来看复位过程函数的代码,如下:

Reset_Handler    PROC

                 EXPORT  Reset_Handler             [WEAK]

        IMPORT  SystemInit

        IMPORT  __main

 

                 LDR     R0, =SystemInit

                 BLX     R0

                 LDR     R0, =__main

                 BX      R0

                 ENDP

在上面这段代码里,使用PROC/ENDP宏指令来声明一个子过程,然后使用EXPORT指令来声明本过程Reset_Handler为弱声明,也就是后面的[WEAK]。为什么要使用WEAK指令?其实是为了方便用户来自定义自己的函数。比如用户使用默认的Reset_Handler过程函数不能满足自己的要求,需要自己定义一个过程函数来替换Reset_Handler。如果使用旧的方法,就是使用宏定义来判断,但这里使用WEAK指令之后,就可以不使用宏来实现了。编译器会直接判断编译所有文件时,是否会发现用户自定义的Reset_Handler函数,如果有就会使用用户定义的Reset_Handler函数,而不是调用这里声明为WEAK的Reset_Handler函数。如果用户没有定义,就调用默认的Reset_Handler函数。这跟C++里虚函数有点像,当派生类有相同函数名称时,就会调用派生类的函数,如果没有就调用基类的函数。因此在向量表里的中断函数,很多都是使用WEAK指令声明的,所以都会有这个作用,如果用户自己没有定义相应中断函数,就会调用默认的中断函数,这样就方便大家写中断函数的代码,不必要把所有的中断函数都写一遍,只需要写自己关心的中断函数即可,这样大大减轻了开发人员的工作量,同时代码更加标准化,更方便移植和复用。

接着下来可以看到下面这行代码:

IMPORT  SystemInit

这里使用IMPORT指令,说明后面的函数SystemInit在别的地方声明的,在这里它是在system_stm32f4xx.c文件里定义的,通过IMPORT指令才可以在startup_stm32f40_41xxx.s文件里调用它,否则它在连接时找不到这个函数,就会编译出错。

同理,IMPORT  __main代码,也是声明_main函数是在别的模块里声明和定义的,所以采用IMPORT指令。

接着会运行这行代码:

LDR     R0, =SystemInit

LDR指令是将存储器地址所指地址处连续的4个字节(1个字)的数据传送到目的寄存器中。在这里是把SystemInit的地址加载到R0寄存器。

BLX     R0

BLX reg是跳转到由寄存器reg给出的地址,并根据REG的LSB切换处理器;状态还要把转移前的下条指令地址保存到LR。这行代码就是实现调用SystemInit函数运行,运行之后跳到下一条指令执行。

LDR     R0, =__main

BX      R0

这两行语句,与前面运行SystemInit函数是一样的,在这里调用_main函数运行,这个函数里会更进一步地初始化C语言运行环境,然后调用C语言的入口函数main来运行,这样就调用C语言编写的代码了。

到这里,就完成复位函数的代码分析,理解整个复位函数为什么要这么写,以及怎么样执行每一步。

https://blog.csdn.net/caimouse/article/details/51749579

展开阅读全文

STM32 RC531 复位问题

08-13

这两天的调试过程描述:rn1 硬件连接:STM32与531通过SPI总线连接,另外531芯片的IRQ与32的PG15连接。rn2 STM32F4与RC531通信描述rn 上电对RC531复位后,读取部分寄存器的值与手册上一致,对RC531天线控制寄存器写入指令可以控制RC531天线的输出。这是否可以说明STM32与RC531通信正常?rn 与RC531通信时需要一个外部中断,经测试也可以进入。rn 现在的问题是:无法读取出RC531芯片的序列号。rn 测试发现,发送读取芯片号命令后,32进入外部中断处理程序,rnif (MpIsrInfo && MpIsrOut && MpIsrIn) // transfer pointers have to be setrn // correctlyrn rn while( ReadRawIO(RegPrimaryStatus) & 0x08) // loop while IRQ pending //[color=#FF0000]此处通不过,直接跳出中断处理程序[/color]。rn rnrn附底层的读写程序:rnvoid WriteIO(unsigned char Address, unsigned char value) rn RC531NSS_CLR(); rn SPI1_ReadWriteByte(0x00); rn SPI1_ReadWriteByte(GetRegPage(Address)); rn RC531NSS_SET(); rn rn RC531NSS_CLR(); rn SPI1_ReadWriteByte((Address<<1)&0x7f); rn SPI1_ReadWriteByte(value); rn RC531NSS_SET(); rn rn/**************************************************************************** rn* ? ?:ReadIO() rn* ? ?:???????????(EEPROM)? rn* ????:Address ?? rn* ????:?? rn****************************************************************************/ rnu8 ReadIO(u8 Address) rn rn u8 a; rn RC531NSS_CLR(); rn SPI1_ReadWriteByte(0x00); rn SPI1_ReadWriteByte(GetRegPage(Address)); rn RC531NSS_SET(); rn rn RC531NSS_CLR(); rn SPI1_ReadWriteByte((Address<<1)|0x80); rn a = SPI1_ReadWriteByte(0xff); //???! rn SPI1_ReadWriteByte(0x00); rn RC531NSS_SET(); rn return a; rn rn rnu8 ReadRawIO(unsigned char Address) rn unsigned char a; rn RC531NSS_CLR(); rn SPI1_ReadWriteByte((Address<<1)|0x80); rn a = SPI1_ReadWriteByte(0xff); //???! rn SPI1_ReadWriteByte(0x00); rn RC531NSS_SET(); rn return a; rn rnvoid WriteRawIO(u8 Address, u8 value) rn RC531NSS_CLR(); rn SPI1_ReadWriteByte((Address<<1)&0x7f); rn SPI1_ReadWriteByte(value); rn RC531NSS_SET(); rn rn rn 论坛

没有更多推荐了,返回首页