led驱动电路:led1 --> GPF4 led2 --> GPF5 led4 --> GPF6
方案一:蛮力破解法。直接安排多个子程序,在各个子程序中点亮一个led,轮转调用子程序点灯。
.text
.global _start
_start:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/*led1 --> GPF4
**led2 --> GPF5
**led4 --> GPF6
**gpfcon 0x56000050
**gpfdat 0x56000054
*/
ldr r0,=0x56000050 //配置gpfcon寄存器让对应引脚为输出模式
ldr r1,=0x00001500
str r1,[r0]
ldr r0,=0x56000054 //gpfdat
ldr r1,=0x00000070 //全灭
str r1,[r0]
led3:
ldr r0,=0x56000054 //gpfdat
ldr r1,=0x00000030 //亮led4
str r1,[r0]
bl delay
led2:
ldr r0,=0x56000054 //gpfdat
ldr r1,=0x00000050 //亮led2
str r1,[r0]
bl delay
led1:
ldr r0,=0x56000054 //gpfdat
ldr r1,=0x00000060 //亮led1
str r1,[r0]
bl delay //在跳转前,将pc保存在R14寄存器中(lr)
b led3 //直接跳转
delay:
mov r0, #0x20000
1: subs r0, r0, #1
bne 1b
mov pc,lr //修改pc返回
halt:
b halt
方案二:利用IO口连续的优势。对某特殊值逻辑右移,在每次右移之后,对应的二进制0/1位刚好吻合跑马灯gpio的高低电平要求。因为逻辑右移空出的高位自动补0。所以,当在临界值时候,应该重新赋值,让其继续移位输出。
.text
.global _start
_start:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/*led1 --> GPF4
**led2 --> GPF5
**led4 --> GPF6
**gpfcon 0x56000050
**gpfdat 0x56000054
*/
ldr r0,=0x56000050 //配置gpfcon寄存器让对应引脚为输出模式
ldr r1,=0x00001500
str r1,[r0]
ldr r0,=0x56000054 //gpfdat
ldr r1,=0x00000070 //全灭
str r1,[r0]
//灯从左往右跑
loop:
ldr r1,=0xb6db6db6 //1011 0110 1101 1011 0110 1101 1011 0110
run:
bl delay
str r1,[r0]
mov r1,r1,lsr#1 //逻辑右移1
cmp r1,#182 //临界值 0000 0000 0000 0000 0000 0000 1011 0110B
bne run //当r1右移到某一值时返回重新赋值
b loop
delay:
mov r2, #0x20000
1: subs r2, r2, #1
bne 1b
mov pc,lr
halt:
b halt
其中,cmp将r1和182作比较,就是两者相减,仅影响cpsr状态寄存器中的标志位,不保存运算结果。如果运算结果不为零,则通过ben run指令跳转到run,从run开始执行。
方案三:和方案二有点类似。利用IO口连续的优势。对某特殊值 循环右移,在每次右移之后,对应的二进制0/1位刚好吻合跑马灯gpio的高低电平要求。但是需要考虑cpsr状态寄存器中的C进位标志的处理与保护。
.text
.global _start
_start:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/*led1 --> GPF4
**led2 --> GPF5
**led4 --> GPF6
**gpfcon 0x56000050
**gpfdat 0x56000054
*/
ldr r0,=0x56000050 //gpfcon
ldr r1,=0x00001500
str r1,[r0]
ldr r0,=0x56000054 //gpfdat
ldr r1,=0x00000070 //全灭
str r1,[r0]
mrs r3,cpsr //读出cpsr的值保存在r3
orr r3,r3,#0x20000000 //进位标志置1
msr cpsr_f,r3 //仅修改条件标志位域中的C进位标志
ldr r1,=0xb6db6db6
//灯从左往右跑
loop:
str r1,[r0]
movs r1,r1,rrx //循环右移1,并且影响cpsr寄存器
bl delay
b loop
delay:
mrs r3,cpsr //保存当前的状态寄存器
mov r2, #0x20000
1: subs r2, r2, #1
bne 1b
msr cpsr_f,r3
mov pc,lr
halt:
b halt
在延时子程序中,对状态寄存器的处理,主要是为了保护C进位标志的值,因为延时的过程中C的值已经被指令 subs r2, r2, #1 修改了。若放任不管,这会破坏数值的逻辑,使得led全灭或全亮。
以上代码,在韦东山的jz2440的开发板上都能正常运行,个人比较喜欢第二种方法。
需要注意:如果没有关闭看门狗,则过一段时间会自动复位,导致流水灯失败。