```bash
/*
* 文件名: led.s
* 作者: XXX
* 描述: 汇编开关icache
*/
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
// 第1步:关看门狗(向WTCON的bit5写入0即可)
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]
// 第2步:设置SVC栈
ldr sp, =SVC_STACK
// 第3步:开/关icache
mrc p15,0,r0,c1,c0,0; // 读出cp15的c1到r0中
//bic r0, r0, #(1<<12) // bit12 置0 关icache
orr r0, r0, #(1<<12) // bit12 置1 开icache
mcr p15,0,r0,c1,c0,0;
// 从这里之后就可以开始调用C程序了
bl led_blink // led_blink是C语言实现的一个函数
// 汇编最后的这个死循环不能丢
b .
按照上面的代码,开始总结。
1.global指令
这条指令用于在链接阶段寻找文件中的函数名。
比如,开始位置只写一个函数名称,那么在编译阶段就会提示警告,正确的做法是:
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
2. ldr 和 str
这两条指令是ARM架构汇编的特点之一,ARM采用数据和指令分开存储的方式,CPU不能直接读取内存,而是通过寄存器的方式。相对于CPU的寄存器而言,load就是将内存内容加载到寄存器;store就是将寄存器内容存储到内存中。
ldr r0, =WTCON 将STCON地址对应的内容加载到寄存器r0中。
ldr r1, =0x0 将0x0地址对应内容加载到r1中。
str r1, [r0] 将r1内容存储在[r0]对应的内存中去。
ldr sp, =SVC_STACK
- ldr sp, =SVC_STACK 这条指令是在这是C语言的运行时栈,设置了它,就可以运行C语言了。
3. 协处理器指令:mcr 和 mrc 指令
mrc用于读取CP15中的寄存器;mcr用于写入cp15中的寄存器。
什么是协处理器(coprocessor)?
协处理器是SoC内部的另一处理器核心,协助CPU实现某些功能,被主CPU调用去执行一定的任务。ARM支持16个协处理器。
mrc p15,0,r0,c1,c0,0; // 读出cp15的c1到r0中
这句汇编的意思是:将协处理器寄存器c0 c1中的内容加载到ARM寄存器r1中;
mcr p15,0,r0,c1,c0,0;
这句代码意思是:将r0寄存器内容送入协处理器的c0 c1 寄存器中。
4. bic 和 orr
bic是某些寄存器的某些位;orr是将某些位置1
//bic r0, r0, #(1<<12) // bit12 置0 关icache
orr r0, r0, #(1<<12) // bit12 置1 开icache
5. bl 有返回的调用函数
bl 指令是有返回的调用函数,调用函数之前保存了现场。
6. 汇编语言死循环写法
b .
b 和 bl 区别:b直接跳转,没有想返回,所以也不用保存值。
7. 再来看看汇编的延时函数写法
delay:
ldr r2, =3000000 进行30万次减法
ldr r3, =0x0
delay_loop:
sub r2, r2, #1 // r2 = r2 - 1
cmp r2,r3 // cmp会影响Z标志位,如果 r2 r3 相等,则z=1, 下一句中beq就会成立。
bne delay_loop // 不相等就继续执行减法,继续延时, delay_loop ,否则直接执行mov pc,lr
mov pc,lr // 程序返回调用的位置,lr保存了现场。