关于stm32中R15寄存器的理解

本文解析了STM32中PC指针的工作原理及注意事项,包括如何正确设置函数地址以避免HardFault异常,以及IAR调试器对PC指针的影响。

今天上午看《stm32权威指南》中关于R15寄存器中有些内容不理解,查了查资料,原来是这样。


这里有一个别人的问题。



我把Nor Flash里的一个函数代码数据(函数首地址是:0x6400EC10)拷贝到RAM的 0x2000FC00,

然后把0x2000FC00加载给PC指针(为了让代码在RAM里运行),接着运行代码就出现了Hard Fault 

Exception,而用 IAR 单步执行的时候能正常运行。同时我将一个函数地址值赋给一个unsigned int

变量之后发现自动加了1。对于这些问题先看权威指南里是怎么说的吧。

一,PC指针(程序计数器R15)(权威指南)
R15是程序计数器,在汇编代码中称为“PC指针”。因为CM3内部使用了指令流水线,读PC时返回的值

是当前指令的地址+4。比如说: 
0x1000: MOV R0, PC ; R0 = 0x1004 
如果向PC中写数据,就会引起一次程序的分支(但是不更新LR寄存器)。CM3中的指令至少是半字

对齐的,所以PC的LSB总是读回0。然而,在分支时,无论是直接写PC的值还是使用分支指令,都必

须保证加载到PC的数值是奇数(即LSB=1),用以表明这是在Thumb状态下执行。倘若写了0,则视

为企图转入ARM模式,CM3将产生一个fault异常。这些可以总结为读PC指针时,返回LSB总是为0;

写PC指针时,一定要保证LSB为奇数。

二,函数地址(自己总结的)
正如上面所说,写PC指针的时候必须保证LSB为奇数,如果执行跳转指令的时候,将一个函数指针

加载给PC,这时候就必须保证这个函数地址的LSB为奇数,而且还必须是(4*n+1)(n=0,1,2,3)

这样的奇数,只有这样运行程序才不会跑飞。在以后的编程中,如果执行函数跳转的时候,一定要

保证这个函数地址的低四位的值为(4*n+1)(n=0,1,2,3)。
基于上面两点,就可以很好的解释所出现的问题了,如果将0x2000FC00加载给PC指针,肯定会出现

Hard Fault Exception,因为 0x2000FC00 的 LSB = 0;但是如果将 0x2000FC01加载给PC指针,程

序就能正常运行了,这就应证了权威指南里所说的写PC指针的时候必须保证加载给PC的值的LSB为1


而之前的为什么用IAR单步执行能成功,原因是IAR的调试器做了处理,使得每一次调试的时候对PC

指针的LSB做了一个奇偶变换;当全速运行的时候,调试器不管用了,直接由CPU接管,这个时候将

函数地址加载给PC的值的LSB为偶数,当然导致程序跑飞了。

还有为什么将一个函数地址值赋给一个unsigned int 变量之后会自动加一呢,这个是Cortex-M3的一

个特性,也就是说读取一个函数地址值返回的LSB为0,当赋值给一个变量的时候会加1,这是为了将

这个变量值加载给PC指针之后保证PC指针的LSB为1。

问题解决了,希望对碰到同样问题的朋友能有所帮助。同时如果总结的不好,大家可以一起交流交流。



但是,这个部分又是怎么理解的呢?


STM32中,R0 - R15寄存器是CPU内部的寄存器,它们并没有直接对应的物理地址可供打印。不过,可以在调试环境中使用调试器来查看这些寄存器的值,若要在代码运行时间接地获取相关信息并打印,可结合Keil MDK等开发环境并使用汇编和C语言混合编程。 ### 示例代码 ```c #include "stm32fxxx.h" // 定义一个函数来获取寄存器值 void get_registers_values() { volatile uint32_t reg_values[16]; __asm volatile ( "MOV R15, LR\n" "MOV %0, R0\n" "MOV %1, R1\n" "MOV %2, R2\n" "MOV %3, R3\n" "MOV %4, R4\n" "MOV %5, R5\n" "MOV %6, R6\n" "MOV %7, R7\n" "MOV %8, R8\n" "MOV %9, R9\n" "MOV %10, R10\n" "MOV %11, R11\n" "MOV %12, R12\n" "MOV %13, R13\n" "MOV %14, R14\n" "MOV %15, R15\n" : "=r" (reg_values[0]), "=r" (reg_values[1]), "=r" (reg_values[2]), "=r" (reg_values[3]), "=r" (reg_values[4]), "=r" (reg_values[5]), "=r" (reg_values[6]), "=r" (reg_values[7]), "=r" (reg_values[8]), "=r" (reg_values[9]), "=r" (reg_values[10]), "=r" (reg_values[11]), "=r" (reg_values[12]), "=r" (reg_values[13]), "=r" (reg_values[14]), "=r" (reg_values[15]) : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" ); // 这里假设使用串口打印,需要根据实际情况修改 for (int i = 0; i < 16; i++) { // 打印寄存器值 // 例如使用printf函数,需初始化串口 printf("R%d = 0x%08X\n", i, reg_values[i]); } } int main(void) { // 初始化串口等外设 // 这里省略具体初始化代码 get_registers_values(); while (1) { // 主循环 } } ``` ### 代码解释 - **汇编部分**:通过`__asm volatile`嵌入汇编代码,将R0 - R15寄存器的值依次赋给`reg_values`数组。 - **C语言部分**:定义了一个`get_registers_values`函数,用来获取寄存器的值并通过`printf`函数打印出来。主函数中调用`get_registers_values`函数,并在完成后进入主循环。 ### 注意事项 - 上述代码假设使用了标准C库中的`printf`函数,实际开发中需要根据所用的开发板和调试环境初始化串口等外设。 - 代码中的`stm32fxxx.h`需要根据实际使用的STM32芯片型号进行替换。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值