目录
- ARM架构简明教程
 - 1. ARM架构
 - 电脑的组成
 - 1.2 RISC
 - 1.2 提出问题
 - 1.3 CPU内部寄存器
 - 1.4 汇编指令
 
- 2. C函数的反汇编
 
学习视频
【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=9&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933
ARM架构简明教程
1. ARM架构
电脑的组成

对于单片机,叫SoC system on chip,在芯片上有完整的系统
芯片集成了CPU 内存 硬盘(flash)
内存:读出数据,写入数据
计算都是在CPU内完成的
1.2 RISC
ARM芯片属于精简指令集计算机(RISC:Reduced Instruction Set Computing),它所用的指令比较简单,有如下特点:
① 对内存只有读、写指令
 ② 对于数据的运算是在CPU内部实现
 ③ 使用RISC指令的CPU复杂度小一点,易于设计

对于上图所示的乘法运算a = a * b,在RISC中要使用4条汇编指令:
① 读内存a
 ② 读内存b
 ③ 计算a*b
 ④ 把结果写入内存
1.2 提出问题
问题:在CPU内部,用什么来保存a、b、a*b ?
回答:寄存器 R0、R1、R2……
1.3 CPU内部寄存器

无论是cortex-M3/M4,
还是cortex-A7,
CPU内部都有R0、R1、……、R15寄存器;
它们可以用来“暂存”数据。

对于R13、R14、R15,还另有用途:
R13:别名SP(Stack Pointer),栈指针
R14:别名LR(Link Register),用来保存返回地址
R15:别名PC(Program Counter),程序计数器,表示当前指令地址,写入新值即可跳转
1.4 汇编指令
-  
读内存:Load
# 示例 LDR R0, [R1, #4] ; 读地址"R1+4", 得到的4字节数据存入R0 
Load R 读 四个字节,读R1+4的地址,读入的数据保存到R0里
 其他指令:LDRB\LDRH
 LDRB 读取一个字节 1Byte,LDRH 读取Half,2Bytes
-  
写内存:Stroe
# 示例 STR R0, [R1, #4] ; 把R0的4字节数据写入地址"R1+4" 
写内存 4个字节STR 把R0的4字节数据写到R1+4
 写其他字节数
 STRB 1byte
 STRH half - 2bytes
读写指令经常用到,一定要掌握这两条指令
-  
加减
ADD R0, R1, R2 ; R0=R1+R2 ADD R0, R0, #1 ; R0=R0+1 SUB R0, R1, R2 ; R0=R1-R2 SUB R0, R0, #1 ; R0=R0-1 -  
比较
CMP R0, R1 ; 结果保存在PSR(程序状态寄存器) 
结果保存到程序状态寄存器里
-  
跳转
B main ; Branch, 直接跳转 BL main ; Branch and Link, 先把返回地址保存在LR寄存器里再跳转 
跳转
 B main; PC/R15=main的地址
BL main; 分为两个步骤
 LR/R14=返回地址
 PC/R15=main的地址
2. C函数的反汇编
C函数:
int add(volatile int a, volatile int b)
{
	volatile int sum;
    sum = a + b;
    return sum;
}
 
这段代码里用volatile int a,不要让编译器优化我们的程序
把这个函数复制,随便我们工程的放到一个地方
修改如下部分
 
void OLED_Test(void)
{
    int OLED_Count = 0;
    OLED_Init();
    // 清屏
    OLED_Clear();
    while (1)
    {
        // 在(0, 0)打印'A'
        OLED_PutChar(0, 0, 'A');
        // 在(1, 0)打印'Y'
        OLED_PutChar(1, 0, 'Y');
        // 在第0列第2页打印一个字符串"Hello World!"
        OLED_PrintString(0, 2, "Hello World!");
        OLED_PrintSignedVal(6, 4, OLED_Count);
        
        OLED_Count = add(OLED_Count, 1);    //OLED_Count ++ 看反汇编代码
    }
}
 
让Keil生成反汇编:
选择 魔术棒-uesr 示例图片
 
- 为例方便复制,制作反汇编的指令如下:
 
fromelf  --text  -a -c  --output=xxx.dis  xxx.axf
 
上面语句的作用是 用 xxx.axf 输出 xxx.dis 的反汇编
- 为了方便复制粘贴,先做好我自己的这行代码,自己的路径自己粘贴,方法在如下
 - 在linker的窗口下,往下翻,就能找到linker的路径,复制粘贴到对应的位置即可
 

 结合起来就是
fromelf  --text  -a -c  --output=test.dis  01_freertos_template\01_freertos_template.axf
 
用01_freertos_template\01_freertos_template.axf ,输出test.dis的反汇编
粘贴到对应的位置

编译 看输出结果
 
 这里很直观的看到输出了.dis文件
找到这个文件,用文本文档打开

OLED_Test
	0x08002a00:    2400        .$      MOVS     r4,#0
	0x08002a02:    f7ffff01    ....    BL       OLED_Init ; 0x8002808
	0x08002a06:    f7fffeea    ....    BL       OLED_Clear ; 0x80027de
	0x08002a0a:    2100        .!      MOVS     r1,#0
	0x08002a0c:    2241        A"      MOVS     r2,#0x41
	0x08002a0e:    4608        .F      MOV      r0,r1
	0x08002a10:    f7ffff9c    ....    BL       OLED_PutChar ; 0x800294c
	0x08002a14:    2259        Y"      MOVS     r2,#0x59
	0x08002a16:    2100        .!      MOVS     r1,#0
	0x08002a18:    2001        .       MOVS     r0,#1
	0x08002a1a:    f7ffff97    ....    BL       OLED_PutChar ; 0x800294c
	0x08002a1e:    a208        ..      ADR      r2,{pc}+0x22 ; 0x8002a40
	0x08002a20:    2102        .!      MOVS     r1,#2
	0x08002a22:    2000        .       MOVS     r0,#0
	0x08002a24:    f7ffff79    ..y.    BL       OLED_PrintString ; 0x800291a
	0x08002a28:    4622        "F      MOV      r2,r4
	0x08002a2a:    2104        .!      MOVS     r1,#4
	0x08002a2c:    2006        .       MOVS     r0,#6
	0x08002a2e:    f7ffff35    ..5.    BL       OLED_PrintSignedVal ; 0x800289c
	0x08002a32:    2101        .!      MOVS     r1,#1
	0x08002a34:    4620         F      MOV      r0,r4
	0x08002a36:    f000fa7f    ....    BL       add ; 0x8002f38
	0x08002a3a:    4604        .F      MOV      r4,r0
	0x08002a3c:    e7e5        ..      B        0x8002a0a ; OLED_Test + 10
 
add
	0x08002f38:    b503        ..      PUSH     {r0,r1,lr}
	0x08002f3a:    b081        ..      SUB      sp,sp,#4
	0x08002f3c:    e9dd0101    ....    LDRD     r0,r1,[sp,#4]
	0x08002f40:    4408        .D      ADD      r0,r0,r1
	0x08002f42:    9000        ..      STR      r0,[sp,#0]
	0x08002f44:    bd0e        ..      POP      {r1-r3,pc}
i.main
main
	0x08002f46:    f7fdfe77    ..w.    BL       HAL_Init ; 0x8000c38
	0x08002f4a:    f7fffe0f    ....    BL       SystemClock_Config ; 0x8002b6c
	0x08002f4e:    f7fffa1d    ....    BL       MX_GPIO_Init ; 0x800238c
	0x08002f52:    f7fffac3    ....    BL       MX_I2C1_Init ; 0x80024dc
	0x08002f56:    f7fff9cf    ....    BL       MX_ADC1_Init ; 0x80022f8
	0x08002f5a:    f7fffadf    ....    BL       MX_SPI1_Init ; 0x800251c
	0x08002f5e:    f7fffc1f    ....    BL       MX_USB_PCD_Init ; 0x80027a0
	0x08002f62:    f7fffaff    ....    BL       MX_TIM1_Init ; 0x8002564
	0x08002f66:    f7fffb65    ..e.    BL       MX_TIM2_Init ; 0x8002634
	0x08002f6a:    f7fffbc1    ....    BL       MX_TIM3_Init ; 0x80026f0
	0x08002f6e:    f7fffbfb    ....    BL       MX_USART1_UART_Init ; 0x8002768
	0x08002f72:    f000f813    ....    BL       osKernelInitialize ; 0x8002f9c
	0x08002f76:    f7fff9e7    ....    BL       MX_FREERTOS_Init ; 0x8002348
	0x08002f7a:    f000f82b    ..+.    BL       osKernelStart ; 0x8002fd4
	0x08002f7e:    e7fe        ..      B        0x8002f7e ; main + 56
 
搜索add,要区分大小写!
 
C函数add
int add(volatile int a, volatile int b)
{
	volatile int sum;
    sum = a + b;
    return sum;
}
 
C函数add的反汇编代码如下:
    i.add
    add
        0x08002f34:    b503        ..      PUSH     {r0,r1,lr}
        0x08002f36:    b081        ..      SUB      sp,sp,#4
        0x08002f38:    e9dd0101    ....    LDRD     r0,r1,[sp,#4]
        0x08002f3c:    4408        .D      ADD      r0,r0,r1
        0x08002f3e:    9000        ..      STR      r0,[sp,#0]
        0x08002f40:    bd0e        ..      POP      {r1-r3,pc}
 

 add函数里的cnt参数保存在r0里,1保存在r1里,局部变量保存在栈里

对于单片机来说,在FLASH的地址上,保存这些数据

 cpu会读取FLASH地址,得到对应的机器码,在cpu内部执行机器码,
 cpu会读取FLASH地址,得到对应的机器码,在cpu内部执行机器码,
 cpu会读取FLASH地址,得到对应的机器码,在cpu内部执行机器码,
 ……

从栈里把数值POP到R1R2R3,
仅仅是为了后面把LR的值POP到PC寄存器,仅
仅是为了恢复栈
以上可以看出,对于汇编代码,本质上,大多数汇编代码都是读取内存,写入内存,加加,减减,跳转
看起来很高大上的C语言的函数,在汇编里都是如此,读内存,写内存,加加,减减,跳转~
韦老师说这节课看不懂没关系,后面课程还会讲的
学习视频
 【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=9&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933



















