1、查看外设寄存器的值

配合对应的芯片开发手册以查看寄存器及其每一位的意义,可以解决90%以上的单纯的片内外设bug,学会如何通过寄存器的值来排外设上的蛊是嵌入式开发从小白到入门的重要一步,一定要善于使用这个工具,而不是外设出了问题时,一点点的通过表层外设现象排蛊。
2、ARM汇编常用指令集解读
如上图, STM32 的汇编指令(基于 ARM Cortex-M 架构)是调试和底层开发的重要基础。
以下是 STM32 中常用的 ARM Thumb/Thumb-2 汇编指令及其用途:
一、数据操作指令
-
MOV
-
作用: 寄存器之间的数据移动或立即数加载
-
示例:
MOV R0, R1 ; R0 = R1 MOV R2, #0x10 ; R2 = 0x10
-
-
LDR
/STR
-
作用: 从内存加载数据到寄存器(Load)或存储寄存器数据到内存(Store)
-
示例:
LDR R0, [R1] ; R0 = *R1(从地址 R1 加载数据) STR R2, [R3] ; *R3 = R2(将 R2 的值存储到地址 R3)
-
-
ADD
/SUB
-
作用: 加减运算
-
示例:
ADD R0, R1, R2 ; R0 = R1 + R2 SUB R3, R4, #5 ; R3 = R4 - 5
-
-
AND
/ORR
/EOR
/BIC
-
作用: 逻辑运算(与、或、异或、位清除)
-
示例:
AND R0, R1, #0xFF ; R0 = R1 & 0xFF(保留低8位) ORR R2, R3, R4 ; R2 = R3 | R4
-
二、流程控制指令
-
B
-
作用: 无条件跳转(Branch)
-
示例:
B label ; 跳转到标签 label
-
-
BX
-
作用: 跳转到寄存器指定的地址,并切换指令集(如 ARM ↔ Thumb)
-
示例:
BX LR ; 跳转到 LR 寄存器中的地址(常用于函数返回)
-
-
BL
-
作用: 带链接的跳转(保存返回地址到 LR 寄存器)
-
示例:
BL function ; 调用函数 function,LR = 下一条指令地址
-
-
条件分支指令
-
作用: 根据标志位(如 Z、C)跳转
-
常见指令:
BEQ label ; 相等时跳转(Z=1) BNE label ; 不等时跳转(Z=0) BCS label ; 进位标志 C=1 时跳转
-
三、栈操作指令
-
PUSH
/POP
-
作用: 压栈和出栈操作(保存/恢复寄存器)
-
示例:
PUSH {R0, R1} ; 将 R0, R1 压入栈 POP {R2, R3} ; 从栈中恢复数据到 R2, R3
-
四、标志位操作
-
CMP
-
作用: 比较两个值(内部执行减法,更新标志位)
-
示例:
CMP R0, R1 ; 比较 R0 和 R1,更新 Z(Zero)标志
-
-
TST
-
作用: 按位与测试(不保存结果,仅更新标志位)
-
示例:
TST R0, #0x01 ; 测试 R0 的最低位是否为 1
-
五、特殊指令
-
CPSID
/CPSIE
-
作用: 全局中断开关
-
示例:
CPSID I ; 关闭中断 CPSIE I ; 开启中断
-
-
NOP
-
作用: 空操作(常用于调试时插入断点或延时)
-
示例:
NOP ; 空操作
-
-
IT
(If-Then)-
作用: 条件执行指令块(Thumb-2 特有)
-
示例:
ITE EQ ; If-Then-Else 条件块(EQ 为条件) MOVEQ R0, R1; 如果 EQ 成立,执行此指令 MOVNE R0, R2; 否则执行此指令
-
六、调试场景示例
1. 函数调用与返回
BL HAL_Init ; 调用 HAL_Init 函数,LR 保存返回地址
...
BX LR ; 函数返回
2. 中断服务函数
PUSH {R0-R3} ; 保存寄存器到栈
...
POP {R0-R3} ; 恢复寄存器
BX LR ; 从中断返回
3. 条件分支调试
CMP R0, #10 ; 比较 R0 是否等于 10
BEQ loop_end ; 如果相等,跳转到 loop_end
keil其他技巧
symbols窗口,可以看到每个文件下面包含的函数和成员
寄存器窗口
可以用来查看CPU内部寄存器的使用状态
栈窗口
可以用来查看当前栈内函数调用的使用情况,从图中可以看出在main函数中调用了SystemClock_Config这个函数,紧接着又在SystemClock_Config又调用了HAL_RCC_OscConfig这个函数,可以很清晰的看出函数之间调用的层次关系。
监控变量窗口
可以将需要观察的变量添加到窗口中去,观察变量值的变化。
内存窗口
将需要观察的变量拖动到内存窗口中,即可显示当前变量的值。例如 int i =0;将i拖动到内存窗口即可显示当前变量的值,如果在 i 前面加上取地址符 & 即可显示当前变量的地址。地址后,则显示当前地址的值。