今天开始从ARM的最简单的部分开始写博客了
那就是通过设置GPIO来点亮LED灯。
第一步,先看原理图和硬件工作原理
由硬件工作原理图我们可以知道,要想LED亮,只需要将GPJ0端设置为低电平,并且为输出状态,就可以了,这里我们只设置前面三个GPJ0端口的。
对应的GPIO是:GPJO_3,GPJO_4,GPJO_5,
第二步,查看数据手册
(1)第一步,看涉及哪些寄存器
我们一共看到GPJ0相关的有6个寄存器,相应寄存器的作用是:
GPJ0CON:(地址:0xE0200240)(control)控制寄存器,配置各引脚的工作模式
GPJ0DAT:(地址:0xE0200244)(data)当引脚为输入/输出模式时,寄存器相应位和引脚高低电平一致
GPJ0PUD:(pull up down):控制引脚内部弱上拉,下拉
GPJ0DRV:(driver)配置引脚的驱动能力
GPJ0CONPDN:
GPJ0PUDPDN:
总结:也就是我们只需要写GPJ0CON寄存器和GPJ0DAT寄存器就可以了
(2)看GPJ0CON寄存器
往寄存器写1可以设置为输出模式了。(初始值为0,默认为输入模式)
(2)看GPJ0DAT寄存器
这里有两个问题:为什么设置为低电平和输出状态呢?
1.我们都知道电流是从电压高的流向电压低的时候,LED灯就会亮了,而左边端口是接高电压,所以右边端口就肯定是接低电平了,就设置为0就好了。
2.从数据手册我们可以看到:
当端口被设置为输入模式的时候,我们寄存器GPJ0DAT所对应的位的数据(1或者0)是由GPJ0接口上的状态来决定的(高低电平)(也就是寄存器上的值是由GPJ0接口决定的).也就是端口上是低电平,那么寄存器上的值就是0.端口上是高电平,寄存器上的值就是1了。
当端口被设置为输出模式的时候,GPJ0接口上的状态就和我们寄存器上的值是一样的(就是GPJ0接口上的状态是由寄存器上的值来决定的)。也就是说我们往寄存器上写0,端口上就是低电平;我们往寄存器上写1,端口上是就是高电平。
这就是我们为什么用输出模式了。往寄存器写0可以设置为低电平了。(初始值为0,默认为低电平)
第三步,写代码
先用汇编代码来写
(1)功能:全部灯亮
1 _start: //这里用的是ldr的伪命令,就是不用区分合法立即数还是非法立即数,=就是伪,%#就是真 2 ldr r0, =0xE0200240 //0xE0200240对应的是GPJ0CON寄存器,它就是控制接口的状态的,是输出还是输入 3 ldr r1, =0x11111111 //0x11111111就对应着8个接口的状态,1就表示输出状态,0就表示输入状态,每个接口就有 4 str r1, [r0] //8个状态,其他状态可以查数据手册就知道了
5 ldr r0, =0x0 //这里的0表示的就是输出了低电平,如果是1的话就输出的是高电平 6 ldr r1, =0xE0200244 //0xE0200244对应的是GPJ0DAT寄存器,它就是用来输出什么的,是输出高电平还是低电平 7 str r0, [r1]
(2)功能:用位操作来实现指定的灯亮
1 .global _start 2 _start: 3 ldr r0, =GPJ0CON 4 ldr r1, =0x11111111 5 str r1, [r0] 6 7 ldr r0, =(1<<3) | (0<<4) | (1<<5) //这里就是使用了那个或运算,和左移,它的作用和0x2的作用一样 8 ldr r1, =GPJ0DAT //在中间加了那个0是为了比较容易看清楚代码的作用 9 str r0, [r1] 10 11 b . //和那个b . 的作用是一样的啊
(3)功能:一个延迟函数的写法,及其汇编代码的解释
1 delay: 2 ldr r2, =9000000 3 ldr r3, =0x0 4 5 delay_loop: 6 sub r2, r2, #1 //这里就是等价于:sub r2, #1,其实就是r2 = r2 - 1; 7 cmp r2, r3 8 bne delay_loop 9 mov pc, lr 10 11 12 /* 13 总结: 14 1. bne用法: 15 bne指令: 非零则跳转 16 17 个人总结:cmp 和bne连用: 先是用cmp进行那个比较,然后将比较的结果与0比较,如果不为0,则跳到bne紧跟着的标记(如bne sleep,则跳到sleep处)。就是说不相等就跳转。 18 19 2.cmp的用法: 20 cmp是比较指令,cmp的功能相当于减法指令。它不保存结果,只是影响相应的标志位。ZF (Zero Flag):零标志位。 21 它记录相关指令执行后,其结果是否为0 22 其他的指令通过识别这些被影响的标志位来得知比较结果。 23 24 cmp指令格式: cmp 操作对象1, 操作对象2 25 26 计算 操作对象1 - 操作对象2 但不保存结果,只是根据结果修改相应的标志位。 27 也就是说它把标志位修改成了1了。因为不相等就跳转,并且不为零就跳转 28 29 3.b指令和bl指令的区别: 30 B或BL指令引起处理器转移到“子程序名”处开始执行。两者的不同之处在于BL指令在转移到子程序执行之前, 31 将其下一条指令的地址拷贝到R14(LR,链接寄存器)。由于BL指令保存了下条指令的地址, 32 因此使用指令“MOV PC ,LR”即可实现子程序的返回。而B指令则无法实现子程序的返回,只能实现单纯的跳转。 33 用户在编程的时候,可根据具体应用选用合适的子程序调用语句。 34 35 b delay :就可以跳转到delay下面的那个程序那里执行了,和bl delay也可以 36 37 */
用c语言来写
(1)功能:一闪一闪的
1 #define GPJ0CON 0xE0200240 2 #define GPJ0DAT 0xE0200244 3 4 void delay(void); 5 6 //这个函数就用来实现一闪一闪的功能 7 void led_blink(void) 8 { 9 unsigned int *p = (volatile unsigned int *)GPJ0CON; //强制类型转换 10 unsigned int *p1 = (volatile unsigned int *)GPJ0DAT; 11 12 *p = 0x11111111; //把那个接口的模式调为输出模式 13 14 while(1) //死循环 15 { 16 //点亮 17 *p1 = (0<<3)|(0<<4)|(0<<5); 18 19 //延迟 20 delay(); 21 22 //熄灭 23 *p1 = (1<<3)|(1<<4)|(1<<5); //这里写成了那个*p = (1<<3)|(1<<4)|(1<<5),出错了 24 //就是把接口关闭了,所以后面就不亮了 25 //延迟 26 delay(); 27 } 28 29 } 30 31 32 void delay(void) 33 { 34 volatile unsigned int i = 900000; //volatile的意思就是让编译器不对这进行那个优化了 35 //如果进行了优化,就可能起不到延迟的功能了 36 while(i--); 37 }
参考来源:朱老师物联网大教程