一步一步点亮Led

1.了解物理特性

点亮LED的关键在于电压差

2.查阅原理图了解板载LED的硬件接法

方法:可以利用PDF文档的搜索功能(搜索LED即可,在底板搜索)

可知:有4颗正极接3.3V,负极分别接SoC上面的引脚(即引脚低电平亮),另外一个开机常亮。

3个SoC引脚是知道的,另外在核心板上查看pwmtout1可知引脚为GPD0_1,如下图

GPD0表示端口号,后面的1是引脚号

3.查阅数据手册

当我们想要编程操控GPIO来操作LED时,需要通读一下数据手册关于GPIO的部分

我们要操作的硬件为LED, 通过GPIO间接控制,实际要操作的设备是GPIO。

3.1、GPJ0相关的寄存器如下

其中,最主要的寄存器有:GPJ0_DAT, GPJ0CON

3.2、以下为GPJ0CON寄存器:

GPJ0这个端口的第一个引脚GPJ0_0对应寄存器GPJ0_CON的bit0~bit3, 点亮LED时应该将这个引脚配置成output模式。

3.3、GPJ0DAT寄存器介绍

GPJ0DAT[7:0]后面的7:0表示GPJ0对应的0~7引脚,后面的BIT[7:0]表示GPJ0DAT寄存器的bit0~bit7,从这里可以知道,
GPJ0端口的每一个引脚分别对应GPJODAT寄存器的每一个bit位。
 
3.4、总结
根据以上,我们就知道如何点亮LED
(1)操控GPJ0CON寄存器,选中为output模式。
(2)操控GPJ0DAT寄存器,选择相应位为低电平。
 
4.一步一步点亮le
4.1、点亮led灯
 1 _start:
 2     // 第一步:把0x11111111写入0xE0200240(GPJ0CON)位置
 3     ldr r0, =0x11111111            // 从后面的=可以看出用的是ldr伪指令,因为需要编译器来判断这个数
 4     ldr r1, =0xE0200240            // 是合法立即数还是非法立即数。一般写代码都用ldr伪指令
 5     str r0, [r1]                // 寄存器间接寻址。功能是把r0中的数写入到r1中的数为地址的内存中去
 6 
 7     // 第二步:把0x0写入0xE0200244(GPJ0DAT)位置
 8     ldr r0, =0x0
 9     ldr r1, =0xE0200244
10     str r0, [r1]                // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
11 
12 flag:                            // 这两行写了一个死循环。因为裸机程序是直接在CPU上运行的,CPU会
13     b flag                        // 逐行运行裸机程序直到CPU断电关机。如果我们的程序所有的代码都
14                                 // 执行完了CPU就会跑飞(跑飞以后是未定义的,所以千万不能让CPU
15                                 // 跑飞),不让CPU跑飞的办法就是在我们整个程序执行完后添加死循环
View Code

4.2、改进代码1

 1 #define GPJ0CON    0xE0200240
 2 #define GPJ0DAT    0xE0200244
 3 
 4 .global _start                    // 把_start链接属性改为外部,这样其他文件就可以看见_start了
 5 _start:
 6     // 第一步:把所有引脚都设置为输出模式,代码不变
 7     ldr r0, =0x11111111            // 从后面的=可以看出用的是ldr伪指令,因为需要编译器来判断这个数
 8     ldr r1, =GPJ0CON            // 是合法立即数还是非法立即数。一般写代码都用ldr伪指令
 9     str r0, [r1]                // 寄存器间接寻址。功能是把r0中的数写入到r1中的数为地址的内存中去
10 
11     // 第二步:把中间LED(GPJ0_4)的输出设置为0,其余两颗设置为1,剩下的其他位不管
12     ldr r0, =0x28
13     ldr r1, =GPJ0DAT
14     str r0, [r1]                // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
15 
16     b .                            // .代表当前这一句指令的地址,这个就是高大上的死循环
View Code

上述代码改进:

(1)用宏定义来替换地址的数字

(2)用b .来替换以前的死循环

(3)加上.global _start(将_start的链接属性改为外部链接)

改变:代码把4.1中点亮的led改为点亮中间的led。

4.3、改进代码2

在4.2中的缺陷:需要人为的计算0x28这个特定值,而且可读性不好。

解决方案:在写代码时用位运算让编译器帮我们计算特定值。
 1 #define GPJ0CON    0xE0200240
 2 #define GPJ0DAT    0xE0200244
 3 
 4 .global _start                    // 把_start链接属性改为外部,这样其他文件就可以看见_start了
 5 _start:
 6     // 第一步:把所有引脚都设置为输出模式,代码不变
 7     ldr r0, =0x11111111            // 从后面的=可以看出用的是ldr伪指令,因为需要编译器来判断这个数
 8     ldr r1, =GPJ0CON            // 是合法立即数还是非法立即数。一般写代码都用ldr伪指令
 9     str r0, [r1]                // 寄存器间接寻址。功能是把r0中的数写入到r1中的数为地址的内存中去
10 
11     // 第二步:把中间LED(GPJ0_4)的输出设置为0,其余两颗设置为1,剩下的其他位不管
12     //ldr r0, =((1<<3) | (1<<5))    // 等效于0b00101000,即0x28
13     ldr r0, =((1<<3) | (0<<4) | (1<<5))    // 清清楚楚的看到哪个灭,哪个是亮
14     ldr r1, =GPJ0DAT
15     str r0, [r1]                // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
16 
17     b .                            // .代表当前这一句指令的地址,这个就是高大上的死循环
View Code

4.4、改进代码3

加上延时函数和一些功能

 1 #define GPJ0CON    0xE0200240
 2 #define GPJ0DAT    0xE0200244
 3 
 4 .global _start                    // 把_start链接属性改为外部,这样其他文件就可以看见_start了
 5 _start:
 6     // 第一步:把所有引脚都设置为输出模式,代码不变
 7     ldr r0, =0x11111111            // 从后面的=可以看出用的是ldr伪指令,因为需要编译器来判断这个数
 8     ldr r1, =GPJ0CON            // 是合法立即数还是非法立即数。一般写代码都用ldr伪指令
 9     str r0, [r1]                // 寄存器间接寻址。功能是把r0中的数写入到r1中的数为地址的内存中去
10 
11     // 要实现流水灯,只要在主循环中实现1圈的流水显示效果即可
12 flash:
13     // 第1步:点亮LED1,其他熄灭
14     //ldr r0, =((0<<3) | (1<<4) | (1<<5))    // 清清楚楚的看到哪个灭,哪个是亮
15     ldr r0, =~(1<<3)
16     ldr r1, =GPJ0DAT
17     str r0, [r1]                // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
18     // 然后延时
19     bl delay                    // 使用bl进行函数调用
20     
21     // 第2步:点亮LED2,其他熄灭    
22     ldr r0, =~(1<<4)
23     ldr r1, =GPJ0DAT
24     str r0, [r1]                // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
25     // 然后延时
26     bl delay                    // 使用bl进行函数调用
27     
28     // 第3步:点亮LED3,其他熄灭    
29     ldr r0, =~(1<<5)
30     ldr r1, =GPJ0DAT
31     str r0, [r1]                // 把0写入到GPJ0DAT寄存器中,引脚即输出低电平,LED点亮
32     // 然后延时
33     bl delay                    // 使用bl进行函数调用
34     
35     b flash
36 
37 
38 // 延时函数:函数名:delay
39 delay:
40     ldr r2, =9000000
41     ldr r3, =0x0
42 delay_loop:    
43     sub r2, r2, #1                //r2 = r2 -1
44     cmp r2, r3                    // cmp会影响Z标志位,如果r2等于r3则Z=1,下一句中eq就会成立
45     bne delay_loop
46     mov pc, lr                    // 函数调用返回
View Code

关键知识点:

(1)B常用于不返回的跳转,比如跳到某个标号处,BL则用于子程序跳转(要返回,返回地地存于LR)

(2)在子函数中一般用mov pc, lr 这句指令来用于调用返回

(3)在延时函数中 

cmp r2, r3     // cmp会影响Z标志位,如果r2等于r3则Z=1,下一句中eq就会成立

bne delay_loop

ne为条件后缀,成立则执行这条指令

/********************************************************

 * 以上知识来自于朱老师物联网大讲堂

 *******************************************************/

原文地址:https://www.cnblogs.com/zou27/p/5022460.html