正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-17讲 定时器按键消抖
前言:
本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。
引用:
正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com
《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》
正点原子资料下载中心 — 正点原子资料下载中心 1.0.0 文档
正文:
本文是 “正点原子[第二期]Linux之ARM(MX6U)裸机篇--第17 讲” 的读书笔记。第17讲主要是介绍I.MX6U处理器的EPIT定时器的按键消抖。本节将参考正点原子的视频教程第17讲和配套的正点原子开发指南文档进行学习。
0. 概述
在第15章和第17章实验都使用到了按键,用到按键就要处理因为机械结构带来的抖动问题,也就是按键消抖。前面的时延中都是直接使用了延时函数来实现消抖,因为简单,但是直接使用延时函数来实现消抖会浪费CPU的性能,因为在延时函数里面CPU什么都做不了。如果按键使用中断的话更不能再中断里面使用延时函数,因为中断服务函数要快进快出!本章我们学习如何使用定时器来实现按键消抖,使用定时器既可以实现按键消抖,而且也不会浪费CPU性能,这个也是Linux驱动里面按键消抖的做法。
1. 定时器按键消抖简介
按键消抖的原理已经在第十五章详细的讲解过了,起始就是在按键按下以后延时一段时间再去读取按键值,如果此时按键值还有效那就表示这是一次有效的按键,中间的延时就是消抖的。
但是这有一个缺点,就是已按时函数会浪费CPU性能,应为延时函数就是空跑。如果按键是用中断方式实现的,那就更不应该在中断服务函数里使用延时函数,因为中断服务函数最基本的要求就是快进快出!上一章我们学习了EPIT定时器,定时器设置好定时时间,然后CPU就可以做其他事情去了,定时时间到了以后就会触发中断,然后在中断中做相应的处理即可。
因此,我们可以借助定时器来实现消抖,
- 按键采用中断驱动的方式,当按下按键触发按键中断,在按键中断中开启一个定时器,
- 定时周期为10ms,当定时时间到了以后就会触发定时器中断
- 最后在定时器中断处理函数中读取按键的值,如果按键还是按下的状态那就表示这是一次有效的按键。
定时器按键消抖如下图所示:

在图19.1.1中的t1~t3这一段时间就是按键抖动,是需要消除的。设置按键为下降沿触发,因此会在t1,t2,和t3这三个时刻触发那件中断,每次进入中断处理函数都会重开定时器中断,所以会在t1,t2,和t3这三个时刻开定时器中断。但是t1~t2和t2~t3这两段时间是小于我们设置的定时器中断周期(也就是消抖时间,比如10ms),所以虽然t1开启了定时器,但是定时器时间没有到呢t2时刻就重置了定时器,最终之后t3时刻开启的定时器能完整的完成整个定时周期并触发中断,我们就可以在中断处理函数里面做按键处理了,这就是定时器完成按键消抖的原理,Linux里面的按键驱动用的就是这个原理!
关于定时器消毒的原理就介绍到这里,接下来讲解如何使用EPIT1来配置按键KEY来实现具体的消抖,步骤如下:
- 配置按键IO中断
配置按键所使用的IO,因为要使用到中断驱动按键,所以要配置IO的中断模式。- 初始化消抖用的定时器
上面已经讲的很清楚了,消抖要用定时器来完成,所以需要初始化一个定时器,这是使用上一章讲解的EPIT1定时器,也算是对EPIT1定时器的一次巩固。定时器的定时周期为10ms,也可以根据实际情况调整定时周期。- 编写中断处理函数
需要编写两个中断处理器函数:按键对应的GPIO中断处理函数和EPIT1定时器的中断处理函数。在按键的中断处理函数中主要用于开启EPIT1定时器,EPIT1定时器处理函数才是重点,按键要做的具体任务都是在定时器EPIT1的中断处理函数中完成的,比如控制蜂鸣器打开或关闭。
2. 定时器按键消抖程序编写
更具上面分析的定时器按键消抖的原理和定时器按键消抖实验的步骤,编写定时器按键消抖程序源码如下:
bsp/keyfilter/bsp_keyfilter.h
#ifndef __BSP_KEYFILTER_H__
#define __BSP_KEYFILTER_H__#include "imx6u.h"void keyfilter_init(void);
void keyfilter_timer_init(int value);
void keyfilter_timer_stop(void);
void keyfilter_timer_restart(int value);
void keyfilter_timer_irqhandler(IRQn_Type irq, void *userparam);
void gpio1_16_31_irqhandler(IRQn_Type irq, void *userparam);#endif
bsp/keyfilter/bsp_keyfilter.c
#include "bsp_keyfilter.h"
#include "bsp_beep.h"
#include "bsp_led.h"
#include "bsp_int.h"
#include "bsp_gpio.h"
#include "bsp_epittimer.h"void keyfilter_init(void)
{/* GPIO1_IO18 */gpio_pin_config_t config;/* 1. 初始化IO复用,复用为GPIO1_IO18 */IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);/* 2. 设置 UART1_CTS_B IO 的电气特性 */IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xf080);/* 3. 初始化 GPIO1_IO18 设置为输入 */config.directioin = kGPIO_DigitalInput;config.intMode = kGPIO_FalllingEdgeInt;gpio_init(GPIO1, 18, &config);/* 启用GIC IRQ */GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);/* 注册IRQ处理函数 */system_irqhandler_register(GPIO1_Combined_16_31_IRQn, gpio1_16_31_irqhandler, NULL);/* EPIT1定时器初始化 */keyfilter_timer_init(66000000/100);/* 启用gpio中断 */gpio_int_enable(GPIO1, 18);
}void keyfilter_timer_init(int value){EPIT1->CR = 0;EPIT1->CR = (1 << 24 )| (1 << 3) | (1 << 2) | (1 << 1);EPIT1->LR = value;EPIT1->CMPR = 0;/* 使能GIC EPIT1_IRQn 中断 */GIC_EnableIRQ(EPIT1_IRQn);/* 注册中断处理函数 */system_irqhandler_register(EPIT1_IRQn, keyfilter_timer_irqhandler, NULL);
}void keyfilter_timer_stop(void)
{EPIT1->CR &= ~(1 << 0);
}void keyfilter_timer_restart(int value){EPIT1->CR &= ~(1 << 0); /* 关闭EPIT1 */EPIT1->LR = value; /* EPIT1加载值寄存器 */EPIT1->CR |= (1 << 0); /* 打开EPIT1 */
}void keyfilter_timer_irqhandler(IRQn_Type irq, void *userparam){static int beep_state = 0;if(EPIT1->SR & (1 << 0)){ /* 检查EPIT1中断标志 */keyfilter_timer_stop(); /* 关闭EPIT1定时器 */if(gpio_pinread(GPIO1, 18) == 0) /* 检查gpio引脚电平值 */{beep_state = !beep_state; /* 翻转蜂鸣器 */beep_switch(beep_state);}}/* 清除中断标志位 */EPIT1->SR |= (1<<0);
}void gpio1_16_31_irqhandler(IRQn_Type irq, void *userparam){if(GPIO1->ISR & (1 << 18)){keyfilter_timer_restart(66000000/100); /* 重启EPIT1定时器,定时器周期10ms */}/* 清除中断标志位 */gpio_int_cleanFlag(GPIO1, 18);
}
在如上的源码中,初始化按键KEY0 对应GPIO1_IO18的 IO 复用,IO特性,启用IO中断,并注册GPIO1_IO18的中断处理函数,在按键中断处理函数中重启 EPIT1定时器设置定时周期为10ms,当定时周期完成时触发EPIT1定时器比较事件中断,在EPIT1定时器中断里再次检查gpio引脚的输入电平如果还是有效说明此次按键按下是有效的,此时在EPIT1定时器中断里翻转蜂鸣器的鸣叫。
3. 编译烧写SD卡验证实验结果
译修改主频后源码烧录SD卡验证本节的EPIT定时器消抖实验是否生效。预期烧录SD卡后正点原子I.MX6ULL ALPHA/Mini 开发板后,按下按键蜂鸣器鸣叫,再次按下按键蜂鸣器停止鸣叫,多次测试按键按下都能翻转蜂鸣器开关。
我本地验证的结果是EPIT定时器按键消抖实验结果正常,多次按下按键都能正确的翻转蜂鸣器鸣叫开关。

4. 总结和实验遇到的问题记录
4.1 问题1:EPIT定时器消抖实验程序烧录SD,发现有时按下并松开按键后蜂鸣器只鸣叫一声就停止,预期应该一直鸣叫。
原因分析如下:

- 由于按键的机械结构,不仅仅在按键按下的瞬间有电平的多次抖动,从而在按键按下的瞬间多次在按键gpio电平的下降沿触发GPIO中断,进而重置EPIT1定时器最终出发EPIT1定时器中断。
- 按键的机械结构决定了,在按键松开的瞬间也有多次的gpio引脚电平抖动,也会在按键松开的瞬间由于抖动在电平的下降沿触发GPIO中断,进而重置EPIT1定时器最终出发EPIT1定时器中断。
- 由上分析可知,由于按键的机械结构,在按键按下的时会有电平抖动从而触发GPIO中断;在按键松开的时同样也会有电平抖动从而触发GPIO中断;
所以,必须在EPIT1定时器中断里再次检查 GPIO 引脚的电平是否有效,对于本实验,在按键按下的时候EPIT1中断处理函数读取到的gpio引脚为低电平证明按键按下有效。
(EPIT1定时中断处理函数里读取到gpio引脚为高电平,说明是按键松开。)

5. 结束
本文至此结束
相关文章:
正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-17讲 定时器按键消抖
前言: 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…...
【系统架构师】-论文考点整理
1、软件架构风格 1.1、概述 1、软件架构为软件系统提供了一个结构、行为和属性的高级抽象。 2、软件架构风格是特定应用领域的惯用模式,架构定义一个词汇表和一组约束。 1.2、作用 1、软件架构是项目干系人进行交流的手段。 2、软件架构是可传递和可复用的模型&…...
Android Activity 设计详解
文章目录 Android Activity 设计说明1. Activity 的生命周期2. Activity 的启动模式3. Activity 的通信4. Activity 的布局和视图管理5. Activity 的配置变化处理6. Activity 的保存和恢复状态7. Activity 的任务和返回栈 总结 Android Activity 设计说明 在 Android 中&#…...
国家开放大学,javaScript程序设计-形考任务-实训五:设计登录和注册页|实训六:设计简单的购物车
实训五:设计登录和注册页 1. 题目 设计登录和注册页。 2. 目的 (1)掌握表单域的引用方法。 (2)掌握常用控件的基本方法。 (3)掌握事件的处理方法。 (4)理解Cookie…...
微服务可用性之隔离
摘要 本文主要微服务场景下服务的可用性保障之隔离。隔离又分为几种情况,动静隔离、读写隔离、热点隔离、资源隔离等场景。 为什么要隔离 本质上是对资源进行分割确保在出现故障的时候服务只是部分不可用,不至于系统陷入整体性瘫痪,…...
设计模式——概述
1.设计模式定义 设计模式是软件设计中常见问题的典型解决方案,可用于解决代码中反复出现的设计问题。设计模式的出现可以让我们站在前人的肩膀上,通过一些成熟的设计方案来指导新项目的开发和设计,以便于我们开发出具有更好的灵活性和可扩展性&#…...
#P0564. 数组元素查找升级版
问题描述 给你 n 个数,再给你一个数 k,查找 k 在这 n 个数中第一次出现的位置(从 0 开始计数),不存在输出 No。 输入 多组测试数据,对于每组测试数据: 第一行输入一个整数 n (1 ≤ n ≤ 100…...
如何修改WordPress网站的域名
我的网站用的是Hostease的虚拟主机,但是域名是之前在其他平台买的,而且已经快到期了,因为主机和域名在不同的平台上,管理不太方便,所以我又在Hostease重新注册了一个域名,然后把网站换成了新的域名…...
python爬虫[简易版]
python爬数据[简易版] 对于每个网站的爬的原理基本是一样的,但是具体的代码写法的区别就在于爬的数据中解析出想要的数据格式: 以爬取有道词典中的图片为例: 第一步:打开网站,分析图片的数据源来自哪里, https://dict-subsidiary.youdao.com/home/content?invalid&pre…...
128天的创意之旅:从初心到成就,我的博客创作纪念日回顾
文章目录 🚀机缘:初心的种子——回望创作之旅的启航🌈收获:成长的果实——128天创作之旅的宝贵馈赠❤️日常:创作与生活的交织👊成就:代码的艺术🚲憧憬:未来的蓝图 &…...
前端绘制流程节点数据
根据数据结构和节点的层级、子节点id,前端自己绘制节点位置和关联关系、指向、已完成节点等 <template><div><div>通过后端节点和层级,绘制出节点以及关联关系等</div><div class"container" ref"container&…...
2024年顶级算法-黑翅鸢优化算法(BKA)-详细原理(附matlab代码)
黑翅鸢是一种上半身蓝灰色,下半身白色的小型鸟类。它们的显著特征包括迁徙和捕食行为。它们以小型哺乳动物、爬行动物、鸟类和昆虫为食,具有很强的悬停能力,能够取得非凡的狩猎成功。受其狩猎技能和迁徙习惯的启发,该算法作者建立…...
Linux 内核开发 28 内核模块文件ko文件介绍
Linux 内核开发 28 内核模块文件ko文件介绍 1. ELF格式简介 内核模块文件ko文件,格式为elf格式, ELF(Executable and Linkable Format)可执行链接格式,是一种用于存储可执行程序、目标代码、共享库和内核模块的标准文件…...
DDR5—新手入门学习(一)【1-5】
目录 1、DDR背景 (1)SDR SDRAM时代 : (2)DDR SDRAM的创新 : (3)DDR技术的演进 : (4)需求推动: 2、了解内存 (1&…...
力扣HOT100 - 138. 随机链表的复制
解题思路: class Solution {public Node copyRandomList(Node head) {if(headnull) return null;Node p head;//第一步,在每个原节点后面创建一个新节点//1->1->2->2->3->3while(p!null) {Node newNode new Node(p.val);newNode.next …...
深入分析 Android Activity (五)
深入分析 Android Activity (五) 1. Activity 的进程和线程模型 在 Android 中,Activity 默认在主线程(也称为 UI 线程)中运行。理解进程和线程模型对于开发响应迅速且无阻塞的应用程序至关重要。 1.1 主线程与 UI 操作 所有 UI 操作必须…...
Kubernetes 应用滚动更新
Kubernetes 应用版本号 在 Kubernetes 里,版本更新使用的不是 API 对象,而是两个命令:kubectl apply 和 kubectl rollout,当然它们也要搭配部署应用所需要的 Deployment、DaemonSet 等 YAML 文件。 在 Kubernetes 里应用都是以 …...
五分钟”手撕“图书管理系统
前言: 图书馆管理系统需要结合JavaSE的绝大部分知识,是一个很好的训练项目。 为了让大家更加方便的查阅与学习,我把代码放开头,供大家查询。 还有对代码的分析,我将以类为单位分开讲解。 目录 全部代码 Main类 Us…...
8个实用网站和软件,收藏起来一定不后悔~
整理了8个日常生活中经常能用得到的网站和软件,收藏起来一定不会后悔~ 1.ZLibrary zh.zlibrary-be.se/这个网站收录了超千万的书籍和文章资源,国内外的各种电子书资源都可以在这里搜索,98%以上都可以在网站内找到,并且支持免费下…...
电商内卷时代,视频号小店凭借一己之力“脱颖而出”
大家好,我是电商笨笨熊 今年618各大电商平台花样百出; 某宝更是直接取消了“预售”,从5月就开始进入618预热期; 不少玩家既开心又难过,市场如此内卷,618确实是个爆发期,但更多的需要不断压低…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
