在Linux中,通过GPIO子系统设置引脚复用、配置输出模式并读取值需分两步:pinctrl配置物理引脚功能 + GPIO子系统逻辑操作。以下是详细实现:
一、内核驱动实现
1. 设备树配置(pinctrl)
// arch/arm/boot/dts/your-board.dts
my_device {
compatible = "mycompany,mydevice";
pinctrl-names = "default";
pinctrl-0 = <&my_gpio_pin>;
gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>; // 引用gpiochip1的17号引脚
status = "okay";
};
// 定义GPIO引脚配置
my_gpio_pin: my_gpio_pin {
pins = "GPIO17";
function = "gpio"; // 复用为GPIO功能
bias-disable; // 禁用上拉/下拉(可选)
};
2. 内核模块代码
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
static int gpio_num;
static int __init my_gpio_init(void) {
int ret;
struct device_node *np;
// 从设备树获取GPIO编号
np = of_find_compatible_node(NULL, NULL, "mycompany,mydevice");
if (!np) {
printk(KERN_ERR "Failed to find device node\n");
return -ENODEV;
}
gpio_num = of_get_gpio(np, 0); // 获取第一个GPIO
if (!gpio_is_valid(gpio_num)) {
printk(KERN_ERR "Invalid GPIO\n");
of_node_put(np);
return -EINVAL;
}
// 申请GPIO
ret = gpio_request(gpio_num, "my_gpio");
if (ret) {
printk(KERN_ERR "Failed to request GPIO%d\n", gpio_num);
of_node_put(np);
return ret;
}
// 设置为输出模式(初始值0)
ret = gpio_direction_output(gpio_num, 0);
if (ret) {
printk(KERN_ERR "Failed to set direction\n");
gpio_free(gpio_num);
of_node_put(np);
return ret;
}
// 读取当前值(输出模式返回设置的电平)
int value = gpio_get_value(gpio_num);
printk(KERN_INFO "GPIO%d output value: %d\n", gpio_num, value);
of_node_put(np);
return 0;
}
static void __exit my_gpio_exit(void) {
gpio_set_value(gpio_num, 0); // 确保退出时为低电平
gpio_free(gpio_num);
}
module_init(my_gpio_init);
module_exit(my_gpio_exit);
MODULE_LICENSE("GPL");
二、用户空间实现(libgpiod)
1. 安装库
sudo apt-get install libgpiod-dev
2. C程序示例
#include <gpiod.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
struct gpiod_chip *chip;
struct gpiod_line *line;
int value;
// 打开GPIO芯片(如gpiochip0)
chip = gpiod_chip_open_by_name("gpiochip0");
if (!chip) {
perror("Failed to open GPIO chip");
return 1;
}
// 获取GPIO线(17号引脚)
line = gpiod_chip_get_line(chip, 17);
if (!line) {
perror("Failed to get GPIO line");
gpiod_chip_close(chip);
return 1;
}
// 申请输出模式,初始值0
int ret = gpiod_line_request_output(line, "user_gpio", 0);
if (ret) {
perror("Failed to request output");
gpiod_line_release(line);
gpiod_chip_close(chip);
return 1;
}
// 读取当前值(返回设置的输出值)
value = gpiod_line_get_value(line);
printf("GPIO17 output value: %d\n", value);
// 清理资源
gpiod_line_release(line);
gpiod_chip_close(chip);
return 0;
}
三、关键说明
- 引脚复用
- 通过pinctrl在设备树中配置引脚为GPIO功能(
function = "gpio"
)。 - 确保驱动或用户空间程序引用的GPIO编号与硬件匹配。
- 通过pinctrl在设备树中配置引脚为GPIO功能(
- 输出模式与读取
gpio_direction_output()
或gpiod_line_request_output()
设置输出模式。- 读取操作(
gpio_get_value()
或gpiod_line_get_value()
)返回的是输出寄存器的值,而非实际引脚电平。如需检测外部电平,需临时切换为输入模式:gpio_direction_input(gpio_num); // 内核空间 // 或 gpiod_line_set_direction(line, GPIOD_LINE_DIRECTION_INPUT); // 用户空间
- 权限与兼容性
- 用户空间程序需
root
权限或配置udev
规则。 - GPIO编号可能因平台而异(如树莓派使用BCM编号,需通过
gpiod
工具转换)。
- 用户空间程序需
四、验证步骤
-
编译并加载内核模块:
make -C /lib/modules/$(uname -r)/build M=$PWD modules sudo insmod my_gpio.ko dmesg | tail # 查看日志
-
运行用户空间程序:
sudo ./gpio_read
-
使用工具验证:
gpiodetect # 查看GPIO控制器 gpioinfo gpiochip0 # 查看GPIO17详细信息
通过上述步骤,您可以在Linux系统中安全地配置GPIO引脚并读取其值。