1、使用位运算实现复杂点亮要求
- 用宏定义来定义寄存器名字,再来操作。
- 用 b . 来实现死循环
- 用.global把_start链接属性改为外部,消除链接时的警告
问题提出:如何只点亮中间1颗(两边是熄灭的)LED
分析:程序其实就是写了GPJ0CON和GPJ0DAT这2个寄存器而已,功能更改也要从这里下手。
GPJ0CON寄存器不需要修改,GPJ0DAT中设置相应的输出值即可。
直接解法(不使用位运算)和它的弊端
GPJ0DAT = 0x28
代码见<3.led_s>
总结:
1. 这样写可以完成任务。
2. 这样写有缺陷。缺陷就是需要人为的去计算这个特定的设置值,而且看代码的也不容易看懂。
解决方案:在写代码时用位运算去让编译器帮我们计算这个特定值。
通过位运算点灯
1、使用位运算实现功能
1<<3 等于 0b1000
1<<5 等于 0b100000
(1<<3)|(1<<5) 等于 0b101000
扩展一下:如何只熄灭中间1颗而点亮旁边2颗
ldr r0, =((0<<3) | (1<<4) | (0<<5))
/*
* 文件名: led.s
* 作者: 朱老师
* 描述: 这是我们一步步点亮LED教程的自己写的第一个裸机程序
*/
#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
// 第一步:把所有引脚都设置为输出模式,代码不变
ldr r0, =0x11111111 // 从后面的=可以看出用的是ldr伪指令,因为需要编译器来判断这个数
ldr r1, =GPJ0CON // 是合法立即数还是非法立即数。一般写代码都用ldr伪指令
str r0, [r1] // 寄存器间接寻址。功能是把r0中的数写入到r1中的数为地址的内存中去
// 第二步:把中间LED(GPJ0_4)的输出设置为0,其余两颗设置为1,剩下的其他位不管
//ldr r0, =((1<<3) | (1<<5)) // 等效于0b00101000,即0x28
ldr r0, =((1<<3) | (0<<4) | (1<<5)) // 清清楚楚的看到哪个灭,哪个是亮
ldr r1, =GPJ0DAT
str r0, [r1] // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
b . // .代表当前这一句指令的地址,这个就是高大上的死循环
2、汇编编写延时函数并实现LED闪烁效果
延时函数原理
在汇编中实现延时的方法:用一些没有目的的代码来执行消耗时间,达到延时的效果。
汇编编写延时函数
汇编编写延时函数的原理,用一个寄存器存放一个数字,然后在循环中每个循环里给数字减1,然后再判断这个数字的值是否为0.如果为0则停止循环,如果不为0则继续循环。
汇编编写及调用函数的方式
汇编中整个汇编的主程序是一个死循环,这个死循环是我们汇编程序的主体,类似于C中的main函数。其他函数必须写在这个主死循环程序的后面(死循环外),不然会出错。
汇编编写delay延时函数时,要注意函数的初始化和函数体的位置,不能把初始化写在了循环体内。
汇编中调用函数用bl指令,子函数中最后用mov pc, lr来返回。
#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
// 第一步:把所有引脚都设置为输出模式,代码不变
ldr r0, =0x11111111 // 从后面的=可以看出用的是ldr伪指令,因为需要编译器来判断这个数
ldr r1, =GPJ0CON // 是合法立即数还是非法立即数。一般写代码都用ldr伪指令
str r0, [r1] // 寄存器间接寻址。功能是把r0中的数写入到r1中的数为地址的内存中去
// 第二步:全部点亮
ldr r0, =((0<<3) | (0<<4) | (0<<5)) // 清清楚楚的看到哪个灭,哪个是亮
ldr r1, =GPJ0DAT
str r0, [r1] // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
// 第三步:延时
bl delay // 使用bl进行函数调用
// 第四步:全部熄灭
ldr r0, =((1<<3) | (1<<4) | (1<<5)) // 清清楚楚的看到哪个灭,哪个是亮
ldr r1, =GPJ0DAT
str r0, [r1]
// 第五步:延时
bl delay
// 第六步:点亮
ldr r0, =((0<<3) | (0<<4) | (0<<5)) // 清清楚楚的看到哪个灭,哪个是亮
ldr r1, =GPJ0DAT
str r0, [r1]
// 第七步:全部熄灭
ldr r0, =((1<<3) | (1<<4) | (1<<5)) // 清清楚楚的看到哪个灭,哪个是亮
ldr r1, =GPJ0DAT
str r0, [r1]
// 第八步:延时
bl delay
b . // .代表当前这一句指令的地址,这个就是高大上的死循环
// 延时函数:函数名:delay
delay:
ldr r2, =9000000
ldr r3, =0x0
delay_loop:
sub r2, r2, #1 //r2 = r2 -1
cmp r2, r3 // cmp会影响Z标志位,如果r2等于r3则Z=1,下一句中eq就会成立
bne delay_loop
mov pc, lr // 函数调用返回