在嵌入式Linux开发中,GPIO(General Purpose Input/Output)是最基础也是最常用的外设接口之一。全志系列芯片作为国产嵌入式处理器的代表,其GPIO子系统提供了丰富的硬件资源和灵活的配置方式。本文将基于全志平台,深入讲解GPIO的编程实现细节。
全志芯片的GPIO子系统采用标准的Linux GPIO框架,主要通过sysfs接口和字符设备两种方式进行访问。sysfs方式简单直观,适合快速原型开发;而字符设备方式效率更高,适合生产环境。
GPIO编号的计算公式为:
code复制GPIO编号 = (组号 - 1) * 32 + 引脚号
例如GPIO5_3对应的编号就是(5-1)*32+3=131。这个计算方式在全志系列芯片中是通用的。
在实际硬件连接时需要注意:
GPIO在使用前需要先导出到用户空间,这是通过向/sys/class/gpio/export写入GPIO编号实现的。导出后会在/sys/class/gpio下生成对应的gpioN目录。
关键代码解析:
c复制int sysfs_gpio_export(unsigned int gpio)
{
int fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) {
perror("gpio/export");
return fd;
}
char buf[MAX_BUF];
int len = snprintf(buf, sizeof(buf), "%d", gpio);
write(fd, buf, len);
close(fd);
return 0;
}
这段代码有几个关键点:
注意:导出的GPIO默认权限是root,普通用户使用时需要修改udev规则或使用sudo。
GPIO方向设置是通过direction文件实现的,支持"in"和"out"两种模式:
c复制int sysfs_gpio_set_dir(unsigned int gpio, unsigned int out_flag)
{
char path[MAX_BUF];
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", gpio);
int fd = open(path, O_WRONLY);
if (fd < 0) {
perror(path);
return fd;
}
if (out_flag)
write(fd, "out", 4); // 包含null终止符
else
write(fd, "in", 3);
close(fd);
return 0;
}
方向设置时的常见问题:
在实验板上,我们定义了以下GPIO连接:
c复制#define GPIO4_14 110 // 按键1
#define GPIO5_1 129 // 按键2
#define GPIO5_3 131 // LED
这些定义基于全志的GPIO编号规则,建议在头文件中集中管理,方便维护。
完整的GPIO初始化流程如下:
示例代码:
c复制int main()
{
// 导出GPIO
sysfs_gpio_export(GPIO_KEY1);
sysfs_gpio_export(GPIO_KEY2);
sysfs_gpio_export(GPIO_LED);
// 设置方向
sysfs_gpio_set_dir(GPIO_KEY1, 0); // 输入
sysfs_gpio_set_dir(GPIO_KEY2, 0); // 输入
sysfs_gpio_set_dir(GPIO_LED, 1); // 输出
// 其他初始化...
return 0;
}
对于输出GPIO,通过value文件控制电平:
c复制int sysfs_gpio_set_value(unsigned int gpio, unsigned int value)
{
char path[MAX_BUF];
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio);
int fd = open(path, O_WRONLY);
if (fd < 0) {
perror(path);
return fd;
}
write(fd, value ? "1" : "0", 2);
close(fd);
return 0;
}
对于输入GPIO,读取电平状态:
c复制int sysfs_gpio_get_value(unsigned int gpio)
{
char path[MAX_BUF];
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio);
int fd = open(path, O_RDONLY);
if (fd < 0) {
perror(path);
return -1;
}
char val;
read(fd, &val, 1);
close(fd);
return (val == '1') ? 1 : 0;
}
GPIO无法导出
方向设置失败
电平读取不稳定
在实际项目中,我发现GPIO编号计算是最容易出错的地方。建议编写一个验证函数,检查GPIO编号是否在有效范围内。另外,对于生产环境,建议封装一个完整的GPIO操作库,统一管理所有GPIO资源。