news 2026/6/11 9:30:52

嵌入式Linux驱动:GPIO 按键驱动 - 我们终于要从输出走向输入了

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入式Linux驱动:GPIO 按键驱动 - 我们终于要从输出走向输入了

嵌入式Linux驱动:GPIO 按键驱动 - 我们终于要从输出走向输入了

仓库已经开源!所有教程,主线内核移植,跑新版本imx-linux/uboot都在这里,或者一起来尝试跑7.0的Linux!欢迎各位大佬观摩!喜欢的话点个⭐!

仓库地址:https://github.com/Awesome-Embedded-Learning-Studio/imx-forge

静态网页:https://awesome-embedded-learning-studio.github.io/imx-forge/

前面几章我们都在折腾输出设备——LED、蜂鸣器,这些有个共同点:CPU 控制它们,想让它们干嘛就干嘛。说干就干,写个寄存器,LED 就亮了;再写一次,蜂鸣器就叫了。这种掌控感确实让人上瘾。

但现实世界不全是输出设备。我们还需要处理输入设备——按键、传感器、触摸屏,这些设备不听 CPU 的指挥,反过来是它们告诉 CPU 发生了什么。这种角色的转换一开始让人有点不适应,我承认刚开始学的时候确实折腾了好几天。

输入和输出的本质区别

输出设备和输入设备的区别说起来简单,但理解透彻不容易:

/* 输出设备:CPU 写 GPIO */writel(GPIO_DR,val);// LED 亮了/* 输入设备:CPU 读 GPIO */u32 val=readl(GPIO_DR);// 按键状态

区别就在这一读一写。输出是 CPU 主动的,输入是 CPU 被动的。输出什么时候发生由代码决定,输入什么时候发生由用户决定(用户什么时候按键谁知道)。

这个"谁说了算"的问题,直接影响了驱动的设计。

按键驱动要解决的问题

第一次写按键驱动的时候,我以为很简单。不就是读个 GPIO 吗?能有多复杂。结果现实给了我一记响亮的耳光,坑真的不少。

第一个问题是:**怎么知道按键被按下了?**你说我读 GPIO 状态吧,什么时候读?一直读?那 CPU 不就空转了吗?间隔着读?那按键刚好在两次读取之间按下怎么办?

第二个问题是:按键会抖动。机械按键的触点在接触瞬间会抖动。你按一下,GPIO 电平可能会跳变好几次:

理想波形(以为): ┌─────────────────── ────┘ └───── 实际波形(真实): ┌─┬───┬─────┬──────── ────┘ │ │ │ └── └─┘ └─┬─┬─┘ └─┘ ←──── 5-50ms ────→

我第一次测试驱动的时候,按一下按键,应用程序居然收到好多次事件。查了半天才发现是这个抖动问题。真的,这种硬件特性只有踩过坑才会印象深刻。

第三个问题是:CPU 占用的问题

如果你用轮询方式——就是不停地读 GPIO 状态——CPU 占用率会很高。这不像 LED,设置一下就完事了,按键驱动需要持续监控。

我们先学轮询方式

这些问题的终极解决方案是用中断。但中断有自己的复杂度,需要理解中断系统、中断处理函数、下半部机制这些概念。

所以我们决定分两步走:先学轮询方式,再学中断方式。

轮询方式虽然效率低,但有几个好处:

  1. 简单直接——代码逻辑一目了然,没有隐藏的魔法
  2. 容易调试——出问题了直接看循环里的状态就行
  3. 建立概念——先理解"等待事件"的概念,再学中断会轻松很多

说实话,我觉得如果一开始就学中断,很容易迷失在各种机制里。先跑通轮询,建立信心,再学中断,这条路更平滑。

按键的硬件连接

我们的 Alpha 开发板上有一个按键,连接方式是经典的低电平触发:

+3.3V | < > 10kΩ 上拉电阻 < | +---- GPIO1_IO18 | 按键开关 | GND

这个电路的工作原理:

  • 按键松开时,上拉电阻把 GPIO 拉到高电平(3.3V)
  • 按键按下时,开关导通,GPIO 被拉到地(0V)

这是个设计选择问题。上拉的好处是功耗低——按键松开时几乎没有电流。下拉的话,按键按下时会有电流从 VCC 流到 GND。对于电池供电的设备,这个差异可能很重要。

另外,很多传统设计习惯用上拉,可能历史原因多一些。

设备树配置

设备树里这样描述这个按键:

imxaes_key_gpio: key-gpio { compatible = "imxaes-key-gpio"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_key>; gpios = <&gpio1 18 GPIO_ACTIVE_LOW>; status = "okay"; };

这里的GPIO_ACTIVE_LOW很关键。它告诉内核:这个按键是低电平触发的。当按键按下时,GPIO 物理上是低电平,但逻辑上应该解释为"按键按下"(1)。

这个反转逻辑在驱动层会自动处理,我们写代码的时候不用管。
好了,背景介绍差不多了。下一节我们开始看代码,先从 GPIO 输入的机制说起。

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

LangChain 快速入门:从 Hello World 到第一个 Chain

LangChain 快速入门:从 Hello World 到第一个 Chain 目录 前言 技术背景与演进逻辑 2.1 第一阶段:裸 API 调用的蛮荒时代 2.2 第二阶段:Provider SDK 的局部改善 2.3 第三阶段:应用框架的统一抽象 核心原理深度解析 3.1 LangChain 四层架构全景图 3.2 Runnable 协议:框架…

作者头像 李华
网站建设 2026/6/11 9:24:40

三分钟上手:用SMU调试工具彻底掌控你的AMD Ryzen系统

三分钟上手&#xff1a;用SMU调试工具彻底掌控你的AMD Ryzen系统 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://git…

作者头像 李华
网站建设 2026/6/11 9:24:28

Linux 网络栈调优与 TCP 拥塞控制:从默认参数到生产级优化

Linux 网络栈调优与 TCP 拥塞控制&#xff1a;从默认参数到生产级优化一、网络性能的默认配置困境&#xff1a;Linux 内核参数的保守策略 Linux 内核的网络参数默认值面向通用场景设计&#xff0c;对高并发、低延迟的生产环境而言过于保守。一个典型的例子&#xff1a;net.core…

作者头像 李华
网站建设 2026/6/11 9:24:24

Rust 异步编程:从 Future Trait 到手写一个简易 Runtime

Rust 异步编程&#xff1a;从 Future Trait 到手写一个简易 Runtime一、异步编程的"黑盒"困境&#xff1a;用 Tokio 但不懂为什么 Rust 的 async/await 语法看起来和 JavaScript、Python 一样简单——加个 async&#xff0c;写个 .await&#xff0c;但底层机制完全不…

作者头像 李华
网站建设 2026/6/11 9:24:05

为什么你的微信聊天记录需要专业管理?WeChatMsg终极数据归档指南

为什么你的微信聊天记录需要专业管理&#xff1f;WeChatMsg终极数据归档指南 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trendin…

作者头像 李华