写在前面:本系列文章用于记录个人学习过程中的心得体会,可能存在理解偏差或不到位的情况,欢迎指出,互相交流,共同进步!
1.点灯方式
1.通过编写驱动程序点灯。该方法涉及Linux驱动开发的知识,应该是有一套驱动程序框架,主要通过Linux提供的接口open/read/write调用底层驱动。
2.使用GPIO SYSFS系统手动操作GPIO点灯。SYSFS的概念详见如下文章链接。个人理解SYSFS就是提供了一种可以让用户态直接访问GPIO的方式,而原本是应该由内核来访问的。[Linux 基础] -- sysfs 基本原理与使用方法_进程如何使用sysfs-CSDN博客
2.SYSFS点灯步骤
使用SYSFS点灯前提是:清楚要操作的硬件所对应的gpio编号。这需要结合imx6ull的原理图和芯片手册来查看。
2.1 查看芯片的GPIO控制器组
在开发板上访问 /sys/class/gpio/devices 目录,查看一共有几组GPIO
ls /sys/bus/gpio/devices //查看芯片有多少组gpio控制器
//打印的结果
gpiochip0 gpiochip1 gpiochip2 gpiochip3 gpiochip4 gpiochip5
注:Linux中GPIO控制器组从0开始编号,而原理图中LED连接的GPIO5_3,是从1开始编号的,因此应该对应这里的gpiochip4。
2.2 查看每一组GPIO控制器的基编号
这里可以看出从0开始编号,每组32个gpio,由此可以计算出LED的gpio编号为(5-1)*32+3=131。
ls /sys/class/gpio/gpiochip* -d1 //查看每组gpio控制器的第一个gpio口的编号
//打印结果
/sys/class/gpio/gpiochip0
/sys/class/gpio/gpiochip128
/sys/class/gpio/gpiochip32
/sys/class/gpio/gpiochip504
/sys/class/gpio/gpiochip64
/sys/class/gpio/gpiochip96
注:如何验证131就是板子上的LED对应的gpio编号呢?如下图所示查看第4组GPIO的基地址为20ac000,在芯片手册中查找该地址,发现正对应着GPIO5,再偏移3个即可。
2.3 将GPIO引脚导出,使其可以被用户空间程序控制
echo 131 > /sys/class/gpio/export
2.4 将该引脚方向设为out
这里可以理解为设置为输出模式。
echo out > /sys/class/gpio/gpio131/direction
2.5 输出低电平或高电平
根据原理图可知低电平点亮LED。
echo 0 > /sys/class/gpio/gpio131/value
2.6 取消导出引脚
使用结束后可以手动通知系统将该引脚取消导出。
echo 131 > /sys/class/gpio/unexport
3.通过文件IO编写应用程序
上面第2点是手动在命令行进行点灯操作,本质上还是往指定文件里写数据,因此我们可以通过文件IO操作实现一个应用程序来点灯。
上一节编写了QT框架,本节在main函数中对LED初始化操作,在事件处理函数(按钮点击触发)中对LED进行控制即可。
3.1 LED初始化
这里封装一个函数led_init()其实就是实现了2.3和2.4的内容,将两行echo命令通过文件IO的方式向指定文件写入指定内容,直接附上代码。
void led_init()
{
/* The following functions are implemented through file I/O
* echo 131 > /sys/class/gpio/export
* echo out > /sys/class/gpio/gpio131/direction
*/
/* 1.open the target file */
// int fd = open("/sys/class/gpio/export", O_RDWR); //open way not compatible with the file
int fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd == -1)
{
qDebug()<<"/sys/class/gpio/export open error.";
perror("fopen error:");
return;
}
/* 2.write comman to the file */
write(fd, "131\n", 4);
/* 3.close the file */
close(fd);
}
注:这里open的方式是只写,是因为在板子上查看/sys/class/gpio/export的属性是--w只写,因此这里打开方式序要统一。
3.2 LED控制函数
这里这里封装一个函数led_control()其实就是实现2.5节的内容,原理同3.1,代码如下。
void led_control(int on)
{
/* The following functions are implemented through file I/O
* echo 1 > /sys/class/gpio/gpio131/value
* echo 0 > /sys/class/gpio/gpio131/value
*/
/* 1.open the target file */
static int fd = -1; //only open once and keep opening
if(fd == -1)
{
fd = open("/sys/class/gpio/gpio131/value", O_RDWR);
}
if(fd < 0)
{
qDebug()<<"/sys/class/gpio/gpio131/value open error.";
return;
}
/* 2.write comman to the file */
if(on)
{
write(fd, "0\n", 2);
qDebug()<<"LED ON.";
}
else
{
write(fd, "1\n", 2);
qDebug()<<"LED OFF.";
}
}
3.3 事件处理函数
事件处理函数逻辑简单,如下所示。这里只需要注意led_status变量,将其声明为static是因为点击同一个按钮实现开灯/关灯,因此每次点击按钮都会触发该函数,那么需要静态变量来记录当前灯的状态。
/* event handle function */
void MainWindow::on_pushButton_clicked()
{
/* 2.led control */
qDebug()<<"LED Clicked.";
static int led_status = 1; // local static variable only init once
led_control(led_status);
led_status = !led_status; //led flag toggle
}
4.实验上的一些注意事项
注:本节主要是写给自己的备忘录,各位可以自行选择阅读。
4.1 QT找不到头文件
解决办法:需要重新设置QT里的sysroot路径。
- sysroot代表qt会去该路径寻找头文件,先看qt的设置里sysroot路径写的是什么。
- 复制这个路径到ubuntu里,然后随便查找一个程序中包含的头文件,例如types.h。
- 找到其真实路径,然后把这个新的路径复制下来设置为sysroot。
- 在项目文件中添加路径代码。
4.2 环境变量设置
每次开发板重启上电后都需要重新设置环境变量,目前还没学环境变量的永久设置方法,不敢乱改,每次就麻烦一点吧。
export QT_QPA_GENERIC_PLUGINS=tslib:/dev/input/event1
export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0
export QT_QPA_FONTDIR=/usr/lib/fonts/
5.总结
- 学会用SYSFS结合芯片手册查看GPIO引脚和地址对应关系,并通过这种命令实现用户态控制GPIO点灯。
- 通过文件IO的方式实现对GPIO硬件的简单控制。
- 了解了QT的基本操作,有点类似于MFC应用,前面板放置控件,然后写事件处理函数。