news 2026/7/1 9:32:25

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
学习笔记——按键驱动代码

按键驱动代码

一、基本按键驱动 (key.c)

1. 驱动框架概述

这是一个基于平台驱动框架的按键驱动程序,采用GPIO子系统杂项设备架构。

2. 关键代码分析

(1) 文件操作结构定义
static struct file_operations fops = { .owner = THIS_MODULE, .open = open, .read = read, .write = write, .release = close };
  • owner:模块所有者,固定为THIS_MODULE

  • open:设备打开时的处理函数

  • 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); }

关键步骤说明

  1. 设备树节点查找:查找路径为/ptkey的设备树节点

  2. GPIO获取:从设备树中获取名为ptkey-gpio的GPIO编号

  3. GPIO申请:申请使用该GPIO引脚,命名为 "key"

  4. 方向设置:配置为输入模式(按键是输入设备)

(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); }

执行流程

  1. 打开/dev/key设备文件

  2. 循环读取按键状态

  3. 打印状态信息

  4. 关闭设备

二、中断版本按键驱动 (key_irq.ckey_irq1.ckey_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; // 中断已处理 }

中断处理流程

  1. 参数验证:检查是否是本设备的中断

  2. 设置条件:将condition设为1,表示中断已发生

  3. 唤醒等待:唤醒在等待队列上睡眠的进程

  4. 返回状态:返回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为真时被唤醒

    • 可被信号中断

执行流程

  1. 重置条件变量 (condition = 0)

  2. 进入等待状态,直到中断发生(condition被设为1)

  3. 被唤醒后,返回按键状态给用户空间

3. 中断注册的三种方式

方式1:key_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:下降沿触发(按键通常按下时为低电平)

方式2:key_irq_sub.c

c

// 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);

工作机制

  1. 应用程序调用read()时,如果没有按键事件,进程进入睡眠

  2. 按键按下触发中断,中断处理函数设置condition = 1并唤醒进程

  3. 进程被唤醒,读取按键状态并返回给应用程序

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>; };

总结

  1. 基本按键驱动:通过轮询方式读取GPIO状态,实现简单但效率低

  2. 中断按键驱动:通过中断机制响应按键事件,效率高且实时性好

  3. 等待队列:实现进程的阻塞和唤醒,是中断驱动中的关键机制

  4. 两种中断获取方式gpio_to_irq()irq_of_parse_and_map()

  5. 应用程序:统一通过文件操作接口(open/read/close)与驱动交互

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/30 21:51:28

优化大数据批处理的网络传输效率

大数据批处理网络传输优化实战&#xff1a;从“卡脖子”到“飞起来”的5个关键技巧 标题选项 《大数据批处理网络优化指南&#xff1a;解决传输慢的5个可落地技巧》《搞定大数据传输瓶颈&#xff01;批处理场景下的网络效率提升实战》《大数据工程师必看&#xff1a;让批处理任…

作者头像 李华
网站建设 2026/7/1 16:01:23

基于C#实现逐点插入法生成Delaunay三角网

一、核心算法实现&#xff08;DelaunayTriangulator.cs&#xff09; using System; using System.Collections.Generic; using UnityEngine;public class DelaunayTriangulator {public struct Triangle{public Vector2 A, B, C;public Vector2 CircumCenter;public float Circ…

作者头像 李华
网站建设 2026/7/1 18:47:04

jsp大学生助学贷款管理系统46g32--程序+源码+数据库+调试部署+开发环境

本系统&#xff08;程序源码数据库调试部署开发环境&#xff09;带论文文档1万字以上&#xff0c;文末可获取&#xff0c;系统界面在最后面。系统程序文件列表学生,银行,助学贷款,贷款申请,通知公告开题报告内容一、选题背景随着高等教育的普及和学费的不断上涨&#xff0c;许多…

作者头像 李华
网站建设 2026/7/1 20:01:13

基于SpringBoot的物流信息管理系统毕业设计

博主介绍&#xff1a;✌ 专注于Java,python,✌关注✌私信我✌具体的问题&#xff0c;我会尽力帮助你。 一、研究目的 本研究旨在设计并实现一个基于SpringBoot框架的物流信息管理系统&#xff0c;以满足现代物流企业对高效、智能化信息管理系统的需求。具体研究目的如下&…

作者头像 李华