https://zhuanlan.zhihu.com/p/27242098
图解嵌入式系统开发之驱动篇:LED驱动
实验平台详见: 有没有国产的类似树莓派这样的廉价linux主机? - 知乎
硬件层面:
(图一 LED 原理图)
led的硬件连接很简单,负极通过一个限流电阻连接到地,正极接到主控的一个GPIO口上(GPIO1_D0),只要IO拉高,LED就被点亮。所以,我们本节课要做的只有一件事情,那就是穷尽各种手段让这个IO口拉高就就好了。
IC层面:
(图二 GPIO口的内部图)
GPIO的内部结构如上图,也是非常的简单,一个方向寄存器控制IO口作为输入还是输出,一个数据寄存器,数据寄存器控制输出电平(数据寄存器写1:IO输出高电平, 相反,写0:输出低电平)。 或保存输入值(gpio作为输入使用时),所以我们软件要做的就是去配置这两个寄存器。
该开发板使用的瑞芯微公司的旗舰级主控RK3288(内部继承了4个A17的ARM内核,最高频率可达1.8G),通过查看芯片的数据手册,我们可以找到上述GPIO1寄存器的基地址地址为0xff780000,数据寄存器偏移为0(所以,其完整地址0xff780000),方向寄存器的偏移为4(完整地址为0xff780004)。这样我们可以在系统中通过io命令直接设置这两个寄存器的值。
(方式一)在文件系统中通过io命令点亮led
io -4 0xff780004 0x1000000 (设置gpio1_d0方向为输出)
io -4 0xff780000 0x1000000 (设置gpio1_d0输出高电平)
然后led等就被点亮了。
(方式二)编写内核驱动,通过操作寄存器,点亮LED。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
//GPIO1 base address according to rk3288 datasheet
#define GPIO1_BASE 0xff780000
// GPIO register map define
struct gpio {
u32 data; //gpio data register
u32 direction;//gpio direction register
} *gpio1;
//led module init。
int __init led_init(void)
{
//remap the gpio1 physic address to virtual address
gpio1 = (struct gpio *)ioremap(GPIO1_BASE, 8);
//write 1 at bit24 to set gpio1_b0 direction as output
writel(1 << 24, &gpio1->direction);
//write 1 at bit24 to set gpio1_b0 as high
writel(1 << 24, &gpio1->data);
return 0;
}
//led module exit.
void __exit led_exit(void)
{
//Cancel gpio1 remap
iounmap(gpio1);
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("Eric Gao");
MODULE_LICENSE("GPL");
如上,该代码关键部分就两句。
(1)writel(1 << 24, &gpio1->direction);
设置gpio1_b0方向为输出
(2)writel(1 << 24, &gpio1->data);
设置gpio1_b0输出高电平
其他的部分都是作为一个内核驱动模块的模板式代码,这里不在赘述。
(方式三) 在文件系统中,通过内核提供的接口操作GPIO
进入文件系统,可以在/sys/class/gpio/ 目录下看到如下文件及目录。
以上就是内核提供的gpio接口,通过该接口点亮led的过程非常简单。要注意的一点是gpio的标记方式, 比如我们led接的是GPIO1_d0, 那么这里该gpio的标号就是(1*32 + 3*8 = 56)如下。
(1)echo 56 > export (将标号为56的GPIO导出到文件系统)
执行上述命令会在/sys/class/gpio/会自动生成gpio56文件夹,文件夹内包含了我们可以读写的一些gpio的属性,包括方向,输出电平等。
(2)echo out > gpio56/direction(设置gpio方向为输出)
(3)echo 1 > gpio56/value (使该GPIO输出高电平)
然后,LED就成功被点亮了。
(方式四) 通过用户态程序结合内核GPIO接口实现LED闪烁
led.c:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
system("echo 56 > /sys/class/gpio/unexport");
system("echo 56 > /sys/class/gpio/export");
system("echo 'out' > /sys/class/gpio/gpio56/direction");
while (1) {
system("echo 0 > /sys/class/gpio/gpio56/value");
usleep(500000);
system("echo 1 > /sys/class/gpio/gpio56/value");
usleep(500000);
}
system("echo 56 > /sys/class/gpio/unexport");
return 0;
}
上述代码通过内核gpio接口实现led等的闪烁控制。编译方式如下。
gcc -o led.o led.c
如果觉得有帮助,请帮忙点个赞或分享给其他人,多谢,你们的赞同将是我最大的动力,希望大家共同进步。