Linux系统GPIO子系统概述

Linux系统GPIO子系统概述

1. GPIO子系统简介

GPIO(General Purpose Input/Output)子系统是Linux内核中负责管理通用输入输出引脚的子系统。它提供了统一的接口来访问和控制SoC(片上系统)的GPIO引脚,使设备驱动和应用程序能够方便地使用这些引脚进行输入输出操作。

1.1 主要功能

  • 引脚控制:设置GPIO引脚的方向(输入/输出)
  • 电平操作:读取和设置GPIO引脚的电平状态(高/低)
  • 中断处理:配置GPIO中断触发方式(上升沿、下降沿、双边沿等)
  • 设备树集成:通过设备树描述GPIO硬件信息和配置
  • 用户空间访问:提供sysfs接口,允许用户空间程序访问GPIO

2. GPIO子系统架构

GPIO子系统采用分层架构设计,从上到下依次为:

2.1 架构层次

层次描述主要组件
应用层用户空间应用和设备驱动应用程序、设备驱动、sysfs接口
核心层GPIO核心gpiolib模块
驱动层平台特定的GPIO驱动各SoC厂商的GPIO驱动
硬件层实际的GPIO硬件SoC GPIO控制器、物理引脚

2.2 核心模块

  • gpiolib:实现GPIO子系统的核心功能,提供统一的API接口
  • gpiochip:表示一个GPIO控制器,管理一组GPIO引脚
  • gpiodevice:表示一个GPIO设备,由gpiochip管理
  • irqchip:与中断子系统的集成,处理GPIO中断

3. GPIO子系统的关键概念

3.1 基本概念

  • GPIO控制器(GPIO Chip):管理一组GPIO引脚的硬件模块
  • GPIO引脚(GPIO Pin):物理引脚,可配置为输入或输出
  • GPIO编号(GPIO Number):系统范围内唯一的GPIO标识符
  • GPIO方向(GPIO Direction):引脚的输入或输出方向
  • GPIO值(GPIO Value):引脚的电平状态(0或1)
  • GPIO中断(GPIO Interrupt):由GPIO状态变化触发的中断

3.2 数据结构

  • struct gpio_chip:表示一个GPIO控制器
  • struct gpio_desc:表示一个GPIO引脚的描述符
  • struct gpio_device:表示一个GPIO设备
  • struct gpio_irq_chip:表示GPIO中断控制器

4. GPIO子系统与设备树

4.1 设备树绑定

GPIO子系统通过设备树来描述GPIO硬件信息和配置,主要包括以下部分:

  1. GPIO控制器节点:描述GPIO控制器的硬件特性和可用引脚
  2. GPIO引脚配置:描述引脚的使用方式和属性
  3. 设备节点引用:设备通过GPIO属性引用特定的GPIO引脚

4.2 设备树示例

4.2.1 GPIO控制器节点
gpio: gpio@1c20800 {
    compatible = "allwinner,sun8i-h3-gpio";
    reg = <0x01c20800 0x400>;
    interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
    #gpio-cells = <3>;
    gpio-controller;
    interrupt-controller;
    #interrupt-cells = <2>;
    clocks = <&ccu CLK_BUS_GPIO>;
    resets = <&ccu RST_BUS_GPIO>;
};
4.2.2 设备节点引用GPIO
leds {
    compatible = "gpio-leds";
    
    led0 {
        label = "status";
        gpios = <&gpio 1 0 GPIO_ACTIVE_HIGH>;
        default-state = "on";
    };
    
    led1 {
        label = "user";
        gpios = <&gpio 2 0 GPIO_ACTIVE_LOW>;
        default-state = "off";
    };
};

button {
    compatible = "gpio-keys";
    
    power {
        label = "Power Button";
        linux,code = <KEY_POWER>;
        gpios = <&gpio 3 0 GPIO_ACTIVE_LOW>;
        debounce-interval = <100>;
    };
};

4.3 GPIO设备树属性说明

  • #gpio-cells:指定GPIO引用所需的单元格数量,通常为2或3
  • gpio-controller:标记该节点为GPIO控制器
  • interrupt-controller:标记该节点为中断控制器
  • #interrupt-cells:指定中断引用所需的单元格数量
  • gpios:引用GPIO引脚,格式为 <&gpio-controller pin-number flags>
  • flags:GPIO标志,如GPIO_ACTIVE_HIGH、GPIO_ACTIVE_LOW等

5. GPIO子系统API

5.1 内核空间API

5.1.1 基本操作API
  • gpio_request():请求一个GPIO引脚
  • gpio_free():释放一个GPIO引脚
  • gpio_direction_input():设置GPIO为输入方向
  • gpio_direction_output():设置GPIO为输出方向并设置初始值
  • gpio_get_value():读取GPIO的值
  • gpio_set_value():设置GPIO的值
  • gpio_to_irq():将GPIO编号转换为中断编号
5.1.2 设备树相关API
  • of_get_gpio():从设备树中获取GPIO编号
  • of_get_gpio_flags():从设备树中获取GPIO编号和标志
  • gpiod_get():获取GPIO描述符
  • gpiod_put():释放GPIO描述符
  • gpiod_direction_input():设置GPIO为输入方向
  • gpiod_direction_output():设置GPIO为输出方向并设置初始值
  • gpiod_get_value():读取GPIO的值
  • gpiod_set_value():设置GPIO的值
5.1.3 内核驱动使用示例
#include <linux/gpio/consumer.h>

struct gpio_desc *gpio_led;

static int my_driver_probe(struct platform_device *pdev)
{
    int ret;
    
    /* 从设备树获取GPIO */
    gpio_led = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
    if (IS_ERR(gpio_led)) {
        dev_err(&pdev->dev, "Failed to get LED GPIO: %ld\n", PTR_ERR(gpio_led));
        return PTR_ERR(gpio_led);
    }
    
    /* 设置GPIO为高电平 */
    gpiod_set_value(gpio_led, 1);
    
    return 0;
}

static int my_driver_remove(struct platform_device *pdev)
{
    /* 释放GPIO */
    gpiod_put(gpio_led);
    return 0;
}

5.2 用户空间API

5.2.1 sysfs接口

GPIO子系统通过sysfs提供了用户空间访问接口,位于/sys/class/gpio/目录下。

  • /sys/class/gpio/export:导出GPIO到用户空间
  • /sys/class/gpio/unexport:从用户空间取消导出GPIO
  • /sys/class/gpio/gpioX/:导出的GPIO目录,X为GPIO编号
    • direction:设置GPIO方向(in/out)
    • value:读取或设置GPIO值(0/1)
    • edge:设置中断触发方式(none/rising/falling/both)
    • active_low:设置是否为低电平有效
5.2.2 用户空间使用示例
# 导出GPIO 18
echo 18 > /sys/class/gpio/export

# 设置为输出方向
echo out > /sys/class/gpio/gpio18/direction

# 设置为高电平
echo 1 > /sys/class/gpio/gpio18/value

# 读取GPIO值
cat /sys/class/gpio/gpio18/value

# 设置为输入方向
echo in > /sys/class/gpio/gpio18/direction

# 设置中断触发方式为上升沿
echo rising > /sys/class/gpio/gpio18/edge

# 取消导出GPIO 18
echo 18 > /sys/class/gpio/unexport

6. 应用层测试接口和方法

6.1 sysfs接口测试

6.1.1 基本测试
# 查看系统中的GPIO控制器
ls /sys/class/gpio/

# 导出GPIO
# 注意:不同平台的GPIO编号可能不同,需要根据实际情况调整
echo 1 > /sys/class/gpio/export

# 测试输出功能
echo out > /sys/class/gpio/gpio1/direction
echo 1 > /sys/class/gpio/gpio1/value
echo 0 > /sys/class/gpio/gpio1/value

# 测试输入功能
echo in > /sys/class/gpio/gpio1/direction
cat /sys/class/gpio/gpio1/value

# 测试中断触发方式
echo rising > /sys/class/gpio/gpio1/edge

# 取消导出
echo 1 > /sys/class/gpio/unexport
6.1.2 批量测试脚本
#!/bin/bash

# GPIO测试脚本
GPIO_NUM=1

# 导出GPIO
echo $GPIO_NUM > /sys/class/gpio/export

# 测试输出
echo "Testing GPIO $GPIO_NUM as output..."
echo out > /sys/class/gpio/gpio$GPIO_NUM/direction

for i in {1..5}
do
    echo 1 > /sys/class/gpio/gpio$GPIO_NUM/value
    echo "GPIO $GPIO_NUM set to HIGH"
    sleep 1
    echo 0 > /sys/class/gpio/gpio$GPIO_NUM/value
    echo "GPIO $GPIO_NUM set to LOW"
    sleep 1
done

# 测试输入
echo "Testing GPIO $GPIO_NUM as input..."
echo in > /sys/class/gpio/gpio$GPIO_NUM/direction

for i in {1..5}
do
    VALUE=$(cat /sys/class/gpio/gpio$GPIO_NUM/value)
    echo "GPIO $GPIO_NUM input value: $VALUE"
    sleep 1
done

# 取消导出
echo $GPIO_NUM > /sys/class/gpio/unexport
echo "Test completed"

6.2 libgpiod库

libgpiod是一个用户空间库,提供了更高级的GPIO操作接口,替代了传统的sysfs接口。

6.2.1 安装libgpiod
# Debian/Ubuntu
sudo apt-get install libgpiod-dev libgpiod-tools

# CentOS/RHEL
sudo yum install libgpiod-devel libgpiod-utils
6.2.2 使用gpiod工具
# 列出所有GPIO芯片
gpiodetect

# 列出特定芯片的GPIO
gpioinfo gpiochip0

# 设置GPIO 1为输出并设置为高电平
gpioget gpiochip0 1

# 设置GPIO 1为输出并设置为高电平
gpioset gpiochip0 1=1

# 设置GPIO 1为输出并设置为低电平
gpioset gpiochip0 1=0

# 监控GPIO 1的状态变化
gpiomon -n 10 gpiochip0 1
6.2.3 编程示例
#include <gpiod.h>
#include <stdio.h>

int main(void)
{
    struct gpiod_chip *chip;
    struct gpiod_line *line;
    int value;
    int ret;
    
    // 打开GPIO芯片
    chip = gpiod_chip_open("/dev/gpiochip0");
    if (!chip) {
        perror("gpiod_chip_open");
        return 1;
    }
    
    // 获取GPIO线
    line = gpiod_chip_get_line(chip, 1);
    if (!line) {
        perror("gpiod_chip_get_line");
        gpiod_chip_close(chip);
        return 1;
    }
    
    // 设置为输出方向
    ret = gpiod_line_request_output(line, "test", 0);
    if (ret < 0) {
        perror("gpiod_line_request_output");
        gpiod_line_release(line);
        gpiod_chip_close(chip);
        return 1;
    }
    
    // 设置为高电平
    ret = gpiod_line_set_value(line, 1);
    if (ret < 0) {
        perror("gpiod_line_set_value");
        gpiod_line_release(line);
        gpiod_chip_close(chip);
        return 1;
    }
    printf("GPIO set to HIGH\n");
    
    // 释放资源
    gpiod_line_release(line);
    gpiod_chip_close(chip);
    
    return 0;
}

7. 平台特定的GPIO驱动

7.1 常见平台的GPIO驱动

  • Allwinner:sunxi-gpio
  • Qualcomm:msm-gpio
  • NXP:imx-gpio
  • TI:omap-gpio
  • Rockchip:rk-gpio
  • Intel:gpio-ich

7.2 驱动实现要点

  1. GPIO控制器初始化:识别硬件特性,初始化寄存器
  2. 操作函数实现:实现GPIO的读写、方向设置等操作
  3. 中断处理:实现GPIO中断的配置和处理
  4. 设备树解析:解析设备树中的GPIO配置

8. GPIO子系统的优势

  1. 统一管理:集中管理所有GPIO的配置和操作
  2. 标准化接口:提供统一的API,隐藏硬件差异
  3. 设备树集成:通过设备树描述硬件配置,实现配置与代码分离
  4. 用户空间访问:提供sysfs接口,方便用户空间程序访问
  5. 中断支持:内置中断处理机制,支持各种触发方式

9. 常见问题与解决方案

问题原因解决方案
GPIO请求失败GPIO被其他设备占用检查GPIO使用情况,确保没有冲突
中断不触发中断配置错误检查中断触发方式和GPIO方向设置
电平读取错误电气连接问题或方向设置错误检查硬件连接,确保GPIO方向正确
用户空间访问权限权限不足使用root权限或正确设置文件权限
设备树解析失败设备树语法错误或节点引用错误检查设备树语法,确保节点路径正确

10. 总结

GPIO子系统为Linux内核提供了统一的GPIO管理框架,通过分层架构和设备树集成,实现了GPIO配置的标准化和集中管理。它不仅简化了驱动开发,还提供了用户空间访问接口,方便应用程序使用GPIO功能。

在实际应用中,正确使用GPIO子系统可以实现各种输入输出功能,如LED控制、按键检测、传感器接口等。通过sysfs接口和libgpiod库,开发人员可以方便地测试和使用GPIO功能。

随着嵌入式系统的不断发展,GPIO子系统的重要性也将日益凸显,它为各种嵌入式设备提供了基础的输入输出能力,是嵌入式系统开发中不可或缺的一部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值