目录
[gpio 子系统简介](#gpio 子系统简介)
[I.MX6ULL 的 gpio 子系统驱动](#I.MX6ULL 的 gpio 子系统驱动)
[设备树中的 gpio 信息](#设备树中的 gpio 信息)
[GPIO 驱动程序简介](#GPIO 驱动程序简介)
[of_device_id 匹配表](#of_device_id 匹配表)
[mxc_gpio_port 结构体](#mxc_gpio_port 结构体)
[mxc_gpio_get_hw 函数](#mxc_gpio_get_hw 函数)
[gpio_chip 结构体](#gpio_chip 结构体)
[gpio 子系统 API 函数](#gpio 子系统 API 函数)
[设备树中添加 gpio 节点模板](#设备树中添加 gpio 节点模板)
[创建 test 设备节点](#创建 test 设备节点)
[添加 pinctrl 信息](#添加 pinctrl 信息)
[添加 GPIO 属性信息](#添加 GPIO 属性信息)
前言
在上一讲内容里,驱动开发篇10------pinctrl 子系统,我们已经详细地讲解了pinctrl子系统。
这一讲,我们继续学习gpio子系统,熟悉原理、掌握常用的API函数。
gpio 子系统简介
GPIO子系统架构GPIO子系统采用分层设计,主要包含以下组件:
- GPIO芯片驱动层:直接操作硬件寄存器
- GPIO核心层:提供通用接口和框架
- GPIO用户接口层:向用户空间和内核其他模块提供API
gpio 子系统,用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。
cpp
// 申请GPIO
int gpio_request(unsigned gpio, const char *label);
// 设置方向
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
// 读写操作
int gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned gpio, int value);
// 释放GPIO
void gpio_free(unsigned gpio);
gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO,。
Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大地方便了驱动开发者使用 GPIO。
I.MX6ULL 的 gpio 子系统驱动
设备树中的 gpio 信息
我们使用正点原子的I.MX6ULL-ALPHA 开发板,以SD 卡的检测引脚为例:
打开 imx6ull-alientek-emmc.dts,有如下代码:
cpp
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
......
>;
};
将 UART1_RTS_B 这个 PIN 复用为 GPIO1_IO19,并且设置电气属性。
SD 卡连接在I.MX6ULL 的 usdhc1 接口上,在 imx6ull-alientek-emmc.dts 中找到名为"usdhc1"的节点,这个节点就是 SD 卡设备节点,如下所示:
cpp
&usdhc1 {
/* 引脚控制状态名称,对应不同时钟频率下的配置 */
pinctrl-names = "default", "state_100mhz", "state_200mhz";
/* 各状态对应的具体引脚配置组 */
pinctrl-0 = <&pinctrl_usdhc1>; // 默认状态引脚配置
pinctrl-1 = <&pinctrl_usdhc1_100mhz>; // 100MHz时钟下的引脚配置
pinctrl-2 = <&pinctrl_usdhc1_200mhz>; // 200MHz时钟下的引脚配置
// pinctrl-3 = <&pinctrl_hog_1>; // 注释掉的备用配置
/* SD卡检测信号配置 */
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>; // 使用GPIO1_IO19作为卡检测引脚,低电平有效
/* 电源管理相关配置 */
keep-power-in-suspend; // 在挂起状态保持电源
enable-sdio-wakeup; // 允许SDIO唤醒系统
/* 电源供应配置 */
vmmc-supply = <®_sd1_vmmc>; // 指定3.3V电源调节器
/* 设备状态 */
status = "okay"; // 启用该设备
};
其中,属性"cd-gpios"描述了 SD 卡的 CD 引脚使用的哪个 IO。
cpp
/* SD卡检测信号配置 */
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>; // 使用GPIO1_IO19作为卡检测引脚,低电平有效
- "&gpio1"表示 CD 引脚所使用的 IO 属于 GPIO1 组,
- "19" 表示 GPIO1 组的第 19 号 IO,通过这两个值 SD 卡驱动程序就知道 CD 引脚使用了 GPIO1_IO19这 GPIO。
- "GPIO_ACTIVE_LOW"表示低电平有效,如果改为"GPIO_ACTIVE_HIGH"就表示高电平有效。
根据上面这些信息, SD 卡驱动程序就可以使用GPIO1_IO19 来检测 SD 卡的 CD 信号。
打开 imx6ull.dtsi,关于GPIO1节点有如下所示内容:
cpp
gpio1: gpio@0209c000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio"; // 设备兼容性标识,支持i.MX6UL和i.MX35的GPIO驱动
reg = <0x0209c000 0x4000>; // 寄存器物理地址范围:基地址0x0209c000,长度16KB
// 中断配置:两个中断号,均采用高电平触发
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>, // 第一个中断,SPI中断号66
<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>; // 第二个中断,SPI中断号67
gpio-controller; // 声明为GPIO控制器
#gpio-cells = <2>; // 使用GPIO时需要2个参数:引脚号和标志位
interrupt-controller; // 同时作为中断控制器
#interrupt-cells = <2>; // 中断说明符需要2个参数:GPIO号和中断类型
};
gpio1 节点信息描述了 GPIO1 控制器的所有信息,重点就是 GPIO1 外设寄存器基地址以及兼容 属性 。
关 于 I.MX 系 列 SOC 的 GPIO 控 制 器 绑 定 信 息 请 查 看 文档: Documentation/devicetree/bindings/gpio/ fsl-imx-gpio.txt。
reg 属性设置了 GPIO1 控制器的寄存器基地址为 0X0209C000:
cpp
reg = <0x0209c000 0x4000>;

"#gpio-cells"属性:有两个 cell:
cpp
#gpio-cells = <2>;
- 第一个 cell 为 GPIO 编号,比如"&gpio1 3"就表示 GPIO1_IO03。
- 第二个 cell 表示GPIO 极 性 , 如 果 为 0(GPIO_ACTIVE_HIGH) 的 话 表 示 高 电 平 有 效 , 如 果 为1(GPIO_ACTIVE_LOW)的话表示低电平有效。
GPIO 驱动程序简介
of_device_id 匹配表
I.MX6ULL的 GPIO 驱动文件drivers/gpio/gpio-mxc.c,有如下所示 of_device_id 匹配表:
cpp
/*
* i.MX系列GPIO控制器兼容性匹配表
* 用于设备树匹配和驱动初始化
*/
static const struct of_device_id mxc_gpio_dt_ids[] = {
/* i.MX1系列GPIO兼容性定义 */
{
.compatible = "fsl,imx1-gpio", // 设备树兼容性字符串
.data = &mxc_gpio_devtype[IMX1_GPIO], // 指向IMX1的GPIO设备类型数据
},
/* i.MX21系列GPIO兼容性定义 */
{
.compatible = "fsl,imx21-gpio", // i.MX21兼容字符串
.data = &mxc_gpio_devtype[IMX21_GPIO], // IMX21设备类型数据指针
},
/* i.MX31系列GPIO兼容性定义 */
{
.compatible = "fsl,imx31-gpio", // i.MX31兼容字符串
.data = &mxc_gpio_devtype[IMX31_GPIO], // IMX31设备类型数据指针
},
/* i.MX35系列GPIO兼容性定义 */
{
.compatible = "fsl,imx35-gpio", // i.MX35兼容字符串
.data = &mxc_gpio_devtype[IMX35_GPIO], // IMX35设备类型数据指针
},
/* 结束标记 */
{ /* sentinel */ }
};
其中,compatible 值为"fsl,imx35-gpio"的代码,和 gpio1 的 compatible 属性匹配,因此 gpio-mxc.c 就是 I.MX6ULL 的 GPIO 控制器驱动文件。
gpio-mxc.c文件
在 gpio-mxc.c 文件中有如下所示内容:
cpp
/*
* i.MX GPIO平台驱动注册结构体
* 用于向内核注册GPIO控制器驱动
*/
static struct platform_driver mxc_gpio_driver = {
.driver = {
.name = "gpio-mxc", /* 驱动名称,用于/sys/bus/platform/drivers/目录 */
.of_match_table = mxc_gpio_dt_ids, /* 设备树匹配表,指向之前定义的兼容性表 */
},
.probe = mxc_gpio_probe, /* 设备探测函数,匹配成功后调用 */
.id_table = mxc_gpio_devtype, /* 设备ID表,用于非设备树的平台设备匹配 */
};
GPIO 驱动也是个平台设备驱动,因此当设备树中的设备节点与驱动的of_device_id 匹配以后 probe 函数就会执行,在这里就是mxc_gpio_probe函数。
mxc_gpio_probe函数
mxc_gpio_probe函数就是I.MX6ULL 的 GPIO 驱动入口函数。
cpp
/*
* i.MX GPIO控制器探测函数
* 完成GPIO控制器的硬件初始化和驱动注册
*/
static int mxc_gpio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node; // 获取设备树节点
struct mxc_gpio_port *port; // GPIO端口数据结构
struct resource *iores; // IO资源指针
int irq_base; // 中断号基址
int err; // 错误码
// 获取硬件类型信息
mxc_gpio_get_hw(pdev);
// 分配端口内存空间
port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
/*----- 硬件资源初始化 -----*/
// 获取并映射寄存器资源
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
port->base = devm_ioremap_resource(&pdev->dev, iores);
if (IS_ERR(port->base))
return PTR_ERR(port->base);
// 获取中断资源
port->irq_high = platform_get_irq(pdev, 1); // 高16位GPIO中断
port->irq = platform_get_irq(pdev, 0); // 基础中断
if (port->irq < 0)
return port->irq;
/*----- 硬件初始化 -----*/
// 禁用中断并清除状态
writel(0, port->base + GPIO_IMR); // 中断屏蔽寄存器
writel(~0, port->base + GPIO_ISR); // 中断状态寄存器
/*----- 中断处理配置 -----*/
if (mxc_gpio_hwtype == IMX21_GPIO) {
// i.MX21系列的特殊处理
irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
} else {
// i.MX3及后续系列的通用处理
irq_set_chained_handler(port->irq, mx3_gpio_irq_handler);
irq_set_handler_data(port->irq, port);
// 高16位GPIO中断配置
if (port->irq_high > 0) {
irq_set_chained_handler(port->irq_high, mx3_gpio_irq_handler);
irq_set_handler_data(port->irq_high, port);
}
}
/*----- GPIO控制器初始化 -----*/
// 使用gpiolib的通用后端初始化
err = bgpio_init(&port->bgc, &pdev->dev, 4,
port->base + GPIO_PSR, // 引脚状态寄存器
port->base + GPIO_DR, // 数据寄存器
NULL,
port->base + GPIO_GDIR, // 方向寄存器
NULL, 0);
if (err)
goto out_bgio;
// 设置GPIO芯片特性
port->bgc.gc.to_irq = mxc_gpio_to_irq; // 转换GPIO到IRQ的函数
port->bgc.gc.base = (pdev->id < 0) ? // GPIO编号基址
of_alias_get_id(np, "gpio") * 32 :
pdev->id * 32;
// 注册GPIO控制器
err = gpiochip_add(&port->bgc.gc);
if (err)
goto out_bgpio_remove;
/*----- 中断域配置 -----*/
// 分配中断描述符
irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
if (irq_base < 0) {
err = irq_base;
goto out_gpiochip_remove;
}
// 创建IRQ域
port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
&irq_domain_simple_ops, NULL);
if (!port->domain) {
err = -ENODEV;
goto out_irqdesc_free;
}
// 初始化通用中断芯片
mxc_gpio_init_gc(port, irq_base);
// 将端口添加到全局列表
list_add_tail(&port->node, &mxc_gpio_ports);
return 0;
/*----- 错误处理路径 -----*/
...
}
让我们来详细分析一下这段代码:
mxc_gpio_port 结构体
定义一个结构体指针 port,结构体类型为 mxc_gpio_port。
cpp
struct mxc_gpio_port *port; // GPIO端口数据结构
gpio-mxc.c 的重点工作就是维护 mxc_gpio_port, mxc_gpio_port 就是对 I.MX6ULL GPIO 的抽象。
mxc_gpio_port 结构体定义如下:
cpp
/*
* i.MX GPIO端口数据结构
* 描述一个GPIO控制器的所有硬件和软件状态
*/
struct mxc_gpio_port {
struct list_head node; // 链表节点,用于将多个GPIO端口串联到全局列表
void __iomem *base; // GPIO寄存器基地址指针(I/O映射后的虚拟地址)
int irq; // 主中断号(处理GPIO0-15中断)
int irq_high; // 高16位中断号(处理GPIO16-31中断,可选)
struct irq_domain *domain; // IRQ域,用于GPIO中断号映射管理
struct bgpio_chip bgc; // 通用GPIO控制器数据结构,包含:
// - gpio_chip 基础结构
// - 寄存器访问方法
// - 状态缓存等
u32 both_edges; // 双边沿触发状态标志位,用于:
// - 记录配置为双边沿触发的GPIO位
// - 实现模拟双边沿触发功能
};
/*
* 结构体功能详解:
* 1. 硬件资源管理:
* - base: 提供对GPIO控制寄存器的访问
* - irq/irq_high: 管理两组GPIO中断线
*
* 2. 中断系统集成:
* - domain: 将GPIO引脚映射到Linux中断号
* - both_edges: 特殊处理双边沿触发模式
*
* 3. GPIO核心框架:
* - bgpio_chip: 集成到Linux GPIO子系统
* * 包含gpio_chip基本操作
* * 实现方向控制/数值读写等标准接口
*
* 4. 系统管理:
* - node: 允许系统遍历所有已注册的GPIO控制器
*
* 典型使用场景:
* - 每个i.MX处理器的GPIO bank对应一个port实例
* - 通过base+偏移访问具体寄存器
* - 通过bgc提供标准GPIO操作接口
*/
mxc_gpio_get_hw 函数
调用 mxc_gpio_get_hw 函数获取 gpio 的硬件相关数据,其实就是 gpio 的寄存器组。
cpp
// 获取硬件类型信息
mxc_gpio_get_hw(pdev);
函数 mxc_gpio_get_hw 里面有如下代码:
cpp
/*
* mxc_gpio_get_hw - 获取i.MX GPIO控制器硬件类型和配置数据
* @pdev: 平台设备指针
*
* 根据设备树匹配结果确定具体的GPIO硬件类型,并设置对应的硬件配置数据。
* 该函数在probe阶段被调用,用于初始化硬件相关参数。
*/
static void mxc_gpio_get_hw(struct platform_device *pdev)
{
// 通过设备树兼容性字符串匹配硬件类型
const struct of_device_id *of_id =
of_match_device(mxc_gpio_dt_ids, &pdev->dev);
enum mxc_gpio_hwtype hwtype;
/*
* 硬件类型判断逻辑:
* 根据of_match_table中设置的.data指针确定具体型号
*/
if (of_id && of_id->data) {
hwtype = (enum mxc_gpio_hwtype)of_id->data;
} else {
/* 默认回退到最基础的类型 */
hwtype = IMX1_GPIO;
}
// 根据硬件类型选择对应的配置数据结构
if (hwtype == IMX35_GPIO)
mxc_gpio_hwdata = &imx35_gpio_hwdata; // i.MX35系列专用配置
else if (hwtype == IMX31_GPIO)
mxc_gpio_hwdata = &imx31_gpio_hwdata; // i.MX31系列专用配置
else
mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata; // i.MX1/i.MX21通用配置
// 设置全局硬件类型标识
mxc_gpio_hwtype = hwtype;
}
mxc_gpio_hwdata 是个全局变量,如果硬件类型是 IMX35_GPIO 的话,设置mxc_gpio_hwdat 为 imx35_gpio_hwdata。
对于 I.MX6ULL 而言,硬件类型就是 IMX35_GPIO。
imx35_gpio_hwdata结构体
imx35_gpio_hwdata是个结构体变量,描述了 GPIO 寄存器组,内容如下:
cpp
/*
* i.MX35系列GPIO硬件配置数据结构
* 定义i.MX35处理器的GPIO控制器寄存器布局和中断触发类型配置
*/
static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
/* GPIO寄存器偏移地址定义 */
.dr_reg = 0x00, // 数据寄存器:存储GPIO输入/输出值
.gdir_reg = 0x04, // 方向寄存器:配置GPIO输入/输出模式
.psr_reg = 0x08, // 引脚状态寄存器:读取GPIO当前电平状态
.icr1_reg = 0x0c, // 中断配置寄存器1:配置GPIO0-15中断触发方式
.icr2_reg = 0x10, // 中断配置寄存器2:配置GPIO16-31中断触发方式
.imr_reg = 0x14, // 中断屏蔽寄存器:使能/禁用GPIO中断
.isr_reg = 0x18, // 中断状态寄存器:查看触发的中断源
.edge_sel_reg = 0x1c, // 边沿选择寄存器:用于双边沿触发配置
/* 中断触发类型配置值 */
.low_level = 0x00, // 低电平触发配置值
.high_level = 0x01, // 高电平触发配置值
.rise_edge = 0x02, // 上升沿触发配置值
.fall_edge = 0x03, // 下降沿触发配置值
};
imx35_gpio_hwdata 结构体就是 GPIO 寄存器组结构。

这样我们后面就可以通过mxc_gpio_hwdata 这个全局变量来访问 GPIO 的相应寄存器了。
bgpio_init函数
bgpio_init 函数主要任务就是初始化 bgc->gc 。 bgpio_init 里 面 有 三 个 setup 函 数 : bgpio_setup_io 、bgpio_setup_accessors 和 bgpio_setup_direction。
这三个函数就是初始化 bgc->gc 中的各种有关GPIO 的操作,比如输出,输入等等。
cpp
/*----- GPIO控制器初始化 -----*/
// 使用gpiolib的通用后端初始化
err = bgpio_init(&port->bgc, &pdev->dev, 4,
port->base + GPIO_PSR, // 引脚状态寄存器
port->base + GPIO_DR, // 数据寄存器
NULL,
port->base + GPIO_GDIR, // 方向寄存器
NULL, 0);
bgpio_init函数第一个参数为 bgc,是 bgpio_chip 结构体指针。
bgc 既有对 GPIO 的操作函数,又有 I.MX6ULL 有关 GPIO的寄存器,那么只要得到 bgc 就可以对 I.MX6ULL 的 GPIO 进行操作。
gpio_chip 结构体
bgpio_chip结构体有个 gc 成员变量, gc 是个 gpio_chip 结构体类型的变量。 gpio_chip 结构体是抽象出来的GPIO 控制器。
gpio_chip 结构体如下所示(有缩减):
cpp
/*
* GPIO控制器抽象接口
* 定义GPIO控制器的标准操作方法和属性
*/
struct gpio_chip {
const char *label; /* 控制器标签/名称,用于标识 */
struct device *dev; /* 关联的设备结构体 */
struct module *owner; /* 所属模块(用于引用计数) */
struct list_head list; /* 全局GPIO控制器链表节点 */
/*----- 核心操作函数集 -----*/
int (*request)(struct gpio_chip *chip, unsigned offset); /* GPIO引脚申请 */
void (*free)(struct gpio_chip *chip, unsigned offset); /* GPIO引脚释放 */
/* 方向控制 */
int (*get_direction)(struct gpio_chip *chip, unsigned offset); /* 获取方向 */
int (*direction_input)(struct gpio_chip *chip, unsigned offset); /* 设为输入 */
int (*direction_output)(struct gpio_chip *chip,
unsigned offset, int value); /* 设为输出 */
/* 数值操作 */
int (*get)(struct gpio_chip *chip, unsigned offset); /* 获取引脚值 */
void (*set)(struct gpio_chip *chip, unsigned offset, int value); /* 设置引脚值 */
......
};
可以看出, gpio_chip 大量的成员都是函数,这些函数就是 GPIO 操作函数。
gpiochip_add函数
调用函数gpiochip_add向Linux内核注册gpio_chip,也就是 port->bgc.gc。
cpp
// 注册GPIO控制器
err = gpiochip_add(&port->bgc.gc);
注册完成以后我们就可以在驱动中使用 gpiolib 提供的各个 API 函数。
gpio 子系统 API 函数
对于驱动开发人员,设置好设备树以后就可以使用 gpio 子系统提供的 API 函数来操作指定的 GPIO, gpio 子系统向驱动开发人员屏蔽了具体的读写寄存器过程。
gpio 子系统提供的常用的 API 函数有下面几个:
函数名 | 功能描述 |
---|---|
gpio_request | 申请GPIO引脚使用权(使用前必须调用) |
gpio_free | 释放已申请的GPIO资源 |
gpio_direction_input | 将GPIO配置为输入模式 |
gpio_direction_output | 将GPIO配置为输出模式(可设置初始输出值) |
gpio_get_value | 读取GPIO当前输入电平值 |
gpio_set_value | 设置GPIO输出电平值 |
cpp
// 1. GPIO申请(必须首先调用)
int gpio_request(unsigned int gpio, const char *label);
/* 功能:申请GPIO引脚使用权
* 参数:
* gpio - 要申请的GPIO编号(通过of_get_named_gpio()从设备树获取)
* label - 自定义标识字符串(出现在/sys/kernel/debug/gpio中)
* 返回:
* 0成功,非零失败(-EBUSY表示已被占用)
*/
// 2. GPIO释放
void gpio_free(unsigned int gpio);
/* 功能:释放GPIO资源
* 参数:
* gpio - 要释放的GPIO编号
*/
// 3. 设置为输入模式
int gpio_direction_input(unsigned int gpio);
/* 功能:配置GPIO为输入方向
* 参数:
* gpio - 要配置的GPIO编号
* 返回:
* 0成功,负值表示错误
*/
// 4. 设置为输出模式(带初始值)
int gpio_direction_output(unsigned int gpio, int value);
/* 功能:配置GPIO为输出方向并设置初始电平
* 参数:
* gpio - 要配置的GPIO编号
* value - 初始输出值(0/1)
* 返回:
* 0成功,负值表示错误
*/
// 5. 读取GPIO值(输入模式使用)
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned int gpio);
/* 功能:读取GPIO当前电平值
* 参数:
* gpio - 要读取的GPIO编号
* 返回:
* 0/1 - 实际读取的电平
* 负值 - 读取失败
*/
// 6. 设置GPIO值(输出模式使用)
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned int gpio, int value);
/* 功能:设置GPIO输出电平
* 参数:
* gpio - 要设置的GPIO编号
* value - 要设置的值(0/1)
*/
使用举例:
cpp
#include <linux/gpio.h>
// 假设从设备树获取的GPIO编号
#define LED_GPIO 123 // 输出用GPIO
#define KEY_GPIO 456 // 输入用GPIO
// GPIO初始化
void gpio_init(void)
{
// 1. 申请GPIO
gpio_request(LED_GPIO, "my_led");
gpio_request(KEY_GPIO, "my_key");
// 2. 设置方向
gpio_direction_output(LED_GPIO, 0); // LED初始低电平
gpio_direction_input(KEY_GPIO); // 按键输入模式
}
// GPIO使用示例
void gpio_usage(void)
{
// 3. 输出控制(LED闪烁)
gpio_set_value(LED_GPIO, 1); // LED亮
gpio_set_value(LED_GPIO, 0); // LED灭
// 4. 输入读取(按键检测)
if (gpio_get_value(KEY_GPIO)) {
printk("Key pressed!\n");
}
}
// GPIO释放
void gpio_release(void)
{
gpio_free(LED_GPIO);
gpio_free(KEY_GPIO);
}
设备树中添加 gpio 节点模板
我们来学习一下如何创建 test 设备的 GPIO 节点。
创建 test 设备节点
在根节点"/"下创建 test 设备子节点,如下所示:
cpp
test {
/* 节点内容 */
};
添加 pinctrl 信息
在之前的文章里,pinctrl 子系统,我们创建了 pinctrl_test 节点,此节点描述了 test 设备所使用的 GPIO1_IO00 这个 PIN 的信息。
我们要将这节点添加到 test 设备节点中,如下所示:
cpp
test {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_test>;
/* 其他节点内容 */
};
添加 GPIO 属性信息
在 test 节点中添加 GPIO 属性信息,表明 test 所使用的 GPIO 是哪个引脚。
添加完成以后如下所示:
cpp
test {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_test>;
gpio = <&gpio1 0 GPIO_ACTIVE_LOW>;
};
关于 pinctrl 子系统和 gpio 子系统就讲解到这里。
下一讲内容我们使用 pinctrl 和 gpio 子系统,来驱动 I.MX6ULL-ALPHA 开发板上的 LED 灯。