汇编LED原理分析:
为什么要学习Cortex-A汇编?
1.需要使用汇编初始化一些SOC外设
2.使用汇编初始化DDR
3.设置sp指针,一般指向DDR,设置好C语言运行环境
STM32 IO口初始化和I.MAX6ULL IO口初始化区别?
STM32 IO口初始化流程:
1.通过总线挂靠图找到对应总线进行时钟的开启RCC库函数
2.设置IO复用,将其复用为GPIO
3.配置GPIO的电气属性-输出方式、传输速率等
4.使用GPIO,输出高低电平
I.MAX6ULL IO口初始化流程:
1.使能时钟,CCGR0-CCGR6这七个寄存器控制这6ULL所有外设时钟的使能,为了简单化,设置CCGR0-CCGR6这7个寄存器全部为0xFFFFFFFF ,相当于使能所有外设时钟
2.IO复用,将寄存器的IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的bit3-0设置为0101=5,此时GPIO_IO03配置为复用功能
3.将寄存器IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03是设置GPIO_IO03的电气属性包括压摆率,速度,驱动能力,开漏,上下拉等。
4.配置GPIO功能,设置输入输出,设置GPIO_GDIR寄存器bit3为1,即设置为输出模式
5.设置GPIO1_DR寄存器的bit3,为1表示输出高电平,为0表示输出低电平
汇编简介:
C 语言中的函数调用涉及到出栈入栈,出入栈需要对堆栈进行操作,堆栈实质是一段内存,该段内存由 SP 指针访问,SP 指针指向栈顶。因为 Cortex-A 芯片一上电 SP 指针还没初始化,C 环境还未准备 好,C代码无法运行,必须先用汇编语言设置好 C 环境,比如初始化 DDR、设置 SP 指针等等,C代码正常运行的前提条件是使用汇编代码配置好环境。以STM32F103 为例,启动文件 startup_stm32f10x_hd.s 就是汇编文件。
汇编由一条条指令构成,指令涉及汇编指令。
举例C语言与汇编实现变量值交换程序:
C语言程序:int a,b; a=b;
汇编程序:假设a的地址为0x20;b的地址为0x30 (在使用汇编最为常用的是LDR 和 STR指令)
LDR R0, =0x30 //将数据地址0x30加载到R0中,即R0=0x30
LDR R1, [R0] //读取地址0x30中的数据到R1寄存器
LDR R0, =0x20 //将数据地址0x20加载到R0中,即R0=0x20
STR R1, [R0] //将R1中的值写入R0中所保存的地址
常用汇编指令:
1.存储器访问指令
ARM 不能直接访问存储器,比如 RAM 中的数据,I.MX6UL 中的寄存器就是 RAM 类型 的,我们用汇编来配置 I.MX6UL 寄存器的时候需要借助存储器访问指令,一般先将要配置的值 写入到 Rx(x=0~12)寄存器中,然后借助存储器访问指令将 Rx 中的数据写入到 I.MX6UL 寄存器、
| 指令 | 描述 |
| LDR Rd, [Rn , #offset] | 从存储器 Rn+offset 的位置读取数据存放到 Rd 中。 |
| STR Rd, [Rn, #offset] | 将 Rd 中的数据写入到存储器中的 Rn+offset 位置。 |
LDR 主要用于从存储加载数据到寄存器 Rx 中,LDR 也可以将一个立即数加载到寄存器 Rx 中,LDR 加载立即数的时候要使用“=”,而不是“#”。在嵌入式开发中,LDR 最常用的就是读 取 CPU 的寄存器值。
LDR R0, =0x0209C004 @将寄存器地址 0x0209C004 加载到 R0 中,即 R0=0x0209C004 LDR R1, [R0] @读取地址 0x0209C004 中的数据到 R1 寄存器中
LDR 是从存储器读取数据,STR 就是将数据写入到存储器中,LDR 和 STR 都是按照字进行读取和写入的,是操作的 32 位数据。
LDR R0, =0x0209C004 @将寄存器地址 0x0209C004 加载到 R0 中,即 R0=0x0209C004 LDR R1, =0x20000002 @R1 保存要写入到寄存器的值,即 R1=0x20000002 STR R1, [R0] @将 R1 中的值写入到 R0 中所保存的地址中
2.压栈和出栈指令
A 函数中调用 B 函数,当 B 函数执行完以后再回到 A 函数继续执行。回到 A 函数代码能够接着正常运行要保证跳到 B 函数之前将当前处理器状态保存起来(就是保存 R0~R15 这些寄存器值),当 B 函数执行完成以后再用前面保存的寄存器值恢复 R0~R15 即可。保存 R0~R15 寄存器的操作就叫做现场保护,恢复 R0~R15 寄存器的操作就叫做 恢复现场。现场保护的时候需要进行压栈(入栈)操作,恢复现场就要进行出栈操作。
| 指令 | 描述 |
| PUSH< reg list > | 将寄存器列表存入栈中。 |
| POP < reg list > | 从栈中恢复寄存器列表。 |
PUSH {R0~R3, R12} @将 R0~R3 和 R12 压栈
PUSH {LR} @将 LR 进行压栈
POP {LR} @先恢复 LR
POP {R0~R3,R12} @在恢复 R0~R3,R12
3.算术运算指令
| 指令 | 计算公式 | 备注 |
| ADD Rd, Rn, Rm | Rd = Rn + Rm | 加法运算,指令为 ADD |
| ADD Rd, Rn, #immed | Rd = Rn + #immed | 加法运算,指令为 ADD |
| ADC Rd, Rn, Rm | Rd = Rn + Rm + 进位 | 带进位的加法运算,指令为 ADC |
| ADC Rd, Rn, #immed | Rd = Rn + #immed +进位 | 带进位的加法运算,指令为 ADC |
| SUB Rd, Rn, Rm | Rd = Rn – Rm | 减法 |
| SUB Rd, #immed | Rd = Rd - #immed | 减法 |
| SUB Rd, Rn, #immed | Rd = Rn - #immed | 减法 |
| SBC Rd, Rn, #immed | Rd = Rn - #immed – 借位 | 带借位的减法 |
| SBC Rd, Rn ,Rm | Rd = Rn – Rm – 借位 | 带借位的减法 |
| MUL Rd, Rn, Rm | Rd = Rn * Rm | 乘法(32 位) |
| UDIV Rd, Rn, Rm | Rd = Rn / Rm | 无符号除法 |
| SDIV Rd, Rn, Rm | Rd = Rn / Rm | 有符号除法 |
| 指令 | 计算公式 | 备注 |
| AND Rd, Rn | Rd = Rd &Rn | 按位与 |
| AND Rd, Rn, #immed | Rd = Rn immed | 按位与 |
| AND Rd, Rn, Rm | Rd = Rn & Rm | 按位与 |
| ORR Rd, Rn | Rd = Rd | Rn | 按位或 |
| ORR Rd, Rn, #immed | Rd = Rn | #immed | 按位或 |
| ORR Rd, Rn, Rm | Rd = Rn | Rm | 按位或 |
| BIC Rd, Rn | Rd = Rd & (~Rn) | 位清除 |
| BIC Rd, Rn, #immed | Rd = Rn & (~#immed) | 位清除 |
| BIC Rd, Rn , Rm | Rd = Rn & (~Rm) | 位清除 |
| ORN Rd, Rn, #immed | Rd = Rn | (#immed) | 按位或非 |
| ORN Rd, Rn, Rm | Rd = Rn | (Rm) | 按位或非 |
| EOR Rd, Rn | Rd = Rd ^ Rn | 按位异或 |
| EOR Rd, Rn, #immed | Rd = Rn ^ #immed | 按位异或 |
| EOR Rd, Rn, Rm | Rd = Rn ^ Rm | 按位异或 |



