正点原子[第二期]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确实是个爆发期,但更多的需要不断压低…...

模块缝合-把A模块换成B模块(没写完)
把MLP Head替换为KAN 1.在model文件下新建一个python文件 2.把 模块文件里的整个KAN代码复制到新的python文件中 3.在开头导入 from model.KAN(新建文件名) import KAN(新建文件中的类名) 4.sys.path.append(r"D: Icode(Kansformer"…...
Spring Boot + Thymeleaf 防重复提交
在 Spring Boot 与 Thymeleaf 结合的 Web 应用中,防止重复提交可以采用token 机制 客户端禁用按钮的方式实现,在高并发场景下,考虑使用 Redis 存储 token 而非 Session。 第一步:后端实现 Controller public class FormControl…...

算法专题七:分治
快排 1.颜色分类 题目链接:75. 颜色分类 - 力扣(LeetCode) class Solution {public void swap(int[] nums, int i, int j){int t = nums[i];nums[i] = nums[j];nums[j] = t;}public void sortColors(int[] nums) {int left=-1 ,i=0 ,right=nums.length;while(i<right){i…...

Redis实战-消息队列篇
前言: 讲讲做消息队列遇到的问题。 今日所学: 异步优化消息队列基于stream实现异步下单 1. 异步优化 1.1 需求分析 1.1.1 现有下单流程: 1.查询优惠劵 2.判断是否是秒杀时间,库存是否充足 3.实现一人一单 在这个功能中&…...

在本地电脑中部署阿里 Qwen3 大模型及连接到 Elasticsearch
在今天的文章中,我将参考文章 “使用 Elastic 和 LM Studio 的 Herding Llama 3.1” 来部署 Qwen3 大模型。据测评,这是一个非常不错的大模型。我们今天尝试使用 LM Studio 来对它进行部署,并详细描述如何结合 Elasticsearch 来对它进行使用。…...

软件功能测试报告都包含哪些内容?
软件功能测试报告是软件开发生命周期中的重要文档,主要涵盖以下关键内容: 1.测试概况:概述测试目标、范围和方法,确保读者对测试背景有清晰了解。 2.测试环境:详细描述测试所用的硬件、软件环境,确保…...

基于安卓的文件管理器程序开发研究源码数据库文档
摘 要 伴随着现代科技的发展潮流,移动互联网技术快速发展,各种基于通信技术的移动终端设备做的也越来越好了,现代智能手机大量的进入到了我们的生活中。电子产品的各种软硬技术技术的发展,操作系统的不断更新换代,谷歌…...

(LeetCode 动态规划(基础版))96. 不同的二叉搜索树 (递推 || 递归)
题目:96. 不同的二叉搜索树 思路:二叉树长度为n时,枚举每个点u作为根节点root,那么root左边的数构成左子树种数left,root右边的数构成右子树种数right,那么当前u为根节点下,二叉树的种数为left*…...

力扣面试150题--课程表
Day 63 题目描述 做法 初次思路:本质就是将所有前置课程和后置课程作为一个有向图(前者指向后者),判断这个图是否是一个有向无环图(即是否存在拓扑排序)(本质做法是dfs) 做法&…...

Python60日基础学习打卡Day46
一、 什么是注意力 注意力机制的由来本质是从onehot-elmo-selfattention-encoder-bert这就是一条不断提取特征的路。各有各的特点,也可以说由弱到强。 其中注意力机制是一种让模型学会「选择性关注重要信息」的特征提取器,就像人类视觉会自动忽略背景&…...