上一篇文章中,我们通过操作LED文件位置的brightness文件,控制了LED的亮与灭,并且在DragonBoard 410c上实现了LED的闪烁功能。 同时相信各位也对DragonBoard 410c上的硬件开发有了深入的了解。接下来我们将深入研究其他硬件,并一个一个的手把手进行操作与解释。
无论是输出方波信号或者是处理复杂的通讯协议,我们都要直接或者间接的用到GPIO进行输出与输入。 GPIO是芯片与外界进行通讯最常用的资源之一,其重要性不言而喻。同时它也是Linux中最简单的驱动程序之一。 大部分程序都可以使用提供的接口对GPIO进行控制。
DragonBoard 410c上充满了各种形式的GPIO,如高速口或低速口,还有可以复用成通讯口的GPIO。 于是,Linux中使用0~MAX_INT之间的整数标识来明确指定需要调用的GPIO, 而且标识必须是正整数。 Linux中提供了统一的接口来操作GPIO, 再实际控制GPIO前先向各位介绍GPIO的使用方法。
首先我们可以检测GPIO端口是否可用(合法):
int gpio_is_valid(int number);
其次我们需要将GPIO端口导出到用户空间,这样我们才可以在shell中进行直接控制:
Int gpio_export(unsigned gpio, bool direction_may_change);
参数direction_may_change表示用户程序是否允许修改gpio的方向,假如可以,则参数direction_may_change为真此外,gpio_export的反函数就是gpio_unexport(), 该函数可以将GPIO结点从用户空间移除。
用户空间如何访问gpio呢:一般方法是通过sysfs接口访问gpio,也就是通过操作文件系统中的文件。下面是/sys/class/gpio目录下的三种文件,这三个目录下的文件包含了能够操作GPIO的所有接口:
在进行操作前,开发者必须提前预知以上接口没有device文件与它们连接,接下来对每个文件进行解释:
echo 19 > export
上述操作会为19号gpio创建一个节点gpio19,此时/sys/class/gpio目录下边生成一个gpio19的目录。/sys/class/gpio/unexport和导出的效果相反,比如:
echo 19 > unexport
上述操作将会移除gpio19这个节点。
这个文件节点只有在引脚被配置为输入引脚的时候才存在。 当值是none时可以通过如下方法将变为中断引脚:
echo "both" > edge;
对于是both,falling还是rising依赖具体硬件的中断的触发方式
/* ============================================================================
Name : TogglyGPIO.c
Author : ZhouJunyu
Version : 0.0.1
Copyright : Your copyright notice
Description : Basic Hardware access, Ansi-style ============================================================================
*/ #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h>
#define MAX_BUF 10
#define GPIO_A 36
#define GPIO_B 12
int Export_GPIO(int gpio);
int UnExport_GPIO(int gpio);
int Write_GPIO(int gpio, int value);
int Read_GPIO(int gpio, int *value);
本部分首先将指定的GPIO进行export操作,从而在用户空间对GPIO进行控制。 之后通过写函数与读函数对GPIO进行操作。
int main(void)
{
char c=' ';
int ret;
int out_value = 1;
int in_value = 0;
//
//将制定的GPIO口export
//
ret = Export_GPIO(GPIO_A);
if(ret != 0)
printf("Error exporting GPIO_%d", GPIO_A);
ret = Export_GPIO(GPIO_B);
if(ret != 0)
printf("Error exporting GPIO_%d", GPIO_B);
//
//向指定的GPIO口写值
//
printf("press any key to toggle GPIO_A or 'q' to quit:\n");
while(c != 'q'){
printf("Writing GPIO_%d: value=%d \n", GPIO_A, out_value);
ret = Write_GPIO(GPIO_A, out_value);
if (ret != 0)
printf("Error writing GPIO_%d", GPIO_A);
ret = Read_GPIO(GPIO_B, (int*) &in_value);
if (ret != 0)
printf("Error reading GPIO_%d", GPIO_B);
printf("Reading GPIO_%d: value=%d \n", GPIO_B, in_value);
out_value = !out_value;
c= getchar();
}
//
//将指定的GPIO口进行unexport
//
ret = UnExport_GPIO(GPIO_A);
if(ret != 0)
printf("Error UnExporting GPIO_%d", GPIO_A);
ret = UnExport_GPIO(GPIO_B);
if(ret != 0)
printf("Error UnExporting GPIO_%d", GPIO_B);
return 0;
}
该函数将GPIO注册,使得该GPIO可以在用户空间进行操作
int Export_GPIO(int gpio){
int fd;
char buf[MAX_BUF];
//将指定位置的GPIO进行export
//在/sys/class/gpio目录下将生成一个可用的GPIO文件
sprintf(buf, "%d", gpio);
//打开export文件夹并对其进行操作
fd = open("/sys/class/gpio/export", O_WRONLY);
if(fd < 0)
return -1;
write(fd, buf, strlen(buf));
close(fd);
return 0;
}
该函数将GPIO移除出用户空间,使程序无法调用GPIO
int UnExport_GPIO(int gpio){
int fd;
char buf[MAX_BUF];
sprintf(buf, "%d", gpio);
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if(fd < 0)
return -1;
write(fd, buf, strlen(buf));
close(fd);
return 0;
}
本函数根据输入的GPIO口标识符以及值,修改GPIO输出的电平
int Write_GPIO(int gpio, int value){
int fd;
char buf[MAX_BUF];
//将GPIO方向设置为输出
sprintf(buf, "/sys/class/gpio/gpio%d/direction", gpio);
fd = open(buf, O_WRONLY);
if(fd<0)
return -1;
write(fd, "out", 3);// Set out direction
close(fd);
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
fd = open(buf, O_WRONLY);
if(fd<0)
return -1;
// 向GPIO口写值
sprintf(buf, "%d", value);
write(fd, buf, strlen(buf));
close(fd);
return 0;
}
这个意思就不言自明了吧
int Read_GPIO(int gpio, int *value){
int fd;
char val;
char buf[MAX_BUF];
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
fd = open(buf, O_RDONLY);
if(fd<0)
return -1;
// 读取GPIO的值
read(fd, &val, 1);
*value = atoi(&val);
close(fd);
return 0;
}
运行后,用户可以使用键盘控制GPIO的高低电平,按q键可以退出程序。
效果如下图所示:
GPIO的控制相比起LED可能要复杂一些,但是通过本教程,希望各位能够基本了解如何在DragonBoard 410c的嵌入式Linux下的操作。 可能有人会觉得非常复杂而且难以记住,但是由于Linux系统庞大无比,需要处理无数的硬件与软件特性,自然没有简单的STM32等单片机那么轻松自如。 不过如果学会了这些技巧,那么在以后的开发中,将会如鱼得水。