新闻详情

新闻详情

首页 / 资讯中心 / 详情

Linux内核驱动开发——gpio子系统

发布时间:2026/9/28 21:13:00来源:尧图网络
Linux内核驱动开发——gpio子系统
platform 平台驱动 设备树静态节点 动态加载驱动核心概念静态设备树设备节点写在 DTS 里内核启动阶段就解析动态加载驱动驱动编译成.koinsmod/rmmod手动加载 / 卸载不是内置进内核 zImage 匹配逻辑内核启动扫描 DTS发现test_gpioled静态节点保存等到 insmod 加载 platform 驱动模块驱动注册 platform_driver内核拿驱动里of_match_table和 DTS 节点compatible比对匹配成功 → 自动调用probermmod 卸载驱动调用remove。1.编写 DTS 静态设备节点compatible gpio-led1;核心匹配属性字符串用来和驱动里.compatible数组做匹配pinctrl-names default;引脚控制状态名字default代表默认上电状态pinctrl-0 pinctrl_gpioled;pinctrl-0对应上面pinctrl-names第 0 项default。pinctrl_gpioled是引用 pinctrl 子节点这个节点会配置 GPIO1_IO3 引脚功能复用为普通 GPIO、设置电气属性上下拉、速率。led-gpio gpio1 3 GPIO_ACTIVE_HIGH;自定义属性描述 LED 使用的 GPIO驱动代码中用of_get_named_gpio读取这三个参数gpio1引用 gpio1 控制器3gpio1 组里面第 3 号引脚GPIO1_IO03GPIO_ACTIVE_HIGH高电平点亮 LED低电平熄灭 如果是GPIO_ACTIVE_LOW就是低电平点亮。2.platform 驱动代码编写驱动匹配设备树内节点并注册设备和驱动程序static int probe(struct platform_device * pdev) { int ret 0; /*查找设备节点通过设备树节点完整路径找到 /test_gpioled 节点*/ struct device_node * dev_node of_find_node_by_path(/test_gpioled); if(NULL dev_node) { printk(of_find_node_by_path not found /test_gpioled\n); return -1; } /*从设备树节点属性led-gpio索引0获取GPIO编号*/ gpio_led of_get_named_gpio(dev_node,led-gpio,0); /*申请该GPIO名字为led1占用该GPIO资源防止其他驱动复用*/ gpio_request(gpio_led,led1); /*设置GPIO为输出模式默认输出高电平(LED熄灭低电平点亮)*/ gpio_direction_output(gpio_led,1); /*注册misc杂项设备生成/dev/xxx设备文件供用户空间访问*/ ret misc_register(misc); if(ret 0) goto err_register; printk(led_init ********************\n); return ret; err_register: /*注册失败反注册misc设备这里逻辑有隐患后面说明*/ misc_deregister(misc); printk(led_init failed********************\n); return ret; } /*设备树匹配列表用于设备树compatible属性和驱动匹配*/ const struct of_device_id match_table[] { [0] {.compatible gpio-led1}, //匹配dts中compatible gpio-led1的设备节点 [1] {.compatible pt-led2}, //匹配dts中compatible pt-led2的设备节点 [2] {.compatible pt-led3}, //匹配dts中compatible pt-led3的设备节点 {} //of_device_id数组必须以空结构体结尾作为结束标记 }; /*platform平台驱动结构体*/ static struct platform_driver drv { //设备和驱动成功匹配后内核自动调用probe探测函数 .probe probe, //设备卸载/驱动移除时内核调用remove函数 .remove remove, .driver { .name DEV_NAME, //驱动名字用于传统方式匹配非设备树匹配 .of_match_table match_table //指定设备树匹配表优先使用compatible匹配 } }; /*驱动入口函数模块加载时执行*/ static int __init led_driver_init(void) { /*向内核注册platform平台驱动*/ int ret platform_driver_register(drv); if(ret 0) { //注册失败反注册驱动 platform_driver_unregister(drv); return ret; } printk(platform_driver_register ...\n); return ret; }3.修改Makefile动态编译生成.ko文件修改当前目录下Kconfig使Makefile生效修改根目录下的menuconfig增加makefile编译依赖 obj-$(CONFIG_LED1_GPIO) led1_gpio.o 添加menuconfig选项 config LED1_GPIO tristate LED1_GPIO help this is a LED1 demo testGPIO 子系统是什么GPIO 子系统是 Linux 内核封装好的统一操作 GPIO 的框架屏蔽了芯片寄存器差异i.MX6ULL、STM32、RK3588 寄存器不一样。驱动不需要直接读写寄存器调用内核提供的 GPIO API 即可。配合设备树使用在 DTS 节点描述 GPIO 信息驱动通过 of 函数读取做到硬件信息和驱动代码分离换硬件不用改驱动源码。通过上面一部分已经完成了DTS设备树的修改所以这里只需要在驱动程序中修改就可以了主要使用到一下几个API函数1. of_get_named_gpio()gpio_led of_get_named_gpio(dev_node,led-gpio,0);功能从设备树节点读取 GPIO 编号参数dev_node设备树节点指针propnameDTS 里属性名led-gpioindex同一个属性多个 gpio 时的索引0 取第一个 返回大于等于 0 合法 gpio 号负数读取失败2. gpio_request()gpio_request(gpio_led,led1);功能向内核申请占用该 GPIO 资源防止多个驱动抢同一个引脚 参数gpio 编号gpio 名字用于/sys/kernel/debug/gpio查看 返回0 成功负数失败引脚被占用3. gpio_direction_output()gpio_direction_output(gpio_led,1);设置 GPIO 为输出模式第二个参数是初始电平1输出高0输出低4. gpio_direction_input(gpio)设置 GPIO 为输入模式5. gpio_set_value(gpio, value)动态修改 GPIO 电平value0/1。probe 里面只做初始化应用层 ioctl 调用这个来点灯灭灯6. gpio_get_value(gpio)读取 GPIO 当前电平输入引脚常用7. gpio_free(gpio_led)8.ioctl是字符设备 /misc 设备的控制接口用户空间传命令 (cmd) 参数内核驱动解析 cmd执行点灯、灭灯、读状态这类操作。open/write/read 适合数据流ioctl 适合控制命令LED 开关最常用。函数原型如下static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { // cmd用户传来的命令号 // arg用户传的参数可以是数值、指针 switch(cmd) { case LED_ON: gpio_set_value(gpio_led, 0); break; case LED_OFF: gpio_set_value(gpio_led, 1); break; default: return -EINVAL; // 无效命令 } return 0; }挂载到file_operationsstatic struct file_operations led_fops { .owner THIS_MODULE, .unlocked_ioctl led_ioctl, };其中需要用到cmd参数一般需要通过拼接得到为了避免cmd命令重叠以及误操作设备ioctl命令是由四个字段拼接而成的。bit31~bit30bit29~bit16bit15~bit8bit7~bit0方向数据长度魔数 (幻数)序号内核提供 4 个宏来自动拼装 cmd不要自己手动写数字// 无参数命令 #define _IO(type, nr) // 用户 → 内核写数据(IOW: Input to kernel) #define _IOW(type, nr, size) // 内核 → 用户读数据(IOR: Output to user) #define _IOR(type, nr, size) // 双向读写 #define _IOWR(type, nr, size)这里就是简单的操作LED所以只拼接设备类型和一个简单的命令/*定义设备类型*/ #define MAGIC_NUM X /*定义命令*/ #define LED_ON 0 #define LED_OFF 1 /*拼接命令*/ #define CMD_LED_ON _IO(MAGIC_NUM,LED_ON) #define CMD_LED_OFF _IO(MAGIC_NUM,LED_OFF)修改驱动匹配执行函数static int probe(struct platform_device * pdev) { int ret 0; /*查找设备节点硬编码路径查找/test_gpioled设备树节点*/ struct device_node * dev_node of_find_node_by_path(/test_gpioled); if(NULL dev_node) { printk(of_find_node_by_path not found /test_gpioled\n); return -1; } /*从设备树节点读取名为led-gpio属性中第0个gpio信息得到内核全局GPIO编号*/ gpio_led of_get_named_gpio(dev_node,led-gpio,0); /*向内核申请对应编号的GPIO资源命名为led1防止其他驱动占用该引脚*/ gpio_request(gpio_led,led1); /*设置GPIO为输出模式初始输出高电平LED默认熄灭假设低电平点亮*/ gpio_direction_output(gpio_led,1); /*注册misc杂项设备自动在/dev下创建设备文件供应用层ioctl操作*/ ret misc_register(misc); if(ret 0) goto err_register; printk(led_init ********************\n); return ret; err_register: /*misc注册失败注销杂项设备*/ misc_deregister(misc); printk(led_init failed********************\n); return ret; }最后修改用户应用层代码#include stdio.h #include fcntl.h #include unistd.h #include sys/ioctl.h #define MAGIC_NUM X #define LED_ON 0 #define LED_OFF 1 #define CMD_LED_ON _IO(MAGIC_NUM,LED_ON) #define CMD_LED_OFF _IO(MAGIC_NUM,LED_OFF) int main(int argc, const char *argv[]) { /*打开驱动生成的设备文件/dev/led1读写方式*/ int fd open(/dev/led1,O_RDWR); if(fd 0) { perror(open led1); return -1; // 打开失败退出 } while(1) { /*发送CMD_LED_ON命令驱动ioctl回调收到命令点亮LED第三个参数arg这里无用传0*/ ioctl(fd,CMD_LED_ON,0); sleep(1); //保持点亮1秒 /*发送CMD_LED_OFF命令驱动ioctl回调收到命令熄灭LED*/ ioctl(fd,CMD_LED_OFF,0); sleep(1); //保持熄灭1秒 } close(fd); return 0; }
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

手机本地部署大模型实战:从模型量化到Android/iOS推理优化 2026/9/28 21:57:35

手机本地部署大模型实战:从模型量化到Android/iOS推理优化

1. 手机跑大模型这件事,到底靠不靠谱先说结论:能跑,但别指望它替代云端服务。我前后在骁龙8 Gen 2的Android机和iPhone 15 Pro上折腾了差不多两个月,从最初的“这玩意儿真能跑?”到后来把本地模型接进自己的笔记工作流…

阅读更多 →
Agent-Native架构重构实战:设计原理、最小实现与避坑指南 2026/9/28 21:57:28

Agent-Native架构重构实战:设计原理、最小实现与避坑指南

这两年我经手了不少LLM项目,一个感受越来越明显:大多数团队口中的“AI化”,不过是在传统系统外面套了一层会说话的前端。2024年下半年我在做一个客服知识库系统,最初就是标准的RAG加聊天窗口,用户在右上角点开机器人&a…

阅读更多 →
Python电商评论情感分析全流程实战:从数据采集到模型训练 2026/9/28 21:57:28

Python电商评论情感分析全流程实战:从数据采集到模型训练

简介:基于Python的电商买家评论情感分析项目包,专为毕业设计、期末大作业和课程设计场景打造,代码注释详尽,即使完全没有项目经验的新手也能看懂每一步实现,曾获98分且深受导师认可。整个压缩包约54MB,内含…

阅读更多 →
Substrate区块链开发框架详解:从理解核心架构到动手搭建自定义链 2026/9/28 21:56:58

Substrate区块链开发框架详解:从理解核心架构到动手搭建自定义链

1. substrate到底是什么:从一张实验台布说起很多刚接触区块链底层开发的朋友,看到"substrate"这个词都会愣一下——这到底是个框架、一个库、还是一条链?我第一次接触它的时候也绕了不少弯路,这里先给大家一个最直白的说…

阅读更多 →
S500无人机新手入门:Pixhawk4与FS-IA6B对码接线及飞控配置全攻略 2026/9/28 21:56:58

S500无人机新手入门:Pixhawk4与FS-IA6B对码接线及飞控配置全攻略

1. 为什么S500这套配置值得新手拿来练手S500机架配Pixhawk4飞控再加FS-IA6B接收机,这个组合在入门级四轴里算是相当经典的搭配。S500的轴距500mm,机架空间足够大,装起来不憋屈,炸机了维修成本也低。Pixhawk4作为一款成熟的开源飞控…

阅读更多 →
JSP+MySQL在线音乐管理系统:从数据库设计到部署全解析 2026/9/28 21:56:51

JSP+MySQL在线音乐管理系统:从数据库设计到部署全解析

简介:一个基于 JSP 技术栈开发的在线音乐信息管理系统完整项目,采用 Java Web JSP MySQL JavaScript 实现,适合正在学习 Java Web 开发、需要课程设计或毕业设计参考的学生。系统区分管理员与普通用户两类角色:前台支持歌曲查询…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉