跟着韦老师学Linux学习笔记(一)-GPIO

学编程,第一个程序肯定是“hello world”,学单片机或ARM,最初学的肯定是GPIO的操作。在这一节课里主要涉及到了点亮LED和按键控制LED。

1、要想点亮一个LED,分以下几个步:

①、了解相关硬件

②、配置Gpio的相关寄存器

③、编写相关代码

④、编译下载

2、一般来说,要想点亮LED,只需要控制相关的gpio口输出高电平或低电平就可以了。通过查看2440的原理图可以知道(如下图所示):

 

3个LED的阴极经过1K的限流电阻连接到GPF4、GPF5和GPF6三个Gpio口上了。因此我们只要让PF4、GPF5和GPF6输出低电平,就能点亮LED,高电平熄灭LED。

3、查看2440的DataSheet 可以知道,Gpio主要有三个寄存器需要设置,再本篇中分别是GPFCON、GPFDAT和GPFUP。

 

其中:

     GPFCON是控制寄存器,主要是控制GPIO的功能,主要有输入、输出和中断三个功能。其中每个GPIO口有寄存器的两位来控制,00:输入,01:输出,10:中断,10:保留。

     GPFDAT是数据寄存器,主要是控制GPIO输出高电平还是低电平,0:低电平,1:高电平。

     GPFUP 是设置内部上拉电阻的寄存器,0:不设置上拉电阻,1:设置上拉电阻。

4、编辑代码

在教程中的第一个代码使用汇编语言编写的,代码非常简单。如下:

 1 .text
 2 .global _start
 3 _start:
 4 LDR    R0,=0x56000050
 5 MOV R1,#0x00000400
 6 STR    R1,[R0]    
 7 LDR    R0,=0x56000054    
 8 MOV    R1,#0x00000000
 9 STR    R1,[R0]
10 MAIN_LOOP:
11     B        MAIN_LOOP

 

 

 下面主要分一下这个代码:

.text:这个是GCC编译器里面的一个关键词,它指定了后续编译出来的代码放在可执行代码段,是处理器开始执行代码的地方。

.global _start 告诉编译器后续跟的是一个全局可见的名字(可能是变量,也可以是函数名),.global让 _start 符号成为可见的标识符,这样链接器就知道跳转到程序中的什么地方并开始执行。_start是一个函数的起始地址,也是编译、链接后程序的起始地址。由于程序是通过加载器来加载的,必须要找到 _start名字的函数,因此_start必须定义成全局的,以便存在于编译后的全局符合表中,供其它程序寻找到。

_start是程序的起始地址

LDR R0,=0x56000050

MOV R1,#0x00000400

STR R1,[R0]

这三句主要实现了对GPFCON寄存器的设置,这里首先是把R0设为了GPFCON的寄存器地址,然后给R1赋值,最后把R1的值赋到以R0为地址的寄存器里,从而配置相应端口的功能。这里是0x00000400,正好是GPFCON的[11:10]设置为了01 ,即是GPF5位输出功能。

LDR R0,=0x56000054                

MOR R1,#0x00000000

STR R1,[R0]

这三句代码和上面类似,只要实现了GPFDAT输出低电平,这里是GPF5输出低电平,因为只有GPF5端口被设置为了输出功能。

MAIN_LOOP:

           B MAIN_LOOP

这句是个死循环没有什么太大意思。

5、编译下载

程序的编译命令时通过编写Makefile文件完成的。

1 led_on.bin : led_on.S
2     arm-linux-gcc -g -c -o led_on.o led_on.S
3     arm-linux-ld -Ttext 0x0000000 -g led_on.o -o led_on_elf
4     arm-linux-objcopy -O binary -S led_on_elf led_on.bin
5 clean:
6     rm -f   led_on.bin led_on_elf *.o

 

arm-linux-gcc:

           -g:产生供gdb调试用的可执行文件

           -c:只编译不连接

           -o: 指定输出文件名。这里指定的是生成led_on.o

arm-linux-ld:      

           -o:指定输出文件名。这里生成lec_on.elf。

           -Ttext: 设置代码段的起始地址:0x0000000。

arm-linux-objcopy:

           -O: 输出的格式, binary指二进制文件。这里生成led_on.bin。

           -S: 不从源文件中复制重定位信息和符号信息到目标文件中

Clean:

           是清除命令,清除make生成的文件,这里指的是清除led_on.bin 、led_on_elf以及所有的.o文件。*是通配符。

然后我们linux环境下,执行make命令来编译文件生成可执行文件并下载到开发板上,可以观察到led2被点亮。开发板自带的有下载教程“如何烧写S3C2440裸板程序.pdf”。

 

 

在接下来的视频教程中,主要讲解了如何用c语言来编写程序点亮led灯。用c程序来操作gpio口,首先要设置相关硬件,关看门狗,设置栈,这些功能是在汇编文件里编写的。

 1 .text
 2 .global _start
 3 _start:
 4                 ldr        r0, =0x53000000
 5                 mov        r1, #0x0
 6                 str        r1,[r0]                
 7                 ldr        sp, =1024*4
 8                 bl        main
 9 halt_loop:
10                 b    halt_loop

 

 

代码和上面的类似:

ldr           r0, =0x53000000

mov           r1, #0x0

str           r1,[r0]   

这三句代码是通过写0关闭看门狗,否则CPU会不断地重启。

ldr           sp, =1024*4

设置我们的堆栈空间的大小,这里设置为4K,因为2440的内部RAM只有4K。

bl            main

调用我们写的c函数

接下来是个死循环。

C函数:

1 #define GPFCON (*(volatile unsigned long *) 0x56000050)
2 #define GPFDAT (*(volatile unsigned long *) 0x56000054)
3 int main()
4 {
5     GPFCON = 0x00000100;
6     GPFDAT = 0x00000000;
7     
8     return 0;
9 }

 

 

在这里最重要的是GPFCON和GPFDAT这两个宏的定义,这是指向这两个寄存器地址的指针。

例如:

int a;

int *p;

 

p=&a;

*p = 0x100;//这就是把变量a赋值为0x100

在我们的程序中:

地址是(volatile unsigned long *) 0x56000050,即是p=(volatile unsigned long *) 0x56000050,*p=(*(volatile unsigned long *) 0x56000050),GPFCON和GPFDAT两个宏的意思就是*p。

其他就是往寄存器里赋值就行。

编译下载:

1 led_on_c.bin : crt0.S  led_on_c.c
2     arm-linux-gcc -g -c -o crt0.o crt0.S
3     arm-linux-gcc -g -c -o led_on_c.o led_on_c.c
4     arm-linux-ld -Ttext 0x0000000 -g  crt0.o led_on_c.o -o led_on_c_elf
5     arm-linux-objcopy -O binary -S led_on_c_elf led_on_c.bin
6     arm-linux-objdump -D -m arm  led_on_c_elf > led_on_c.dis
7 clean:
8     rm -f led_on_c.dis led_on_c.bin led_on_c_elf *.o

 

这个Makefile只是比前面多了一句:

arm-linux-objdump -D -m arm  led_on_c_elf > led_on_c.dis

只一句是用来生成反汇编代码的语句,

           -D:反汇编所有代码。

           -m:反汇编目标文件时使用的架构。这里是arm架构。

下载如上。

 

接下来的流水灯实验和按键控制led的实验,本质上与上面的c程序类似,重要点主要是涉及的位操作。

 1 #define    GPF4_out    (1<<(4*2))
 2 #define    GPF5_out    (1<<(5*2))
 3 #define    GPF6_out    (1<<(6*2))
 4 
 5 #define    GPF4_msk    (3<<(4*2))
 6 #define    GPF5_msk    (3<<(5*2))
 7 #define    GPF6_msk    (3<<(6*2))
 8         // LED1,LED2,LED4对应的3根引脚设为输出
 9         GPFCON &= ~(GPF4_msk | GPF5_msk | GPF6_msk); //对寄存器的相应位的清零操作。
10         GPFCON |= GPF4_out | GPF5_out | GPF6_out;//对寄存器的相应位的赋值操作。

 

 

位操作的好处是,清零时只清零相应位,赋值时也只赋值相应位,对其他为不产生影响,防止破坏程序。

 

好了,第一节课的笔记到此为止,这也是第一次写博客,没什么太难的内容,只是上课的一些笔记(记性不好,还是写下来忘了方便查看)。再接再厉。

 

posted @ 2017-02-27 09:55  miyazono  阅读(696)  评论(0编辑  收藏  举报