arm裸机【4】 --- 一步步点亮LED(二)

1、使用位运算实现复杂点亮要求

  1. 用宏定义来定义寄存器名字,再来操作。
  2. 用 b . 来实现死循环
  3. 用.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					// 函数调用返回


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值