学习笔记——按键驱动代码

📅 发布时间:2026/7/8 4:44:23 👁️ 浏览次数:
学习笔记——按键驱动代码
按键驱动代码一、基本按键驱动 (key.c)1. 驱动框架概述这是一个基于平台驱动框架的按键驱动程序采用GPIO子系统和杂项设备架构。2. 关键代码分析(1) 文件操作结构定义static struct file_operations fops { .owner THIS_MODULE, .open open, .read read, .write write, .release close };owner模块所有者固定为THIS_MODULEopen设备打开时的处理函数read从设备读取数据获取按键状态write向设备写入数据按键驱动通常不需要release设备关闭时的处理函数(2) 获取按键状态函数static inline int get_key_status(void) { return gpio_get_value(key_gpio); }inline内联函数编译器会直接将函数体插入调用处减少函数调用开销gpio_get_value()读取GPIO引脚的电平状态返回值0表示低电平1表示高电平具体取决于按键硬件连接(3) 读取函数实现static ssize_t read(struct file * file, char __user * buf, size_t size, loff_t * loff) { int ret 0; int status get_key_status(); // 获取按键状态 ret copy_to_user(buf, status, 4); // 将状态复制到用户空间 printk(key read\n); return ret; }流程读取GPIO状态 → 通过copy_to_user()传递给应用程序copy_to_user()内核空间到用户空间的数据复制返回值成功复制到用户空间的字节数(4) 平台驱动探测函数 (probe)static int probe(struct platform_device * pdev) { // 1. 注册杂项设备 int ret misc_register(misc_dev); // 2. 查找设备树节点 pdts of_find_node_by_path(/ptkey); // 3. 获取GPIO编号 key_gpio of_get_named_gpio(pdts, ptkey-gpio, 0); // 4. 申请GPIO ret gpio_request(key_gpio, key); // 5. 配置GPIO为输入模式 gpio_direction_input(key_gpio); }关键步骤说明设备树节点查找查找路径为/ptkey的设备树节点GPIO获取从设备树中获取名为ptkey-gpio的GPIO编号GPIO申请申请使用该GPIO引脚命名为 key方向设置配置为输入模式按键是输入设备(5) 驱动匹配表static struct of_device_id key_table[] { {.compatible pt-key}, {} };compatible与设备树节点中的compatible属性匹配匹配流程text设备树节点compatible pt-key ↓ 驱动匹配表compatible pt-key ↓ 匹配成功 → 执行probe函数3. 应用程序 (key_app.c)int main(int argc, const char *argv[]) { // 1. 打开设备 int fd open(/dev/key, O_RDWR); // 2. 循环读取按键状态 while(1) { int ret read(fd, status, sizeof status); printf(ret %d status %d\n, ret, status); } // 3. 关闭设备 close(fd); }执行流程打开/dev/key设备文件循环读取按键状态打印状态信息关闭设备二、中断版本按键驱动 (key_irq.c、key_irq1.c、key_irq_sub.c)1. 与基本版本的主要区别基本版本采用轮询方式读取按键状态而中断版本采用中断方式响应按键事件。2. 中断处理核心机制(1) 中断相关数据结构static wait_queue_head_t wq; // 等待队列头 static int condition; // 条件变量表示中断是否发生 static int key_irq; // 中断号(2) 中断处理函数static irqreturn_t key_irq_handler(int irq, void * dev) { int arg *(int *)dev; if(100 ! arg) // 验证设备参数 return IRQ_NONE; // 不是本设备的中断 condition 1; // 设置条件为真 wake_up_interruptible(wq); // 唤醒等待队列 printk(irq %d dev %d\n, irq, arg); return IRQ_HANDLED; // 中断已处理 }中断处理流程参数验证检查是否是本设备的中断设置条件将condition设为1表示中断已发生唤醒等待唤醒在等待队列上睡眠的进程返回状态返回IRQ_HANDLED表示中断已处理返回值说明IRQ_HANDLED中断已成功处理IRQ_NONE不是本设备的中断或者中断处理失败(3) 阻塞式读取函数static ssize_t read(struct file * file, char __user * buf, size_t size, loff_t * loff) { int ret 0; int status 0; printk(key read\n); condition 0; // 重置条件 wait_event_interruptible(wq, condition); // 等待条件成立中断发生 status 1; // 按键按下 ret copy_to_user(buf, status, sizeof(status)); return sizeof(status); }关键函数wait_event_interruptible(wq, condition)使当前进程进入睡眠状态当condition为真时被唤醒可被信号中断执行流程重置条件变量 (condition 0)进入等待状态直到中断发生condition被设为1被唤醒后返回按键状态给用户空间3. 中断注册的三种方式方式1key_irq.c/key_irq1.c// 1. 获取GPIO对应的中断号 key_irq gpio_to_irq(key_gpio); // 2. 申请中断 ret request_irq(key_irq, key_irq_handler, IRQF_TRIGGER_FALLING, key0_irq, arg);特点gpio_to_irq()将GPIO编号转换为中断号IRQF_TRIGGER_FALLING下降沿触发按键通常按下时为低电平方式2key_irq_sub.cc// 1. 从设备树解析中断号 key_irq irq_of_parse_and_map(pdts, 0); // 2. 申请中断 ret request_irq(key_irq, key_irq_handler, IRQF_TRIGGER_FALLING, key0_irq, arg);特点irq_of_parse_and_map()从设备树节点解析中断号和映射设备树中需要定义中断相关属性三种版本对比文件中断获取方式特点key_irq.cgpio_to_irq()需要先获取GPIO编号key_irq1.cgpio_to_irq()同key_irq.c代码基本一致key_irq_sub.cirq_of_parse_and_map()直接从设备树获取中断信息4. 中断申请参数详解request_irq(key_irq, // 中断号 key_irq_handler, // 中断处理函数 IRQF_TRIGGER_FALLING, // 触发方式 key0_irq, // 设备名出现在/proc/interrupts arg); // 传递给中断处理函数的设备参数触发方式标志IRQF_TRIGGER_RISING上升沿触发IRQF_TRIGGER_FALLING下降沿触发IRQF_TRIGGER_HIGH高电平触发IRQF_TRIGGER_LOW低电平触发5. 等待队列机制(1) 初始化等待队列init_waitqueue_head(wq); // 在probe函数中初始化(2) 等待和唤醒函数// 等待条件成立可被信号中断 wait_event_interruptible(wq, condition); // 唤醒等待队列中断处理函数中调用 wake_up_interruptible(wq);工作机制应用程序调用read()时如果没有按键事件进程进入睡眠按键按下触发中断中断处理函数设置condition 1并唤醒进程进程被唤醒读取按键状态并返回给应用程序6. 资源清理 (remove函数)static int remove(struct platform_device * pdev) { disable_irq(key_irq); // 禁用中断 free_irq(key_irq, arg); // 释放中断 gpio_free(key_gpio); // 释放GPIO misc_deregister(misc_dev); // 注销杂项设备 }清理顺序与初始化顺序相反确保安全释放资源三、两种模式的对比轮询模式 (key.c)特点说明实现方式应用程序主动读取GPIO状态CPU占用高需要不断循环读取响应速度取决于读取频率适用场景简单应用不频繁按键中断模式 (key_irq*.c)特点说明实现方式GPIO中断触发进程阻塞等待CPU占用低进程睡眠不占用CPU响应速度实时响应微秒级适用场景需要实时响应的应用四、设备树配置示例轮询模式设备树ptkey { compatible pt-key; ptkey-gpio gpio1 5 GPIO_ACTIVE_LOW; // GPIO1第5脚低电平有效 };中断模式设备树ptkey { compatible pt-key; ptkey-gpio gpio1 5 GPIO_ACTIVE_LOW; interrupts GIC_SPI 66 IRQ_TYPE_EDGE_FALLING; // 中断配置 interrupt-parent gpio1; };总结基本按键驱动通过轮询方式读取GPIO状态实现简单但效率低中断按键驱动通过中断机制响应按键事件效率高且实时性好等待队列实现进程的阻塞和唤醒是中断驱动中的关键机制两种中断获取方式gpio_to_irq()和irq_of_parse_and_map()应用程序统一通过文件操作接口open/read/close与驱动交互