4.3.4. GPIO 调试指南
4.3.4.1. 概述
Linux GPIO( General Purpose Input/Output,通用
4.3.4.2. 特点
1. 灵活性
可
配置 性: GPIO 引脚 可以 在 不同 的 模式 下 进行 配置,支持 输入、输出、上拉、下 拉、开漏 等 不同 电气 特性。这 意味着 同一个 引脚 可以 在 不同 场景 下 用作 不同 功能。 多种
工作 模式: GPIO 引脚 可以 作为 输入(检测 外部 信号),也 可以 作为 输出(控制 外部设备),此外 还 支持 中断 触发,允许 系统 响应 硬件 事件。
2. 易于
文件系统
接口:通过 /sys/class/gpio 文件系统 接口,用户 可以 方便 地 在 用户 空间 操作 GPIO 引脚。例如,可以 使用 简单 的 shell 命令 来 控制 引脚 的 方向 和 电平,甚至 在 没有 编写 驱动 的 情况 下 快速 验证 硬件 接口 的 功能 高层
接口 支持: Linux 也 提供 了 更 高级 的 接口,如 gpiod( GPIO 描述符 接口),使得 编写 GPIO 驱动程序 或 用户 空间 应用 更加 简单 且 高效。支持 通过 C/C++ 等 语言 进行 编程,减少 了 硬件 访问 的 复杂度。
3. 支持
事件驱动:许多 GPIO 引脚
可以 配置 为 触发 中断,允许 系统 响应 外部 事件。这 对于 实现 高效 的 硬件 事件 响应 非常 重要。例如,可以 通过 GPIO 中断 来 响应 按钮 按下 或 外部 传感器 数据 的 变化 中断
类型: Linux 支持 不同 类型 的 中断 触发 机制,如 边沿 触发(上升 沿 或 下降 沿)和 电平 触发(高电平 或 低电平)。这 使得 中断 处理 非常灵活,可以 满足 各种 硬件 需求。
4. 用户
内核
和 用户 空间 接口: GPIO 驱动程序 在 内核 空间 运行,但 开发者 可以 通过 用户 空间 接口 访问 和 控制 GPIO, Linux 提供 了 一个 中间层(如 sysfs 或 gpiod),让 开发者 能够 在 不 修改 内核 代码 的 情况 下 控制 硬件。 内核
驱动 支持:对于 更 复杂 的 GPIO 操作,开发者 可以 编写 内核 驱动程序,直接 操作 GPIO 寄存器 和 控制 硬件 设备。
5. 支持
PWM 输出:尽管 GPIO 通常
用于 数字 输入 和 输出,但 Linux 也 支持 通过 软件 模拟 方式(如 占空比 调节)在 某些 GPIO 引脚 上 实现 脉宽调制( PWM)。 模拟
输入:尽管 GPIO 主要 用于 数字信号,但 与 外部 硬件 结合 时,某些 系统 可以 通过 GPIO 扩展 模块(如 ADC/DAC)
4.3.4.3. 功能描述
典型应用
以 GPIO 按键

功能原理

GPIO、设备树
备注:
gpio 与 pinctrl 详细
工作方式
设备树
引脚
的 复用 功能:比如 是 配置 为 GPIO,还是 其他 外设 功能(如 UART、 SPI 等)。 引脚
方向: GPIO 引脚 可以 配置 为 输入 或 输出。 上拉 / 下
拉 电阻:可以 为 GPIO 引脚 配置 上 拉 或 下 拉 电阻。 驱动
强度:设置 GPIO 引脚 的 电流 驱动 能力,决定 引脚 在 驱动 信号 时 的 电流 大小。
示例:
在
gpio_keys_hobot {
compatible = "hobot,gpio_key";
debounce-interval = <200>;
gpios = <&aon_gpio_porta 2 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&aon_gpio_2>;
};
compatible:用于声明 设备 与 特定 驱动 的 兼容性。它 是 一个 字符串,内核 通过 这个 属性 来 匹配 设备树 中 的 硬件 节点 和 相应 的 驱动程序 。 debounce-interval:此配置 项会 在 驱动 中 解析,用于 配置 按键 的 去 抖动 时间 防止 按键 状态 抖动 导致 误 触发。 gpios:指定了 按键 设备 所 连接 的 GPIO 引脚 , <&aon_gpio_porta 2 GPIO_ACTIVE_LOW> 表示 该 按键 连接 到 aon_gpio_porta 这个 GPIO 控制器 的 第 2 号 引脚,且 该 GPIO 引脚 在 低电平 时 触发 按键 事件 。 pinctrl-0:指定使用 的 gpio 的 引脚 复用,&aon_gpio_2 是 一个 引脚 控制器 节点,通常 会 在 设备树 的 pinmux-gpio.dtsi 中 定义。
aon_gpio_2: aon_gpio_2 {
horizon,pins = <
AON_GPIO0_PIN2 INVALID_PINMUX BIT_OFFSET0 MUX_ALT0 &pconf_input_en_1v8_ds2
>;
};
aon_gpio_2:是该 设备树 节点 的 名称,用于 标识 该 配置。在 设备树 中,节点 名 可以 帮助 区分 不同 的 硬件 功能 和 配置。 horizon,pins: horizon,pins 是一个 属性,用于 描述 GPIO 引脚 的 具体 配置。这些 配置 通常 包括 引脚 的 功能 复用( pinmux)、输入输出 方向( GPIO 配置)、电压 电流 特性 等。 该 属性 的 值 是 一个 数组,包含 了 具体 的 引脚 配置 参数。这里 的 配置 包括 5 个 元素: AON_GPIO0_PIN2: GPIO 引脚的 符号 名称。 INVALID_PINMUX: INVALID_PINMUX 是一个 标志,通常 用于 指示 该 引脚 没有 进行 有效 的 功能 复用 配置。 BIT_OFFSET0: 表示该 引脚 在 某个 寄存器 或 控制 寄存器 中 的 位 偏移。 MUX_ALT0: MUX_ALT0 指定该 引脚 的 复用 模式,设备树 中 的 引脚 复用( pinmux)配置 通常 会 有 多个 选项, MUX_ALT0 代表 引脚 配置 为 GPIO 。 &pconf_input_en_1v8_ds2:它指向 一个 在 设备树 其他 部分 定义 的 配置 节点。通常 会 在 设备树 的 pinmux-func.dtsi 中 定义 , 配置 节点 会 包含 与 输入输出 方向、驱动 能力、上 拉 或 下 拉、电压 域 等 有关 的 设置。
pconf_input_en_1v8_ds2: pconf-input-en-1v8_ds2 {
input-enable;
power-source = <HORIZON_IO_PAD_VOLTAGE_1V8>;
drive-strength = <2>;
};
pconf_input_en_1v8_ds2:该节点 的 名称 是 pconf_input_en_1v8_ds2 ,通常 这样 的 命名 遵循 设备树 命名 规则,用于 标识 特定 的 硬件 配置。在 这个 名称 中: pconf 表示
这是 一个 引脚 配置( Pin Configuration)。 input-en 表示
与 输入 相关 的 配置( input enable)。 1v8 表示 IO 电压
域 为 1.8V。 ds2 表示
与 驱动 能力 等级( drive strength level 2 )。
input-enable:通常表示 启用 引脚 作为 输入 引脚。这 意味着 配置 中 所 涉及 的 引脚 会 被 设置 为 输入 模式,从而 使得 该 引脚 可以 接收 外部 信号,而 不是 驱动 输出 信号。 <HORIZON_IO_PAD_VOLTAGE_1V8>:表示该 引脚 配置 为 使用 1.8V 的 电源 电压。 drive-strength:属性表示 该 引脚 的 驱动 能力。驱动 能力 是 指引 脚 能够 提供 的 电流强度,通常 以 mA 为 单位。
IO 管脚LSIO_UART3_RXD 管脚
功能
打开
表格,选择 PIN Mux List数据表。第 B 列是 PinName,找到
LSIO_UART3_RXD所在的 行,第 F 列 表示 默认 function,为 LSIO_GPIO0_PIN10,即功能 为 GPIO, GPIO 名为 LSIO_GPIO0_PIN10。第 I, K, M, O 列表示 每种 function 对应 的 功能,如下 图 所示: 
配置 PIN 功能:选择
LSIO PIN Control Register数据表,如下图 所示: 
数据表
的 第一行 记录 了 寄存器 的 基 地址,也 就是 0x34180000;数据表
的 第 A 列 记录 了 各个 寄存器 的 偏移; 数据表
的 第 G 列 描述 了 寄存器 的 功能; 在
数据表 G 列 找到“ lsio_uart3_rxd PIN mux selector”所在的 行,可以 看到 LSIO_UART3_RXD的 PIN mux 寄存器偏移 为 0x84,完整地址 可 由 基地址 + 偏移得到:0x34180000 + 0x84 = 0x34180084;找到
以上 配置 项后,就 可以 设置 对应 PIN 的 function , Function [x] 代表 了 如果 想要 配置 为 该 功能,需要 在 寄存器 内 的 对应 偏移 写入 对应 的 [x]。例如 在 寄存器 0x34180084的 bit20-21 置为 0x0,表示LSIO_UART3_RXD引脚被 配置 为 uart3 rx功能,也就是 Function 0;配置为 0x1,表示LSIO_UART3_RXD引脚被 配置 为 i2c5 scl功能,也就是 Function 1;配置为 0x2,表示LSIO_UART3_RXD引脚被 配置 为 GPIO 功能,也 就是 Function 2;
配置 PIN 属性:选择
LSIO PIN Control Register数据表,如下图 所示: 
数据表
的 第一行 记录 了 寄存器 的 基 地址,也 就是 0x34180000;数据表
的 第 A 列 记录 了 各个 寄存器 的 偏移; 数据表
的 第 G 列 描述 了 寄存器 的 功能; 在
数据表 G 列 找到“ lsio_uart3_rxd pull up enable”所在的 行,可以 看到 LSIO_UART3_RXD的 PU/PD/ 施密特开关 /PIN 驱动力 的 寄存器 偏移 为 0x3C,完整地址 可 由 基地址 + 偏移得到:0x34180000 + 0x3C = 0x3418003C;PIN 的
驱动力 具体 数值 请 参考 Description for GPlO App.数据表的 驱动力 表格 ; PIN 的
电源 域 请 搜索 mode select并在 G 列 确认 当前 寄存器 控制 的 PIN 包含 了 LSIO_UART3_RXD,可以看到 控制 PIN 电源 域 的 寄存器 偏移 为 0x38,完整地址 可 由 基地址 + 偏移得到:0x34180000 + 0x38 = 0x34180038如下图 所示: 
配置
寄存器 时,建议 先 把 该值 先读 出来,然后 只 修改 后所 需 寄存器 比特 再 写 回。
GPIO 控制
在
表格 的 DW_apb_gpio8_mem_map和DW_apb_gpio32_mem_map数据表,描述了 引脚 对应 的 GPIO 方向 寄存器 和 数值 寄存器,如下 图( DW_apb_gpio32_mem_map)所示:

例如LSIO_UART3_RXD 对应LSIO_GPIO0_PIN10,如上图0x34120000,那么0x34120000,方向0x34120004。LSIO_UART3_RXD 引脚LSIO_UART3_RXD 所LSIO_GPIO0_PIN10,那么 bit 偏移
4.3.4.4. 驱动代码
Kernel Space
kernel/drivers/gpio/gpio-dwapb.c # gpio 驱动源文件
内核配置
GPIO_DWAPB

DTS 配置
所有arch/arm64/boot/dts/hobot/pinmux-gpio.dtsi 的
用户
/* arch/arm64/boot/dts/hobot/hobot/x5-som.dtsi */
&extcon_usb2otg {
pinctrl-names = "default";
pinctrl-0 = <&aon_gpio_6>;
id-gpios = <&aon_gpio_porta 6 GPIO_ACTIVE_HIGH>;
status = "okay";
};
extcon_usb2otg:&extcon_usb2otg 是对 设备树 中 某个 外部 连接器 节点( extcon)的 引用。外部 连接器( extcon)通常 用于 管理 不同 外部设备(如 USB 设备、音频设备 等)插拔 时 的 状态 变化。在 这种 情况 下, usb2otg 表示 这个 外部 连接器 是 一个 USB OTG 接口。 pinctrl-names:”default” 表示这是 默认 的 引脚 配置,在 设备树 中, pinctrl-names 通常 与 pinctrl-0 属性 配合 使用,用于 指定 不同 的 引脚 控制 设置。 pinctrl-0: pinctrl-0 定义了 设备 使用 的 默认 引脚 配置。在 此,它 引用 了 一个 名为 aon_gpio_6 的 引脚 配置管理 该 引脚 的 输入 \ 输出,复用 功能,驱动力 等。 id-gpios: id-gpios 是一个 用于 USB 端口 ID 检测 的 引脚 配置。 USB OTG 端口 会 检测 设备 端口 的 类型(主机 或 设备),通常 通过 ID 引脚 来 确定 设备 连接 状态 ,<&aon_gpio_porta 6 GPIO_ACTIVE_HIGH> 表示 该 引脚 配置 位于 aon_gpio_porta,并且 使用 端口 6 。 GPIO_ACTIVE_HIGH 表示 引脚 为 高电平 时 触发。 status: status 属性通常 用来 表示 该 节点 是否 启用,设备树 中, status 属性 还 可以 有 disabled、 okay、 等值,用来 控制 设备 的 启用 或 禁用,”okay” 表示 启用 该 功能。
USB OTG 引脚
驱动代码接口
int horizon_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *configs,
unsigned int num_configs)
{
struct horizon_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
unsigned int arg;
int i, ret = 0;
enum pin_config_param param;
if (num_configs == 0) {
dev_err(ipctl->dev, "No pinconfigs to set\n");
return -EINVAL;
}
for (i = 0; i < num_configs; i++) {
param = pinconf_to_config_param(configs[i]);
arg = pinconf_to_config_argument(configs[i]);
dev_dbg(ipctl->dev, "horizon pinconf pin-%d, param=%d, arg=%d\n", pin, param, arg);
switch (param) {
case PIN_CONFIG_POWER_SOURCE:
horizon_pin_power_source(ipctl, pin, arg);
break;
case PIN_CONFIG_BIAS_DISABLE:
horizon_pin_pull_disable(ipctl, pin);
break;
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
/* horizon do not support those configs */
break;
case PIN_CONFIG_BIAS_PULL_UP:
horizon_pin_pull_en(ipctl, pin, HORIZON_PULL_UP);
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
horizon_pin_pull_en(ipctl, pin, HORIZON_PULL_DOWN);
break;
case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
horizon_pin_schmit_en(ipctl, pin, arg);
break;
case PIN_CONFIG_DRIVE_STRENGTH:
horizon_pin_drv_str_set(ipctl, pin, arg);
break;
case PIN_CONFIG_OUTPUT:
ret = horizon_gpio_set_direction(pctldev, pin, false, arg);
break;
case PIN_CONFIG_INPUT_ENABLE:
ret = horizon_gpio_set_direction(pctldev, pin, true, arg);
break;
default:
dev_err(ipctl->dev, "horizon pinconf pin %d param:%d not support\n", pin,
param);
return -ENOTSUPP;
}
} /* For each config */
return ret;
}
PIN_CONFIG_POWER_SOURCE:调用 horizon_pin_power_source 来设置 引脚 的 电源 配置。 PIN_CONFIG_BIAS_DISABLE:调用 horizon_pin_pull_disable 禁用引脚 的 上 拉 或 下 拉 电阻。 PIN_CONFIG_BIAS_PULL_UP:调用 horizon_pin_pull_en 启用上拉 电阻。 PIN_CONFIG_BIAS_PULL_DOWN:调用 horizon_pin_pull_en 启用下 拉 电阻。 PIN_CONFIG_INPUT_SCHMITT_ENABLE:调用 horizon_pin_schmit_en 启用 Schmitt 触发器,以改善 输入 信号 的 噪声 抑制。 PIN_CONFIG_DRIVE_STRENGTH:调用 horizon_pin_drv_str_set 设置引脚 的 驱动 强度, arg 通常 表示 驱动 强度 的 具体 数值。 PIN_CONFIG_OUTPUT:用 horizon_gpio_set_direction 设置引脚 方向 为 输出, arg 表示 输出 电平(高 或 低)。 PIN_CONFIG_INPUT_ENABLE:调用 horizon_gpio_set_direction 设置引脚 方向 为 输入, arg 可能 表示 输入 模式 的 附加 配置(如 上 拉、下 拉)。
4.3.4.5. 功能使用
Uboot space
代码位置
/uboot/arch/arm/dts/pinmux-func.dtsi # uboot 设备树 pinctrl 复用关系位置
/uboot/arch/arm/dts/x5.dtsi # uboot 设备树引用节点位置
调用示例
在 Uboot 状态gpio cmd 可
显示 gpio 接口
状态 信息:
Hobot>gpio status -a
Bank gpio@ls_0_:
gpio@ls_0_0: input: 1 [ ]
gpio@ls_0_1: input: 1 [ ]
gpio@ls_0_2: input: 1 [ ]
gpio@ls_0_3: input: 1 [ ]
gpio@ls_0_4: input: 1 [ ]
gpio@ls_0_5: input: 1 [ ]
gpio@ls_0_6: input: 1 [ ]
gpio@ls_0_7: input: 1 [ ]
gpio@ls_0_8: input: 1 [ ]
gpio@ls_0_9: input: 1 [ ]
gpio@ls_0_10: input: 1 [ ]
gpio@ls_0_11: input: 1 [ ]
gpio@ls_0_12: input: 1 [ ]
gpio@ls_0_13: input: 1 [ ]
gpio@ls_0_14: input: 0 [ ]
gpio@ls_0_15: input: 0 [ ]
gpio@ls_0_16: input: 0 [ ]
gpio@ls_0_17: input: 0 [ ]
gpio@ls_0_18: input: 0 [ ]
gpio@ls_0_19: input: 0 [ ]
gpio@ls_0_20: input: 1 [ ]
gpio@ls_0_21: input: 1 [ ]
gpio@ls_0_22: input: 1 [ ]
gpio@ls_0_23: input: 1 [ ]
gpio@ls_0_24: input: 1 [ ]
gpio@ls_0_25: input: 0 [ ]
gpio@ls_0_26: input: 1 [ ]
gpio@ls_0_27: input: 1 [ ]
gpio@ls_0_28: input: 1 [ ]
gpio@ls_0_29: input: 1 [ ]
gpio@ls_0_30: input: 1 [ ]
gpio@ls_0_31: input: 1 [ ]
Bank gpio@ls_1_:
gpio@ls_1_0: input: 1 [ ]
gpio@ls_1_1: input: 1 [ ]
gpio@ls_1_2: input: 1 [ ]
gpio@ls_1_3: input: 1 [ ]
gpio@ls_1_4: input: 1 [ ]
gpio@ls_1_5: input: 0 [ ]
gpio@ls_1_6: input: 1 [ ]
gpio@ls_1_7: input: 1 [ ]
gpio@ls_1_8: input: 1 [ ]
gpio@ls_1_9: input: 1 [ ]
gpio@ls_1_10: input: 1 [ ]
gpio@ls_1_11: input: 1 [ ]
gpio@ls_1_12: input: 1 [ ]
gpio@ls_1_13: input: 1 [ ]
gpio@ls_1_14: input: 0 [ ]
gpio@ls_1_15: input: 1 [ ]
gpio@ls_1_16: input: 1 [ ]
Bank gpio@hs_0_:
gpio@hs_0_0: input: 0 [ ]
gpio@hs_0_1: input: 0 [ ]
gpio@hs_0_2: input: 0 [ ]
gpio@hs_0_3: input: 0 [ ]
gpio@hs_0_4: input: 0 [ ]
gpio@hs_0_5: input: 0 [ ]
gpio@hs_0_6: input: 0 [ ]
gpio@hs_0_7: input: 0 [ ]
gpio@hs_0_8: input: 0 [ ]
gpio@hs_0_9: input: 0 [ ]
gpio@hs_0_10: input: 0 [ ]
gpio@hs_0_11: input: 0 [ ]
gpio@hs_0_12: input: 0 [ ]
gpio@hs_0_13: input: 0 [ ]
gpio@hs_0_14: input: 0 [ ]
gpio@hs_0_15: input: 0 [ ]
gpio@hs_0_16: input: 0 [ ]
gpio@hs_0_17: input: 0 [ ]
gpio@hs_0_18: input: 0 [ ]
gpio@hs_0_19: input: 0 [ ]
gpio@hs_0_20: input: 0 [ ]
gpio@hs_0_21: input: 0 [ ]
gpio@hs_0_22: input: 0 [ ]
gpio@hs_0_23: input: 1 [ ]
gpio@hs_0_24: input: 1 [ ]
gpio@hs_0_25: input: 1 [ ]
gpio@hs_0_26: input: 1 [ ]
gpio@hs_0_27: input: 1 [ ]
gpio@hs_0_28: input: 1 [ ]
gpio@hs_0_29: input: 1 [ ]
gpio@hs_0_30: input: 1 [ ]
Bank gpio@hs_1_:
gpio@hs_1_0: input: 0 [ ]
gpio@hs_1_1: input: 0 [ ]
gpio@hs_1_2: input: 0 [ ]
gpio@hs_1_3: input: 0 [ ]
gpio@hs_1_4: input: 0 [ ]
gpio@hs_1_5: input: 0 [ ]
gpio@hs_1_6: input: 0 [ ]
gpio@hs_1_7: input: 0 [ ]
gpio@hs_1_8: input: 0 [ ]
gpio@hs_1_9: input: 0 [ ]
gpio@hs_1_10: input: 0 [ ]
gpio@hs_1_11: input: 0 [ ]
gpio@hs_1_12: input: 0 [ ]
gpio@hs_1_13: input: 0 [ ]
gpio@hs_1_14: input: 0 [ ]
gpio@hs_1_15: input: 0 [ ]
gpio@hs_1_16: input: 0 [ ]
gpio@hs_1_17: input: 0 [ ]
Bank gpio@dsp_:
gpio@dsp_0: input: 1 [ ]
gpio@dsp_1: input: 1 [ ]
gpio@dsp_2: input: 0 [ ]
gpio@dsp_3: input: 0 [ ]
gpio@dsp_4: input: 0 [ ]
gpio@dsp_5: input: 0 [ ]
gpio@dsp_6: input: 0 [ ]
gpio@dsp_7: input: 0 [ ]
gpio@dsp_8: input: 0 [ ]
gpio@dsp_9: input: 1 [ ]
gpio@dsp_10: input: 1 [ ]
gpio@dsp_11: input: 1 [ ]
gpio@dsp_12: input: 1 [ ]
gpio@dsp_13: input: 1 [ ]
gpio@dsp_14: input: 0 [ ]
gpio@dsp_15: input: 0 [ ]
gpio@dsp_16: input: 0 [ ]
gpio@dsp_17: input: 0 [ ]
gpio@dsp_18: input: 0 [ ]
gpio@dsp_19: input: 1 [ ]
gpio@dsp_20: input: 0 [ ]
gpio@dsp_21: input: 1 [ ]
gpio@dsp_22: input: 1 [ ]
显示
单个 gpio 接口 状态 信息:
Hobot>gpio status gpio@ls_0_4
Bank gpio@ls_0_:
gpio@ls_0_4: input: 1 [ ]
Hobot>
设置 gpio 为
输入 模式:
Hobot>gpio input gpio@ls_0_4
gpio: pin gpio@ls_0_4 (gpio 4) value is 1
Hobot>
Hobot>gpio status gpio@ls_0_4
Bank gpio@ls_0_:
gpio@ls_0_4: input: 1 [ ]
设置 gpio 为
高电平 输出 模式:
Hobot>gpio set gpio@ls_0_4
gpio: pin gpio@ls_0_4 (gpio 4) value is 1
Hobot>gpio status gpio@ls_0_4
Bank gpio@ls_0_:
gpio@ls_0_4: output: 1 [ ]
设置 gpio 为
低电平 输出 模式:
Hobot>gpio clear gpio@ls_0_4
gpio: pin gpio@ls_0_4 (gpio 4) value is 0
Hobot>gpio status gpio@ls_0_4
Bank gpio@ls_0_:
gpio@ls_0_4: output: 0 [ ]
设置 gpio 电平
翻转:
Hobot>gpio toggle gpio@ls_0_4
gpio: pin gpio@ls_0_4 (gpio 4) value is 1
Hobot>gpio status gpio@ls_0_4
Bank gpio@ls_0_:
gpio@ls_0_4: output: 1 [ ]
对应引脚
可/uboot/arch/arm/dts/pinmux-func.dtsi 查找
pinctrl_lsio_gpio0_0_3: lsiogpio0grp0 {
horizon,pins = <
LSIO_UART7_RX LSIO_PINMUX_3 BIT_OFFSET4 MUX_ALT2 &pconf_input_en
LSIO_UART7_TX LSIO_PINMUX_3 BIT_OFFSET6 MUX_ALT2 &pconf_input_en
LSIO_UART7_CTS LSIO_PINMUX_3 BIT_OFFSET8 MUX_ALT2 &pconf_input_en
LSIO_UART7_RTS LSIO_PINMUX_3 BIT_OFFSET10 MUX_ALT2 &pconf_input_en
>;
};
ls_0_0 对应 LSIO_UART7_RX 引脚,ls_0_1 对应 LSIO_UART7_TX 引脚,依此类推pinmux-func.dtsi 查看
User Space
控制接口
/sys/class/gpio/export # 用户空间可以通过写入 gpio 号申请将 gpio 的控制权导出到用户空间,比如 echo 389 > export
/sys/class/gpio/unexport # 和 export 相反
/sys/class/gpio/gpiochip0 # gpio 控制器
调用接口
使用 export 导出 gpio 的/sys/class/gpio/gpio356/,路径
direction:表示 GPIO 端口
方向,读取 为“ in”或“ out”,写入“ in”或者“ out”可以 设置 输入 或 输出 value:表示 GPIO 的
电平, 0 为 低电平, 1 为 高电平,如果 GPIO 配置 为 输出,则 value 值 可 写 edge:表示
中断 触发 方式,有“ none” “ rising” “ falling” “ both” 4 种 类型,“ none”表示 GPIO 不 为 中断 引脚,“ rising”表示 引脚 为 上升 沿 触发 的 中断,“ falling”表示 引脚 为 下降 沿 触发 的 中断,“ both”表示 引脚 为 边沿 触发 的 中断。
调用示例
以下
echo 389 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio389/direction
echo 1 > /sys/class/gpio/gpio389/value
# echo 0 > /sys/class/gpio/gpiov/value
echo 389 > /sys/class/gpio/unexport
调试接口
如果
首先,检查
mount | grep debugfs
如果
mount -t debugfs none /sys/kernel/debug
确保 debugfs 已
# cat /sys/kernel/debug/gpio
gpiochip5: GPIOs 347-363, parent: platform/34130000.gpio, 34130000.gpio:
gpio-352 ( |enable ) out lo
gpio-354 ( |scl ) in lo
gpio-355 ( |sda ) in lo
gpio-358 ( |scl ) in lo
gpio-359 ( |sda ) in lo
gpio-360 ( |scl ) in lo
gpio-361 ( |sda ) in lo
gpio-362 ( |scl ) in lo
gpio-363 ( |sda ) in lo
gpiochip4: GPIOs 379-410, parent: platform/34120000.gpio, 34120000.gpio:
gpio-385 ( |power ) out hi
gpio-386 ( |reset ) out lo
gpio-389 ( |scl ) in lo
gpio-390 ( |sda ) in lo
gpio-409 ( |scl ) in lo
gpio-410 ( |sda ) in lo
gpiochip3: GPIOs 411-433, parent: platform/32150000.gpio, 32150000.gpio:
gpio-411 ( |scl ) in lo
gpio-412 ( |sda ) in lo
gpio-431 ( |reset ) out hi ACTIVE LOW
gpiochip2: GPIOs 434-451, parent: platform/35070000.gpio, 35070000.gpio:
gpio-435 ( |voltage ) out hi ACTIVE LOW
gpiochip1: GPIOs 466-496, parent: platform/35060000.gpio, 35060000.gpio:
gpio-489 ( |phyreset ) out hi
gpio-492 ( |power ) out hi
gpiochip0: GPIOs 498-505, parent: platform/31000000.gpio, 31000000.gpio:
gpio-500 ( |GPIO Key Power ) in hi IRQ ACTIVE LOW
gpio-503 ( |id ) in lo IRQ
gpio-504 ( |id ) in hi IRQ
备注:
上述
输出 仅为 示例,实际 输出 与 板端 实际 的 DTS 配置 相关; 主控 GPIO 在 User Space 的
接口 都 是 Linux 的 标准接口,更 多 使用 方法 请 参考 Documentation/gpio/sysfs.txt。
GPIO 序号与 sysfs 中 GPIO 号的换算关系
以 LSIO_UART3_RXD 管脚LSIO_GPIO0_PIN10,它

寄存器0x34120000, 通过cat /sys/kernel/debug/gpio 可以
cat /sys/kernel/debug/gpio
...
gpiochip10: GPIOs 379-410, parent: platform/34120000.gpio, 34120000.gpio:
...
LSIO_GPIO0 的LSIO_UART3_RXD.
备注:
上述
Linux GPIO 序号与芯片 Pin 脚的映射关系
注意:
Linux 内
的 GPIO 序号 为 纯 软件 概念,会随着 软件 改变 而 发生变化, GPIO 序号 与 芯片 Pin 脚 没有 物理 意义 上 的 绑定 关系; 通过
在 dts 内 配置“ gpio-base”字 段,固定 住 了 每个 GPIO 控制器 在 Linux 系统 内 的 序号 起始 数字,从而 实现 了 GPIO 序号 固定 的 功能;
| GPIO Sysfs 序号 | Pin 名 | GPIO 功能 |
|---|---|---|
| 347 | LSIO_SPI5_SCLK | LSIO_GPIO1_PIN00 |
| 348 | LSIO_SPI5_SSN | LSIO_GPIO1_PIN01 |
| 349 | LSIO_SPI5_MISO | LSIO_GPIO1_PIN02 |
| 350 | LSIO_SPI5_MOSI | LSIO_GPIO1_PIN03 |
| 351 | LSIO_SPI0_SSN | LSIO_GPIO1_PIN04 |
| 352 | LSIO_SPI0_MISO | LSIO_GPIO1_PIN05 |
| 353 | LSIO_SPI0_MOSI | LSIO_GPIO1_PIN06 |
| 354 | LSIO_I2C0_SCL | LSIO_GPIO1_PIN07 |
| 355 | LSIO_I2C0_SDA | LSIO_GPIO1_PIN08 |
| 356 | LSIO_I2C1_SCL | LSIO_GPIO1_PIN09 |
| 357 | LSIO_I2C1_SDA | LSIO_GPIO1_PIN10 |
| 358 | LSIO_I2C2_SCL | LSIO_GPIO1_PIN11 |
| 359 | LSIO_I2C2_SDA | LSIO_GPIO1_PIN12 |
| 360 | LSIO_I2C3_SCL | LSIO_GPIO1_PIN13 |
| 361 | LSIO_I2C3_SDA | LSIO_GPIO1_PIN14 |
| 362 | LSIO_I2C4_SCL | LSIO_GPIO1_PIN15 |
| 363 | LSIO_I2C4_SDA | LSIO_GPIO1_PIN16 |
| 364 | Reserved | Reserved |
| 365 | Reserved | Reserved |
| 366 | Reserved | Reserved |
| 367 | Reserved | Reserved |
| 368 | Reserved | Reserved |
| 369 | Reserved | Reserved |
| 370 | Reserved | Reserved |
| 371 | Reserved | Reserved |
| 372 | Reserved | Reserved |
| 373 | Reserved | Reserved |
| 374 | Reserved | Reserved |
| 375 | Reserved | Reserved |
| 376 | Reserved | Reserved |
| 377 | Reserved | Reserved |
| 378 | Reserved | Reserved |
| 379 | LSIO_UART7_RXD | LSIO_GPIO0_PIN00 |
| 380 | LSIO_UART7_TXD | LSIO_GPIO0_PIN01 |
| 381 | LSIO_UART7_CTS_N | LSIO_GPIO0_PIN02 |
| 382 | LSIO_UART7_RTS_N | LSIO_GPIO0_PIN03 |
| 383 | LSIO_UART1_RXD | LSIO_GPIO0_PIN04 |
| 384 | LSIO_UART1_TXD | LSIO_GPIO0_PIN05 |
| 385 | LSIO_UART1_CTS_N | LSIO_GPIO0_PIN06 |
| 386 | LSIO_UART1_RTS_N | LSIO_GPIO0_PIN07 |
| 387 | LSIO_UART2_RXD | LSIO_GPIO0_PIN08 |
| 388 | LSIO_UART2_TXD | LSIO_GPIO0_PIN09 |
| 389 | LSIO_UART3_RXD | LSIO_GPIO0_PIN10 |
| 390 | LSIO_UART3_TXD | LSIO_GPIO0_PIN11 |
| 391 | LSIO_UART4_RXD | LSIO_GPIO0_PIN12 |
| 392 | LSIO_UART4_TXD | LSIO_GPIO0_PIN13 |
| 393 | LSIO_SPI0_SCLK | LSIO_GPIO0_PIN14 |
| 394 | LSIO_SPI1_SSN_1 | LSIO_GPIO0_PIN15 |
| 395 | LSIO_SPI1_SCLK | LSIO_GPIO0_PIN16 |
| 396 | LSIO_SPI1_SSN | LSIO_GPIO0_PIN17 |
| 397 | LSIO_SPI1_MISO | LSIO_GPIO0_PIN18 |
| 398 | LSIO_SPI1_MOSI | LSIO_GPIO0_PIN19 |
| 399 | LSIO_SPI2_SCLK | LSIO_GPIO0_PIN20 |
| 400 | LSIO_SPI2_SSN | LSIO_GPIO0_PIN21 |
| 401 | LSIO_SPI2_MISO | LSIO_GPIO0_PIN22 |
| 402 | LSIO_SPI2_MOSI | LSIO_GPIO0_PIN23 |
| 403 | LSIO_SPI3_SCLK | LSIO_GPIO0_PIN24 |
| 404 | LSIO_SPI3_SSN | LSIO_GPIO0_PIN25 |
| 405 | LSIO_SPI3_MISO | LSIO_GPIO0_PIN26 |
| 406 | LSIO_SPI3_MOSI | LSIO_GPIO0_PIN27 |
| 407 | LSIO_SPI4_SCLK | LSIO_GPIO0_PIN28 |
| 408 | LSIO_SPI4_SSN | LSIO_GPIO0_PIN29 |
| 409 | LSIO_SPI4_MISO | LSIO_GPIO0_PIN30 |
| 410 | LSIO_SPI4_MOSI | LSIO_GPIO0_PIN31 |
| 411 | DSP_I2C7_SCL | DSP_GPIO0_PIN00 |
| 412 | DSP_I2C7_SDA | DSP_GPIO0_PIN01 |
| 413 | DSP_UART0_RXD | DSP_GPIO0_PIN02 |
| 414 | DSP_UART0_TXD | DSP_GPIO0_PIN03 |
| 415 | DSP_I2S0_MCLK | DSP_GPIO0_PIN04 |
| 416 | DSP_I2S0_SCLK | DSP_GPIO0_PIN05 |
| 417 | DSP_I2S0_WS | DSP_GPIO0_PIN06 |
| 418 | DSP_I2S0_DI | DSP_GPIO0_PIN07 |
| 419 | DSP_I2S0_DO | DSP_GPIO0_PIN08 |
| 420 | DSP_I2S1_MCLK | DSP_GPIO0_PIN09 |
| 421 | DSP_I2S1_SCLK | DSP_GPIO0_PIN10 |
| 422 | DSP_I2S1_WS | DSP_GPIO0_PIN11 |
| 423 | DSP_I2S1_DI | DSP_GPIO0_PIN12 |
| 424 | DSP_I2S1_DO | DSP_GPIO0_PIN13 |
| 425 | DSP_PDM_CKO | DSP_GPIO0_PIN14 |
| 426 | DSP_PDM_IN0 | DSP_GPIO0_PIN15 |
| 427 | DSP_PDM_IN1 | DSP_GPIO0_PIN16 |
| 428 | DSP_PDM_IN2 | DSP_GPIO0_PIN17 |
| 429 | DSP_PDM_IN3 | DSP_GPIO0_PIN18 |
| 430 | DSP_SPI6_SCLK | DSP_GPIO0_PIN19 |
| 431 | DSP_SPI6_SSN | DSP_GPIO0_PIN20 |
| 432 | DSP_SPI6_MISO | DSP_GPIO0_PIN21 |
| 433 | DSP_SPI6_MOSI | DSP_GPIO0_PIN22 |
| 434 | HSIO_QSPI_SSN_0 | HSIO_GPIO1_PIN00 |
| 435 | HSIO_QSPI_SSN_1 | HSIO_GPIO1_PIN01 |
| 436 | HSIO_QSPI_SCLK | HSIO_GPIO1_PIN02 |
| 437 | HSIO_QSPI_DATA_0 | HSIO_GPIO1_PIN03 |
| 438 | HSIO_QSPI_DATA_1 | HSIO_GPIO1_PIN04 |
| 439 | HSIO_QSPI_DATA_2 | HSIO_GPIO1_PIN05 |
| 440 | HSIO_QSPI_DATA_3 | HSIO_GPIO1_PIN06 |
| 441 | EMMC_SDCLK | HSIO_GPIO1_PIN07 |
| 442 | EMMC_CMD | HSIO_GPIO1_PIN08 |
| 443 | EMMC_DATA_0 | HSIO_GPIO1_PIN09 |
| 444 | EMMC_DATA_1 | HSIO_GPIO1_PIN10 |
| 445 | EMMC_DATA_2 | HSIO_GPIO1_PIN11 |
| 446 | EMMC_DATA_3 | HSIO_GPIO1_PIN12 |
| 447 | EMMC_DATA_4 | HSIO_GPIO1_PIN13 |
| 448 | EMMC_DATA_5 | HSIO_GPIO1_PIN14 |
| 449 | EMMC_DATA_6 | HSIO_GPIO1_PIN15 |
| 450 | EMMC_DATA_7 | HSIO_GPIO1_PIN16 |
| 451 | EMMC_RST_n | HSIO_GPIO1_PIN17 |
| 452 | Reserved | Reserved |
| 453 | Reserved | Reserved |
| 454 | Reserved | Reserved |
| 455 | Reserved | Reserved |
| 456 | Reserved | Reserved |
| 457 | Reserved | Reserved |
| 458 | Reserved | Reserved |
| 459 | Reserved | Reserved |
| 460 | Reserved | Reserved |
| 461 | Reserved | Reserved |
| 462 | Reserved | Reserved |
| 463 | Reserved | Reserved |
| 464 | Reserved | Reserved |
| 465 | Reserved | Reserved |
| 466 | HSIO_ENET_MDC | HSIO_GPIO0_PIN00 |
| 467 | HSIO_ENET_MDIO | HSIO_GPIO0_PIN01 |
| 468 | HSIO_ENET_TXD_0 | HSIO_GPIO0_PIN02 |
| 469 | HSIO_ENET_TXD_1 | HSIO_GPIO0_PIN03 |
| 470 | HSIO_ENET_TXD_2 | HSIO_GPIO0_PIN04 |
| 471 | HSIO_ENET_TXD_3 | HSIO_GPIO0_PIN05 |
| 472 | HSIO_ENET_TXEN | HSIO_GPIO0_PIN06 |
| 473 | HSIO_ENET_TX_CLK | HSIO_GPIO0_PIN07 |
| 474 | HSIO_ENET_RX_CLK | HSIO_GPIO0_PIN08 |
| 475 | HSIO_ENET_RXD_0 | HSIO_GPIO0_PIN09 |
| 476 | HSIO_ENET_RXD_1 | HSIO_GPIO0_PIN10 |
| 477 | HSIO_ENET_RXD_2 | HSIO_GPIO0_PIN11 |
| 478 | HSIO_ENET_RXD_3 | HSIO_GPIO0_PIN12 |
| 479 | HSIO_ENET_RXDV | HSIO_GPIO0_PIN13 |
| 480 | HSIO_EPHY_CLK | HSIO_GPIO0_PIN14 |
| 481 | HSIO_SD_WP | HSIO_GPIO0_PIN15 |
| 482 | HSIO_SD_SDCLK | HSIO_GPIO0_PIN16 |
| 483 | HSIO_SD_CMD | HSIO_GPIO0_PIN17 |
| 484 | HSIO_SD_CDN | HSIO_GPIO0_PIN18 |
| 485 | HSIO_SD_DATA_0 | HSIO_GPIO0_PIN19 |
| 486 | HSIO_SD_DATA_1 | HSIO_GPIO0_PIN20 |
| 487 | HSIO_SD_DATA_2 | HSIO_GPIO0_PIN21 |
| 488 | HSIO_SD_DATA_3 | HSIO_GPIO0_PIN22 |
| 489 | HSIO_SDIO_WP | HSIO_GPIO0_PIN23 |
| 490 | HSIO_SDIO_SDCLK | HSIO_GPIO0_PIN24 |
| 491 | HSIO_SDIO_CMD | HSIO_GPIO0_PIN25 |
| 492 | HSIO_SDIO_CDN | HSIO_GPIO0_PIN26 |
| 493 | HSIO_SDIO_DATA_0 | HSIO_GPIO0_PIN27 |
| 494 | HSIO_SDIO_DATA_1 | HSIO_GPIO0_PIN28 |
| 495 | HSIO_SDIO_DATA_2 | HSIO_GPIO0_PIN29 |
| 496 | HSIO_SDIO_DATA_3 | HSIO_GPIO0_PIN30 |
| 497 | Reserved | Reserved |
| 498 | AON_GPIO0_PIN00 | AON_GPIO0_PIN00 |
| 499 | AON_GPIO0_PIN01 | AON_GPIO0_PIN01 |
| 500 | AON_GPIO0_PIN02 | AON_GPIO0_PIN02 |
| 501 | AON_GPIO0_PIN03 | AON_GPIO0_PIN03 |
| 502 | AON_GPIO0_PIN04 | AON_GPIO0_PIN04 |
| 503 | AON_ENV_VDD | AON_GPIO0_PIN05 |
| 504 | AON_ENV_CNN0 | AON_GPIO0_PIN06 |
| 505 | AON_ENV_CNN1 | AON_GPIO0_PIN07 |
hb_gpioinfo 工具介绍
hb_gpioinfo 是
hb_gpioinfo 使用实例
PinName:指
的 是 Soc 上 的 管脚 名字,原理图 上 管脚 命名 一致 PinNum:指
的 是 主控 实际 的 对应 的 管脚 gpio 编号 PinFunc:指
的 是 主控 实际 的 设备树 中 已经 使用 的 管脚 对应 的 复用 功能 PinStrength :指
的 是 主控 实际 的 设备树 中 配置 的 当前 功能 的 驱动 能力
查看 PinFunc 时
root@ubuntu:~# hb_gpioinfo
gpiochip0 - 8 lines: @31000000.gpio: @498-505
[Number] [Mode] [Status] [GpioName] [PinName] [PinNum] [PinFunc] [PinStrength]
line 0: unnamed input AON_GPIO_PIN0 498 aon_gpio_0 16 mA
line 1: unnamed input interrupt AON_GPIO_PIN1 499 Default 10 mA
line 2: unnamed input active-low GPIO Key Power AON_GPIO_PIN2 500 aon_gpio_2 19 mA
line 3: unnamed output power AON_GPIO_PIN3 501 aon_gpio_3 0 mA
line 4: unnamed input AON_GPIO_PIN4 502 aon_gpio_4 16 mA
line 5: unnamed input id AON_ENV_VDD 503 aon_gpio_5 19 mA
line 6: unnamed input id AON_ENV_CNN0 504 aon_gpio_6 16 mA
line 7: unnamed input AON_ENV_CNN1 505 aon_gpio_7 19 mA
gpiochip1 - 31 lines: @35060000.gpio: @466-496
[Number] [Mode] [Status] [GpioName] [PinName] [PinNum] [PinFunc] [PinStrength]
line 0: unnamed input HSIO_ENET_MDC 466 enetgrp 9 mA
line 1: unnamed input HSIO_ENET_MDIO 467 enetgrp 9 mA
line 2: unnamed input HSIO_ENET_TXD_0 468 enetgrp 9 mA
line 3: unnamed input HSIO_ENET_TXD_1 469 enetgrp 9 mA
line 4: unnamed input HSIO_ENET_TXD_2 470 enetgrp 9 mA
line 5: unnamed input HSIO_ENET_TXD_3 471 enetgrp 9 mA
line 6: unnamed input HSIO_ENET_TXEN 472 enetgrp 9 mA
line 7: unnamed input HSIO_ENET_TX_CLK 473 enetgrp 9 mA
line 8: unnamed input HSIO_ENET_RX_CLK 474 enetgrp 9 mA
GPIO IRQ 功能使用
驱动代码
app/samples/platform_samples/chip_base_test/12_gpio_irq_key/gpio_key_drv.c # gpio_irq 驱动代码
app/samples/platform_samples/chip_base_test/12_gpio_irq_key/Makefile # Makefile 编译文件
DTS 配置
设备树
/* arch/arm64/boot/dts/hobot/x5-evb-v2.dts */
gpio_keys_hobot {
compatible = "hobot,gpio_key";
debounce-interval = <200>;
gpios = <&ls_gpio0_porta 12 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
};
compatible:用于声明 设备 与 特定 驱动 的 兼容性。它 是 一个 字符串,内核 通过 这个 属性 来 匹配 设备树 中 的 硬件 节点 和 相应 的 驱动程序 。 debounce-interval:此配置 项会 在 驱动 中 解析,用于 配置 按键 的 去 抖动 时间 防止 按键 状态 抖动 导致 误 触发。 gpios:指定 GPIO 控制器、引脚编号 和 电平 极性。此处 配置 为 ls_gpio0_porta 控制器 的 第 12 号 引脚,低电平 有效,表示 该 引脚 在 被 拉 低时 触发 响应。 pinctrl-names:用于指定 设备 在 运行 时 使用 的 引脚 控制(pinctrl)状态 名称 列表。
备注:
可以
注意 到 设备树 中 GPIO 节点 并 没有 指定 interrupts 属性,因为 在 驱动 中 使用 gpio_to_irq 将 GPIO(通用 输入 / 输出)引脚 编号 转换 为 对应 的 中断请求( IRQ)编号 的 函数。这个 函数 的 主要 作用 是 对于 那些 可以 生成 中断 信号 的 GPIO 引脚,获取 与 该 GPIO 引脚 相关联 的 中断 号 。
驱动代码接口
1.of_gpio_count(const struct device_node *np)
描述:该
函数 用于 从 设备树 节点( device_node)中 获取 定义 的 GPIO 引脚 数量。 参数: np:指向
设备树 节点 的 指针。 用法:当
设备 驱动 需要 知道 设备树 中为 某个 设备 定义 了 多少 个 GPIO 引脚 时,可以 调用 这个 函数。这个 函数 通常 会 用于 多 按键、多个 外设 控制 的 场景。
2. of_property_read_u32(const struct device_node *np, const char *property, u32 *out_value)
描述:该
函数 用于 从 设备树 中 读取 指定 属性 的 32 位 无 符号 整数 值。设备树 中 的 属性 通常 是 由 设备树 的 设备描述 节点( device_node)定义 的。 参数: np:指向
设备树 节点 的 指针。 np:指向设备树 节点 的 指针,表示 要 读取 属性 的 设备 节点。 property:属性的 名称,通常 是 一个 字符串(例如 “debounce-interval”)。 out_value:指向变量 的 指针,用于 存储 读取 到 的 值。
用法:该
函数 通常 用于 读取 设备树 中 定义 的 属性,例如 读 取消 抖 时间( debounce-interval),从而 配置 按键 的 去 抖动 行为 该 函数 通常 用于 读取 设备树 中 定义 的 属性,例如 读 取消 抖 时间( debounce-interval),从而 配置 按键 的 去 抖动 行为。
3. devm_gpio_request_one(struct device *dev, unsigned int gpio, unsigned long flags, const char *label)
描述:该
函数 用于 请求 分配 一个 GPIO 引脚 并 设置 其 配置。它 是 设备 管理 API 的 一部分,允许 GPIO 的 请求 和 释放 与 设备 生命周期 绑定。 参数: np:指向
设备树 节点 的 指针。 dev:指向设备 的 指针( struct device),通常 是 与 GPIO 引脚 关联 的 设备。 gpio:要请求 的 GPIO 引脚 编号。 flags:设置 GPIO 的标志 位,通常 包括 设置 方向(输入 或 输出)、极性(高电平 或 低电平 触发)等。 label: GPIO 引脚的 标签,通常 是 一个 描述性 的 字符串,用于 调试 和 识别,一般 写 NULL 即可。
用法:该
函数 用于 在 驱动程序 中 请求 GPIO 引脚 并 进行 初始化。它会 设置 GPIO 的 方向、极性 等 信息,便于 后续 操作。
4. gpio_to_irq(unsigned int gpio)
描述:该
函数 用于 将 指定 的 GPIO 引脚 转换 为 中断 号,返回 的 中断 号 可以 用于 request_irq 函数 中 注册 中断 处理程序 参数: gpio:要
转换 的 GPIO 引脚 编号。 用法:该
函数 通常 用于 从 GPIO 引脚 请求 中断 号,以便 将 其 作为 中断 源 传递 给 request_irq。
5. irqreturn_t xxx_irq_handler(int irq, void *dev_id)
描述:这是
一个 中断 处理函数 的 声明。中断 处理函数 用于 处理 中断 事件。当 GPIO 引脚 的 状态 变化 时,内核 会 触发 与 该 GPIO 引脚 相关联 的 中断,调用 相应 的 中断 处理程序。 参数: irq:触发
的 中断 号。 dev_id:中断处理程序 的 私有 数据,通常 是 设备 驱动 中 的 结构 体 指针。
用法: xxx_irq_handler 是
一个 中断 服务例程( ISR),它会 在 GPIO 引脚 触发 中断 时 被 调用。你 可以 在 该 函数 中 实现 特定 的 中断 处理 逻辑,如 读取 GPIO 值、更新 状态 等。
6. request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
描述:该
函数 用于 注册 中断 处理程序。它 将 指定 的 中断 号 与 中断 处理函数( ISR)关联 起来,当 中断 触发 时,内核 会 调用 该 处理函数。 参数: np:指向
设备树 节点 的 指针。 irq:中断号,即 某个 硬件 引脚 或 设备 生成 的 中断。 handler:中断处理 函数指针,指向 实际 的 中断 处理 逻辑(即 irq_handler_t 类型 的 函数)。 flags:中断触发 的 标志,表示 中断 的 类型,如 上升 沿、下降 沿、边缘 触发 等(如 IRQF_TRIGGER_FALLING)。 name:中断的 名称,通常 用于 调试 和 识别 中断 源。 dev:中断处理程序 的 私有 数据,通常 是 一个 指向 设备 结构 体 的 指针。
用法:在
驱动程序 中,当 某个 硬件 设备(如 GPIO 引脚)需要 中断 处理 时,使用 该 函数 将 中断 处理程序( ISR)与 中断 号 关联。
7. free_irq(unsigned int irq, void *dev_id)
描述:该
函数 用于 释放 之前 通过 request_irq 申请 的 中断 资源。它会 取消 中断 号 与 中断 处理函数 的 绑定。 参数: irq:要
释放 的 中断 号。 dev_id:与中断 处理程序 关联 的 私有 数据,通常 是 设备 结构 体 指针。
用法:当
不再 需要 某个 中断 时(如 驱动 卸载 或 设备 关闭 时),调用 free_irq 来 释放 该 中断 资源,防止 内存 泄漏 和 其他 潜在 问题。
8. gpio_free(unsigned gpio)
描述:该
函数 用于 释放 先前 通过 devm_gpio_request_one 或 其他 GPIO 请求 函数 分配 的 GPIO 引脚 资源。它会 撤销 对 GPIO 引脚 的 请求,并 恢复 该 引脚 的 默认 状态。 参数: gpio:要
释放 的 GPIO 引脚 编号。 用法:当
不再 使用 某个 GPIO 引脚 时,应 调用 gpio_free 来 释放 它,确保 系统资源 得到 正确 清理。
驱动调用示例
static int gpio_key_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
int count, i, err;
u32 debounce_interval = 100; // 默认100ms
enum of_gpio_flags flag;
printk("%s: probe start\n", __func__);
count = of_gpio_count(node);
if (!count) {
dev_err(&pdev->dev, "No GPIO defined in DT\n");
return -EINVAL;
}
gpio_keys_hobot = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
if (!gpio_keys_hobot)
return -ENOMEM;
if (of_property_read_u32(node, "debounce-interval", &debounce_interval))
dev_info(&pdev->dev, "No debounce-interval in DT, use default %ums\n", debounce_interval);
else
dev_info(&pdev->dev, "debounce-interval: %u ms\n", debounce_interval);
for (i = 0; i < count; i++) {
gpio_keys_hobot[i].gpio = of_get_gpio_flags(node, i, &flag);
if (gpio_keys_hobot[i].gpio < 0) {
dev_err(&pdev->dev, "of_get_gpio_flags failed for index %d\n", i);
err = gpio_keys_hobot[i].gpio;
goto free_mem;
}
gpio_keys_hobot[i].gpiod = gpio_to_desc(gpio_keys_hobot[i].gpio);
if (!gpio_keys_hobot[i].gpiod) {
dev_err(&pdev->dev, "gpio_to_desc failed for gpio %d\n", gpio_keys_hobot[i].gpio);
err = -ENODEV;
goto free_mem;
}
gpio_keys_hobot[i].flag = flag & OF_GPIO_ACTIVE_LOW;
gpio_keys_hobot[i].irq = gpio_to_irq(gpio_keys_hobot[i].gpio);
if (gpio_keys_hobot[i].irq < 0) {
dev_err(&pdev->dev, "gpio_to_irq failed for gpio %d\n", gpio_keys_hobot[i].gpio);
err = gpio_keys_hobot[i].irq;
goto free_mem;
}
gpio_keys_hobot[i].debounce_interval = debounce_interval * HZ / 1000; // ms -> jiffies
gpio_keys_hobot[i].last_interrupt = jiffies;
err = request_irq(
gpio_keys_hobot[i].irq,
gpio_key_isr,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
dev_name(&pdev->dev),
&gpio_keys_hobot[i]);
if (err) {
dev_err(&pdev->dev, "request_irq failed for gpio %d irq %d\n",
gpio_keys_hobot[i].gpio, gpio_keys_hobot[i].irq);
goto free_irq;
}
}
platform_set_drvdata(pdev, gpio_keys_hobot);
dev_info(&pdev->dev, "gpio_key probe done.\n");
return 0;
free_irq:
while (--i >= 0)
free_irq(gpio_keys_hobot[i].irq, &gpio_keys_hobot[i]);
free_mem:
kfree(gpio_keys_hobot);
return err;
}
static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
struct gpio_key *key = dev_id;
unsigned long now = jiffies;
int val = gpiod_get_value(key->gpiod);
if (time_after(now, key->last_interrupt + key->debounce_interval)) {
printk(KERN_INFO "gpio_key: IRQ GPIO %d value=%d\n", key->gpio, val);
key->last_interrupt = now;
}
return IRQ_HANDLED;
}
gpio_key_probe 函数
获取 GPIO 数量
调用
of_gpio_count,从设备树 (Device Tree)中解析 出该 节点 下定义 的 GPIO引脚数量,用于 后续 循环 申请 和 初始化。
分配内存
使用
kzalloc按数量 申请 一个 gpio_key结构体 数组 gpio_keys_hobot,用于保存 每个 GPIO的相关 信息(PIN、IRQ、中断 防抖 时间 等)。
解析
防抖 时间 使用
of_property_read_u32从设备树 读取 debounce-interval属性(单位:ms),若未定义,则 使用 默认值(如 100ms)。
循环
获取 每个 GPIO 的 信息 调用
of_get_gpio_flags获取具体 的 GPIO PIN 和 active_low标志。调用
gpio_to_desc将 PIN 转换为 gpiod描述符,用于后续 读 电平。 调用
gpio_to_irq将 GPIO PIN 映射为 对应 的 中断 号 IRQ。保存
解析 到 的 参数 到 gpio_keys_hobot。
申请
中断 资源 对
每个 GPIO: irq:要申请 的 硬件 中断 号(由 gpio_to_irq获取)。gpio_key_isr:该 GPIO 对应的 中断 服务 函数。 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING:中断触发 条件,表示 上下 沿都 能 触发 中断。 dev_name(&pdev->dev):驱动名称,作为 中断 名称 标识。 &gpio_keys_hobot[i]:传递给 ISR 的 dev_id,用于区分 多个 GPIO 的 上下文。
若
申请 失败,调用 free_irq依次释放 已 申请 的 中断。
完成 probe 注册
全部
申请 成功 后,使用 platform_set_drvdata把gpio_keys_hobot保存到 pdev,方便后续 remove 阶段 访问。
编译脚本示例
TOPDIR := ../../../../../out/build/kernel
KERN_DIR := $(TOPDIR)
SRC := gpio_key_drv.c
MODULE_NAME := gpio_key_drv
OUT_DIR := output
all:
@mkdir -p $(OUT_DIR)
# 拷贝源码和构建描述文件到 output
cp $(SRC) $(OUT_DIR)/
echo "obj-m := $(MODULE_NAME).o" > $(OUT_DIR)/Makefile
make -C $(KERN_DIR) M=$(shell pwd)/$(OUT_DIR) modules
"${TOPDIR}"/scripts/sign-file sha512 \
"${TOPDIR}"/certs/signing_key.pem \
"${TOPDIR}"/certs/signing_key.x509 \
$(OUT_DIR)/$(MODULE_NAME).ko $(OUT_DIR)/$(MODULE_NAME)_sign.ko
clean:
make -C $(KERN_DIR) M=$(shell pwd) clean
rm -f *.ko *.o *.mod.c *.mod.o *.symvers *.order *.mod
使用示例
正常output 目录gpio_key_drv.ko 和 gpio_key_drv_sign.ko 两个/userdata 下且insmod gpio_key_drv.ko ,详细chip_base_test 目录cat /proc/interrupts 来

中断
触发
次数 :显示了 每个 中断 被 触发 的 次数,例如,中断 号 44 ( mmc2 )被 触发 了 14808 次,由于 刚 加载 驱动 但 并未 按键 去 触发,所以 gpio_key 的 中断 触发 次数 为 0 。 CPU0 ~ CPU7:显示
了 每个 CPU 核心 上 的 中断 计数。这些 数字 表示 自 系统启动 以来 每个 IRQ 处理 的 次数 。 中断
控制器 :大多数中断 都 由 GICv3 ( Generic Interrupt Controller version 3 )管理,这是 一个 通用 中断 控制器,用于 处理 ARM 架构 中 的 中断, gpio-dwapb 是 Synopsys DesignWare 系列 中 的 一个 APB 协议 GPIO IP 核,它 兼容 “snps,dw-apb-gpio” 。 触发
方式 :电平触发( Level),边缘 触发( Edge)。 设备
名称 : hobot_gpio_key
实验现象
当cat /proc/interrupts 中断
FAQ
Makefile 编译路径丢失
报错TOPDIR 或 KERN_DIR 路径,或
解决
Makefile 编译版本报错
报错asm/compiler.h

解决
export PATH=/opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/:$PATH;
export ARCH=arm64;export TOOLCHAIN_PATH=/opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/;
export CROSS_COMPILE=aarch64-none-linux-gnu-;
报错
/bin/sh: 1: /home/gegugu/dev_x5_v1.10/out/build/kernel/scripts/sign-file: not found
make: *** [Makefile:9: all] Error 127
解决bd.sh boot menuconfig 进入 Module signature verification 选项
-> Enable loadable module support
-> Module signature verification
[*] Module signature verification
加载驱动报错
报错
解决