跟着正点原子视频学
万事都从点灯起!
类似于stm32的启动文件:startup_stm32f10x_hd.s
它的作用是:
为什么要学习 Cortex-A 汇编:
STM32 IO 初始化流程:
I.MX6ULL IO 初始化:
对于Cortex-A芯片来讲,大部分芯片在上电以后 C语言 环境还没准备好,所以第一行程序肯定是汇编的,至于要写多少汇编程序,那就看你能在哪一步把C语言环境准备好。所谓的C语言环境就是保证C语言能够正常运行。C语言中的函数调用涉及到出栈入栈,出栈入栈就要对堆栈进行操作,所谓的堆栈就是一段内存,这段内存比较特殊,由SP指针访问,SP指针指向栈顶。芯片一上电SP指针还没有初始化,所以 C语言无法运行,对于有些芯片还需要初始化DDR,因此一开始要用汇编来初始化DDR控制器。后面学习Uboot和Linux内核的时候汇编是必须要会的,是不是觉得很难?还要会汇编!前面都说了只是在芯片上电以后用汇编来初始化一些外设,不会涉及到复杂的代码,而且使用到的指令都是很简单的,用到的就那么十几个指令。所以,不要看到汇编就觉得复杂,打击学习信心。
存储器访问指令:
LDR指令:
LDR主要用于 从存储加载数据到寄存器 Rx中,LDR也可以将一个立即数加载到寄存器Rx中,LDR加载立即数的时候要使用 “=” ,而不是 “#” 。在嵌入式开发中,LDR最常用的就是读取CPU的寄存器值,比如 I.MX6UL 有个寄存器 GPIO1_GDIR,其地址为 0x0209C004,我们现在要读取这个寄存器中的数据
// 将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R0, =0X0209C004
// 读取地址 0X0209C004 中的数据到 R1 寄存器中
LDR R1, [R0]
上述代码就是读取寄存器 GPIO_GDIR 中的值,读取到的寄存器值保存在 R1 寄存器中,上面代码 offset 是0,也就是没有用到 offset
STR指令:
LDR是从存储器读取数据,STR就是将数据写入到寄存器中,同样以 I.MX6UL 寄存器 GPIO1_GDIR 为例,现在我们要配置寄存器 GPIO1_GDIR 的值为 0X2000002
// 将寄存器地址 0X0209C004 加载到R0中,即R0=0X0209C004
LDR R0, =0X0209C004
// R1保存要写入到寄存器的值,即 R1=X20000002
LDR R1, =0X20000002
// 将R1中的值写入到R0中所保存的地址中
STR R1, [R0]
LDR 和 STR 都是按照字进行读取和写入的,也就是操作的 32位数据,如果要按照字节、半字节进行操作的话可以在指令 “LDR” 后面加上 B 或 H,比如按字节操作的指令就是 LDRB 和 STRB,按照半字操作的指令就是 LDRH 和STRH
汇编由一条一条指令构成,指令就涉及到汇编指令。
int a, b;
a = b;
假设a地址为0x20,b地址为0x30
LDR R0,=0x30 // R0=b
LDR R1,[R0] // R1=R0 -> R1=b
LDR R0,=0x20 // R0=a
STR R1,[R0] // R0=R1 -> a=R0=R1=b
我们在使用汇编编写驱动的时候最常用的就是 LDR 和 STR 这两个指令
压栈和出栈指令:
我们通常会在 A 函数中调用 B 函数,当 B 函数执行完后再回到 A 函数继续执行。要想在跳回 A 函数以后代码能够接着正常运行,那就必须在跳到 B 函数之前将当前处理器状态保存起来(就是保存 R0~R15 这些寄存器值),当 B 函数执行完成后再用前面保存的寄存器恢复 R0~R15 即可。保存 R0~R15 寄存器的操作就叫做现场保护,恢复 R0~R15 寄存器的操作就叫做恢复现场。在进行现场保护的时候需要进行压栈(入栈)操作,恢复现场就要进行出栈操作。压栈的指令为 PUSH ,出栈的指令为 POP ,PUSH 和 POP 是一种多存储和多加载指令,即可以一次操作多个寄存器数据,他们利用当前的栈指针 SP 来生成地址, PUSH 和 POP 的用法如下表:
指令 | 描述 |
---|---|
PUSH <.reg list> | 将寄存器列表存入栈中。 |
POP <.reg.list> | 从栈中恢复寄存器列表 |
http://www.360doc.com/content/20/0423/21/277688_907959744.shtml
上面网址内容很好的讲解了出栈和入栈
以前的 ARM 处理器有 7 种运行模型:User、FIQ、IRQ、Supervisor(SVC)、Abort、Undef 和 System,其中 User 是非特权模式。但新的 Cortex-A 架构加入了 TrustZone 安全拓展,所以就新加了一种运行模式:Monitor,新的处理器架构还支持虚拟机扩展,因此又加入了另一种运行模式:Hyp,所以 Cortex-A 处理器有 9 种处理模式
模式 | 描述 |
---|---|
User(USR) | 用户模式,非特权模式,大部分程序运行的时候就处于此模式 |
FIQ | 快速中断模式,进入FIQ中断异常 |
IRQ | 一般中断模式 |
Supervisor(SVC) | 超级管理员模式,特权模式,供操作系统使用 |
Monitor(MON) | 监视模式,这个模式用于安全扩展模式,只用户安全 |
Abort(ABT) | 数据访问终止模式,用于虚拟存储以及存储保护 |
Hyp(HYP) | 超级监视模式,用于虚拟化扩展 |
Undef(UND) | 未定义指令终止模式 |
System | 系统模式,用于运行特权级的操作系统任务 |
在表中,除了User(USR)用户模式之外,其他8种运行模式都是特权模式,在特权模式下,程序可以访问所有的系统资源。
ARM架构提供了 16个 32位的通用寄存器(R0~R15)供软件使用,前 15个(R0~R14)可以用作通用的数据存储,R15 是程序计数器 PC,用来保存将要执行的指令。ARM还提供了一个当前程序状态寄存器 CPSR 和一个备份程序状态寄存器 SPSR,SPSR寄存器就是 CPSR寄存器的备份。
.global _start @全局标号
_start:
/*使能所有外设时钟*/
ldr r0, =0x020c4068 @ CCGR0
ldr r1, =0xffffffff @ 要向CCGR0写入打数据
str r1, [r0] @ 将0xffffffff写入到CCGR0中
ldr r0, =0x020c406c @ CCGR1
str r1, [r0] @ 将0xffffffff写入到CCGR1中
ldr r0, =0x020c4070 @ CCGR2
str r1, [r0] @ 将0xffffffff写入到CCGR2中
ldr r0, =0x020c4074 @ CCGR3
str r1, [r0] @ 将0xffffffff写入到CCGR3中
ldr r0, =0x020c4078 @ CCGR4
str r1, [r0] @ 将0xffffffff写入到CCGR4中
ldr r0, =0x020c407c @ CCGR5
str r1, [r0] @ 将0xffffffff写入到CCGR5中
ldr r0, =0x020c4080 @ CCGR6
str r1, [r0] @ 将0xffffffff写入到CCGR6中
/*配置 GPIO1——IO03 PIN的复用为GPIO,也就是设置
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03=5
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03寄存器地址为0X020E0068
*/
ldr r0, =0x020E0068 @ IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03
ldr r1, =0x5 @ 要写入到数据
str r1, [r0] @ 将5写入到IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03中
/*配置GPIO1——IO03到电气属性 也就是寄存器:
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03寄存器地址为0x020e02f4
bit0: 0 低速率
bit2-1: 保留
bit5-3: 110 R0/6驱动能力
bit7-6: 10 100MHz速度
bit10-8: 保留
bit11: 0 关闭开路输出
bit12: 1 使能pull/keeper
bit13: 0 keeper
bit15-14: 00 100k下拉
bit16: 0 关闭HYS
bit31-17: 保留
*/
ldr r0, =0x020e02f4
ldr r1, =0x10b0
str r1, [r0]
/* 设置GPIO
设置GPIO1_GDIR寄存器,设置GPIO1_GPIO03为输出
GPIO1_GDIR寄存器地址为0x0209c004,设置GPIO1_GDIR寄存器bit3为1
也就是设置GPIO1_GPIO03为输出
*/
ldr r0, =0x0209c004
ldr r1, =0x8
str r1, [r0]
/* 打开LED,也就是设置GPIO1_IO03为0
GPIO1_DR寄存器地址为0x0209c000
*/
ldr r0, =0x0209c000
ldr r1, =0
str r1, [r0]
loop:
b loop
1、编译程序:
链接:
STM32烧写到内部 FLASH
6ULL 支持 SD卡,EMMC、NAND、nor、SPI flash 等等启动。裸机例程选择烧写到SD卡里面。
在Ubuntu下向SD卡烧写裸机 bin文件。烧写不是将 bin文件 拷贝到 SD卡中,而是将 bin文件烧写到SD卡 绝对地址上。而且对于 LMX 而言,不能直接烧写bin文件,比如先在bin文件前面添加头部。完成这个工作,需要正点原子提供的 imxdownload软件。
imxdownload 使用方法,确定要烧写的SD卡文件,我的是 /dev/sdb
给予 imxdownload 可执行权限:chmod 777 imxdownload
烧写:/imxdownload led.bin /dev/sdb
imxdownload 会向 led.bin 添加一个头部,生成新的 load.imx文件,这个 load.imx 文件就是最终烧写到SD卡里面去的。
JLINK:
1、启动方式的选择:
LED灯实验,是从 SD 卡读取 bin 文件并启动,说明 6ULL支持从 SD 卡启动。6ULL 支持多种启动方式。6ULL是怎么支持从多种外置 flash 启动程序的。
2、启动设备的选择:
1、Boot Rom做的事情
2、IVT 和 Boot Data数据
3、DCD数据(Device Configuration Data)
在前面我们用汇编的形式来编写LED灯驱动,但由于汇编太难,而且写出来也不好理解,所以大部分情况下汇编就是在工程开始时初始化一下C语言环境,比如初始化 DDR ,设置堆栈指针 SP 等等,当这些工作都做完以后就可以进入C语言环境,也就是运行C语言代码,一般是进入main函数,所以我们有两部分文件要做:
0x80000000~0x9FFFFFFF
。栈大小,0x200000=2MB.处理器栈增长方式,对于A7而言是向下增长的。设置SP指向 0x80200000。对于STM32而言,使用一个结构体将一个外设的所有寄存器都放到一起。