Linux GPIO驱动开发实战:从传统接口到新式gpiod

张开发
2026/4/8 23:41:39 15 分钟阅读

分享文章

Linux GPIO驱动开发实战:从传统接口到新式gpiod
一、顶级架构一句话总结设备树(gpio描述) → GPIO子系统 → gpiod接口 → 硬件引脚控制GPIO是嵌入式开发中最基础的外设接口Linux提供了新旧两套API新式gpiod接口更安全、更简洁。二、GPIO子系统架构架构层次┌─────────────────────────────────────────────────────────┐ │ GPIO子系统架构 │ ├─────────────────────────────────────────────────────────┤ │ │ │ 用户空间 /sys/class/gpio/ 或 /dev/gpiochipX │ │ ↓ │ │ GPIO核心层 gpiolib.c (统一抽象层) │ │ ↓ │ │ GPIO控制器 gpio_chip (厂商驱动) │ │ ↓ │ │ 硬件层 GPIO寄存器操作 │ │ │ └─────────────────────────────────────────────────────────┘GPIO控制器结构structgpio_chip{constchar*label;// 控制器名称structgpio_device*gpiodev;// GPIO设备structdevice*parent;// 父设备structmodule*owner;// 所属模块int(*request)(structgpio_chip*chip,unsignedoffset);void(*free)(structgpio_chip*chip,unsignedoffset);int(*get_direction)(structgpio_chip*chip,unsignedoffset);int(*direction_input)(structgpio_chip*chip,unsignedoffset);int(*direction_output)(structgpio_chip*chip,unsignedoffset,intvalue);int(*get)(structgpio_chip*chip,unsignedoffset);void(*set)(structgpio_chip*chip,unsignedoffset,intvalue);// ...};三、新旧API对比接口对比表功能旧接口(整数型)新接口(描述符型)获取GPIOgpio_request()gpiod_get()释放GPIOgpio_free()gpiod_put()设置方向gpio_direction_input/output()gpiod_direction_input/output()读写值gpio_get_value() / gpio_set_value()gpiod_get_value() / gpiod_set_value()获取中断号gpio_to_irq()gpiod_to_irq()为什么推荐新接口特性旧接口新接口类型安全无整数有结构体指针方向管理手动自动错误处理返回值返回值ERR_PTR设备树集成需手动解析自动解析资源管理手动释放devm自动释放四、新式gpiod接口详解1. 头文件#includelinux/gpio/consumer.h#includelinux/gpio/driver.h2. 获取GPIO描述符// 从设备树获取单个GPIOstructgpio_desc*gpiod_get(structdevice*dev,constchar*con_id,enumgpiod_flagsflags);// 获取多个GPIOstructgpio_descs*gpiod_get_array(structdevice*dev,constchar*con_id,enumgpiod_flagsflags);// 获取索引GPIOstructgpio_desc*gpiod_get_index(structdevice*dev,constchar*con_id,unsignedintidx,enumgpiod_flagsflags);// 可选获取不存在不报错structgpio_desc*gpiod_get_optional(structdevice*dev,constchar*con_id,enumgpiod_flagsflags);3. 标志位enumgpiod_flags{GPIOD_ASIS0,// 不改变当前状态GPIOD_INGPIOD_FLAGS_BIT_DIR_SET,// 输入GPIOD_OUT_LOWGPIOD_FLAGS_BIT_DIR_SET|// 输出低GPIOD_FLAGS_BIT_DIR_OUT|GPIOD_FLAGS_BIT_DIR_VAL,GPIOD_OUT_HIGHGPIOD_FLAGS_BIT_DIR_SET|// 输出高GPIOD_FLAGS_BIT_DIR_OUT,};4. 释放GPIO// 释放单个GPIOvoidgpiod_put(structgpio_desc*desc);// 释放多个GPIOvoidgpiod_put_array(structgpio_descs*descs);// devm自动释放版本structgpio_desc*devm_gpiod_get(structdevice*dev,constchar*con_id,enumgpiod_flagsflags);voiddevm_gpiod_put(structdevice*dev,structgpio_desc*desc);5. 方向设置// 设置为输入intgpiod_direction_input(structgpio_desc*desc);// 设置为输出intgpiod_direction_output(structgpio_desc*desc,intvalue);// 设置为输出原始值不考虑active-lowintgpiod_direction_output_raw(structgpio_desc*desc,intvalue);6. 读写操作// 读取值考虑active-lowintgpiod_get_value(conststructgpio_desc*desc);// 读取原始值不考虑active-lowintgpiod_get_value_cansleep(conststructgpio_desc*desc);intgpiod_get_raw_value(conststructgpio_desc*desc);// 设置值考虑active-lowvoidgpiod_set_value(structgpio_desc*desc,intvalue);// 设置原始值voidgpiod_set_raw_value(structgpio_desc*desc,intvalue);// 设置多个GPIOvoidgpiod_set_array_value(unsignedintarray_size,structgpio_desc**desc_array,int*value_array);7. 获取中断号intgpiod_to_irq(conststructgpio_desc*desc);五、设备树配置GPIO设备树节点/ { my_device { compatible mycompany,mydevice; // 单个GPIO led-gpio gpio1 21 GPIO_ACTIVE_LOW; // 多个GPIO gpios gpio1 21 GPIO_ACTIVE_LOW, gpio1 22 GPIO_ACTIVE_HIGH, gpio2 10 GPIO_ACTIVE_LOW; // 带名称的GPIO reset-gpios gpio1 5 GPIO_ACTIVE_LOW; enable-gpios gpio1 6 GPIO_ACTIVE_HIGH; }; };GPIO标志说明标志说明GPIO_ACTIVE_HIGH高电平有效GPIO_ACTIVE_LOW低电平有效GPIO_OPEN_DRAIN开漏输出GPIO_OPEN_SOURCE开源输出GPIO_PULL_UP内部上拉GPIO_PULL_DOWN内部下拉六、完整驱动示例LED控制驱动#includelinux/module.h#includelinux/platform_device.h#includelinux/gpio/consumer.h#includelinux/of.h#includelinux/delay.hstructled_data{structgpio_desc*led_gpio;constchar*name;};staticintled_probe(structplatform_device*pdev){structled_data*data;structdevice*devpdev-dev;intret;datadevm_kzalloc(dev,sizeof(*data),GFP_KERNEL);if(!data)return-ENOMEM;// 获取GPIO自动设置为输出高电平data-led_gpiodevm_gpiod_get(dev,led,GPIOD_OUT_HIGH);if(IS_ERR(data-led_gpio)){dev_err(dev,Failed to get LED GPIO\n);returnPTR_ERR(data-led_gpio);}// 获取名称of_property_read_string(dev-of_node,label,data-name);platform_set_drvdata(pdev,data);dev_info(dev,LED %s initialized\n,data-name);return0;}staticintled_remove(structplatform_device*pdev){structled_data*dataplatform_get_drvdata(pdev);// 关闭LEDgpiod_set_value(data-led_gpio,0);dev_info(pdev-dev,LED removed\n);return0;}// LED闪烁函数staticvoidled_blink(structled_data*data,inttimes){inti;for(i0;itimes;i){gpiod_set_value(data-led_gpio,1);msleep(500);gpiod_set_value(data-led_gpio,0);msleep(500);}}staticconststructof_device_idled_of_match[]{{.compatiblemycompany,led,},{}};MODULE_DEVICE_TABLE(of,led_of_match);staticstructplatform_driverled_driver{.probeled_probe,.removeled_remove,.driver{.nameled_driver,.of_match_tableled_of_match,},};module_platform_driver(led_driver);MODULE_LICENSE(GPL);MODULE_AUTHOR(Driver Developer);按键输入驱动#includelinux/module.h#includelinux/platform_device.h#includelinux/gpio/consumer.h#includelinux/interrupt.h#includelinux/of.h#includelinux/input.hstructbutton_data{structgpio_desc*button_gpio;intirq;structinput_dev*input;};staticirqreturn_tbutton_irq_handler(intirq,void*dev_id){structbutton_data*datadev_id;intstate;// 读取按键状态stategpiod_get_value(data-button_gpio);// 上报按键事件input_report_key(data-input,KEY_ENTER,!state);input_sync(data-input);pr_info(Button %s\n,state?released:pressed);returnIRQ_HANDLED;}staticintbutton_probe(structplatform_device*pdev){structbutton_data*data;structdevice*devpdev-dev;intret;datadevm_kzalloc(dev,sizeof(*data),GFP_KERNEL);if(!data)return-ENOMEM;// 获取GPIO设置为输入data-button_gpiodevm_gpiod_get(dev,button,GPIOD_IN);if(IS_ERR(data-button_gpio))returnPTR_ERR(data-button_gpio);// 获取中断号data-irqgpiod_to_irq(data-button_gpio);if(data-irq0)returndata-irq;// 申请输入设备data-inputdevm_input_allocate_device(dev);if(!data-input)return-ENOMEM;data-input-namemy-button;input_set_capability(data-input,EV_KEY,KEY_ENTER);retinput_register_device(data-input);if(ret)returnret;// 注册中断retdevm_request_irq(dev,data-irq,button_irq_handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,button_irq,data);if(ret)returnret;platform_set_drvdata(pdev,data);dev_info(dev,Button initialized\n);return0;}staticconststructof_device_idbutton_of_match[]{{.compatiblemycompany,button,},{}};MODULE_DEVICE_TABLE(of,button_of_match);staticstructplatform_driverbutton_driver{.probebutton_probe,.driver{.namebutton_driver,.of_match_tablebutton_of_match,},};module_platform_driver(button_driver);MODULE_LICENSE(GPL);七、用户空间GPIO操作sysfs接口旧方式# 导出GPIOecho21/sys/class/gpio/export# 设置方向echoout/sys/class/gpio/gpio21/directionechoin/sys/class/gpio/gpio21/direction# 读写值echo1/sys/class/gpio/gpio21/valuecat/sys/class/gpio/gpio21/value# 释放GPIOecho21/sys/class/gpio/unexportlibgpiod工具新方式# 安装工具aptinstallgpiod-tools# 查看GPIO控制器gpiodetect# 查看GPIO状态gpioinfo gpiochip0# 设置GPIO值gpioset gpiochip0211# 读取GPIO值gpioget gpiochip021# 监控GPIO事件gpiomon gpiochip021八、常见问题与解决方案问题原因解决方案gpiod_get返回错误设备树配置错误检查gpio-*属性名和引用GPIO无法控制方向未设置或权限问题使用正确的flags参数中断不触发触发类型错误检查IRQF_TRIGGER_*标志值读取不正确active-low配置检查设备树GPIO_ACTIVE_*九、终极总结GPIO驱动 嵌入式开发的基础技能新接口优势类型安全、自动管理、设备树集成核心APIgpiod_get/put、gpiod_direction_*、gpiod_set/get_value设备树配置使用gpio-*属性描述GPIO最佳实践优先使用devm_前缀的资源管理API掌握GPIO驱动就掌握了嵌入式开发的基础

更多文章