建议:最好跟着来一遍,不然的话,纸上谈兵,诋毁一切 !!!
一、进制知识
10进制里,每一位的权重,从右往左数:个十百千万,也就是:10^0, 10^1, 10^2,10^3……
16进制里,每一位的权重,从右往左数,分别是:16^0, 16^1, 16^2, 16^3, 16^4, ……
8进制里,每一位的权重,从右往左数,分别是:8^0, 8^1, 8^2, 8^3, 8^4, ……
2进制里,每一位的权重,从右往左数,分别是:2^0, 2^1, 2^2, 2^3, 2^4, ……
二、字节序&位操作
- 0x12345678的低位(0x78)存在低地址,即方式1,叫做小字节序(Little endian);
- 0x12345678的高位(0x12)存在低地址,即方式2,叫做大字节序(Big endian);
- 在嵌入式开发中,我们只涉及逻辑移位:不关心符号位,都是补0
- 算术移位,需要分有符号型值和无符号型值
- 对于无符号型值,算术移位等同于逻辑移位。
- 对于有符号型值 ,算术左移等同于逻辑左移;算术右移补的是符号位,正数补0,负数补1。
- 取反
- 位于
- 位或
- 置位(|=(1<<n))
- 清位 (&=~(1<<n))
三、汇编_反汇编_机器码
- 我们想深入了解ARM架构,想深入了解汇编与C,想深入理解栈的作用,想深入理解C语言的实质,就必须把最终的可执行程序,反汇编后,阅读得到的汇编代码。
- 汇编:汇编文件转换为目标文件(机器码)
- 反汇编:可执行文件(目标文件,机器码)转换为汇编文件、
- KEIL怎么反汇编:
- 在KEIL的User选项中,如下图添加这两项:(可能需要改命名)
- fromelf --bin --output=led.bin Objects\led_c.axf
- fromelf --text -a -c --output=led.dis Objects\led_c.axf
- 然后重新编译,即可得到二进制文件led.bin(以后会分析)、反汇编文件led.dis。
- 如下图操作:
-
- GCC下反汇编
- 使用GCC工具链编译程序时,在Makefile中有一句:
- $(OBJDUMP) -D -m arm led.elf > led.dis # OBJDUMP = arm-linux-gnueabihf-objdump
- 他就是把可执行程序led.elf,反汇编,得到led.dis
- 机器码与汇编
- 前面介绍过伪指令,伪指令是实际不存在的ARM指令,编译器在编译时转换为存在的ARM指令,我们代码中的ldr r1,=0x????????这条为指令的真实指令是什么呢?
- LDR SP, =(0x20000000+0x10000) // STM32F103
ldr sp, =(0x80000000+0x100000) // IMX6ULL
ldr sp, =0xc0000000 + 0x100000 // STM32MP157 A7 -
我们只摘取前面一小段,第一列是地址,第二列是机器码,第三列是汇编:
-
- 解析LDR伪指令
- 为什么PC=当前指令+4或8呢?
- CORTEX M3/M4:使用Thumb2指令集,一条指令是16位或32位
- CORTEX A7:默认使用ARM指令集,一条指令是32位
- 流水线:ARM指令采用流水线机制:1️⃣、当前执行A地址的指令;2️⃣、同时已经在对下一条指令进行译码;3️⃣、同时已经在读取下下一条指令:PC = A + 4(Thumb/Thumb2指令集)、PC = A + 8(ARM指令集)
总结: C:为了方便人类使用,发明的高级语言,要转换为汇编;汇编:为了解放人类的记忆,发明的助记符,不用去记各类机器码,最终要转换为机器码;机器码:给CPU使用 !!!
四、C 与汇编深入分析
- 直接调用:bl main
- 想传参数怎么办:在arm中有个ATPCS规则,(ARM-THUMB procedure call standard)(ARM-Thumb 过程调用标准):约定 r0 - r15的用途:
- r0 - r3:传参
- r4 - r11:局部变量(入口保存,出口恢复)
- r12 - r13:特殊寄存器
- C 函数的反汇编码阅读
- 要解决这几个问题:
- 为什么调用C函数前要设置栈?栈的作用是?
- C函数传参
- C函数执行过程体验
- Flash 上的内容
- 反汇编示例
-
- 烧写在Flash上的内容
-
- 启动流程
上电后:
1️⃣、设置栈:CPU会从0x80000000读取值,用来设置SP(我们的程序里再次设置了SP)
2️⃣、跳转:CPU从0x80000004得到地址值,根据它的Bit0切换为ARM状态或Thumb状态
然后跳转;
01、对于cortex M3/M4,它只支持Thumb状态,所以0x08000004上的值bit0必定是1
02、0x08000004上的值 = Reset_Handler + 1
3️⃣、从Reset_Handler继续执行
五、纯汇编点灯(走进汇编的大门)
- 流程:
- 对于寄存器的操作,主要涉及读、修改、写
- 读可以使用LDR,写可以使用STR
- 清除使用BIC或AND指令,设置位使用ORR指令
- 函数条件判断:减一:SUB;但是顺便使用减1后的结果影响程序状态寄存器,代码为`SUBS R0, R0, #1`
- 程序的调用与返回
1️⃣、传参:R0
2️⃣、调用:BL
3️⃣、返回:MOV PC,LR
-
ARM的规则ATPCS和AAPCS:参考链接:如下(重要)
- (2条消息) ARM的规则ATPCS和AAPCS_aapcs规则_猿猿_yzg的博客-CSDN博客
- AAPCS(ARM Archtecture(架构) Procedure Call Standard)是ATPCS的改进版本
- 子程序中结果返回的规则如下:
-
-
-
-
-
- AAPCS规则:
- ARM架构过程调用规则参考文档链接:
ATPCS和AAPCS_atpcs规定数据栈是_Achou.Wang的博客-CSDN博客
-
- 但是:中断保存现场多了一个寄存器:PC,一个栈帧,进入异常前,依次把xPSR, PC, LR, R12以及R3-R0由硬件自动压入适当的堆栈中(响应异常时,若当前代码正在使用PSP,则压入PSP,否则就压入MSP),其中,PC(
SP-0x24
处)即为图中返回地址: - 为什么多了PC:()
- 这样的话,中断返回的时候,就会用 PC - 4 来处理了 !!!(猜测,这里我的理解可能有错+三级流水线,具体问题具体解析)
- 引用PC是为了存储返回地址,因为发生异常时LR寄存器的特殊性,保存了EXC_RETURN
- 所以你得认真处理,不明白的是怎么恢复现场
-
ASM 语言 (星星点灯 程序):
六、串口硬件知识
-
-
要发送数据时,CPU控制内存要发送的数据通过FIFO传给UART单位,UART里面的移位器,依次将数据发送出去,在发送完成后产生中断提醒CPU传输完成。
-
接收数据时,获取接收引脚的电平,逐位放进接收移位器,再放入FIFO,写入内存。在接收完成后产生中断提醒CPU传输完成。
-