看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动EPIT定时器中断《按键消抖》) 2023.5.10

news2024/11/16 18:06:37

前言

首先我们在使用开发板进行开发时,自然而然会使用到定时器这个外设,因为我们需要它来完成精准的定时功能,但是说到精准,我会在下一篇文章中使用其他的定时器来完成这个功能即GPT定时器。在本文章中我们会利用定时器中断来解决按键消抖功能,并且解决上一讲GPIO中断中的问题。

整体文件结构

在这里插入图片描述

源码分析(保姆级讲解)

带有消抖功能的按键初始化部分

void filterkey_init(void)
{	
	gpio_pin_config_t key_config;
	
	IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);	

	IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);

	key_config.direction = kGPIO_DigitalInput;
	key_config.interruptMode = kGPIO_IntFallingEdge;
	key_config.outputLogic = 1;
	gpio_init(GPIO1, 18, &key_config);

	GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);

	system_register_irqhandler(GPIO1_Combined_16_31_IRQn, 
							   (system_irq_handler_t)gpio1_16_31_irqhandler, 
							   NULL);
	
	gpio_enableint(GPIO1, 18);		

	filtertimer_init(66000000/100);	
}

好!按照老样子,接下来开始详细讲解每行代码的用处,以及为什么这样写!

gpio_pin_config_t key_config;

声明了一个gpio_pin_config_t 类型,并且名称为key_config。那我们可以看下这个结构体中声明了什么?

typedef struct _gpio_pin_config
{
    gpio_pin_direction_t direction; 		/* GPIO方向:输入还是输出 */
    uint8_t outputLogic;            		/* 如果是输出的话,默认输出电平 */
	gpio_interrupt_mode_t interruptMode;	/* 中断方式 */
} gpio_pin_config_t;

其中声明了三个变量,分别是directionoutputLogicinterruptMode

其中gpio_pin_direction_t 结构体如下所示:

typedef enum _gpio_pin_direction
{
    kGPIO_DigitalInput = 0U,  		/* 输入 */
    kGPIO_DigitalOutput = 1U, 		/* 输出 */
} gpio_pin_direction_t;

其中gpio_interrupt_mode_t 结构体如下所示:

typedef enum _gpio_interrupt_mode
{
    kGPIO_NoIntmode = 0U, 				/* 无中断功能 */
    kGPIO_IntLowLevel = 1U, 			/* 低电平触发	*/
    kGPIO_IntHighLevel = 2U, 			/* 高电平触发 */
    kGPIO_IntRisingEdge = 3U, 			/* 上升沿触发	*/
    kGPIO_IntFallingEdge = 4U, 			/* 下降沿触发 */
    kGPIO_IntRisingOrFallingEdge = 5U, 	/* 上升沿和下降沿都触发 */
} gpio_interrupt_mode_t;	
IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);	

初始化IO复用功能为用为GPIO1_IO18

IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);

配置GPIO1_IO18的IO属性 ,主要可设置功能如下,具体配置根据具体使用情况而定:

*bit 16:0 HYS关闭
*bit [15:14]: 11 默认22K上拉
*bit [13]: 1 pull功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 000 关闭输出
*bit [0]: 0 低转换率

key_config.direction = kGPIO_DigitalInput;

设置GPIO1_IO18方向为输入。

key_config.interruptMode = kGPIO_IntFallingEdge;

设置GPIO1_IO18为下降沿触发。

key_config.outputLogic = 1;

设置GPIO1_IO18初始电平为1,即高电平。

gpio_init(GPIO1, 18, &key_config);

因为要产生GPIO中断,所以需要配置中断号等其他设置。

GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn); 

使能GIC中对应的中断

system_register_irqhandler(GPIO1_Combined_16_31_IRQn, 
							   (system_irq_handler_t)gpio1_16_31_irqhandler, 
							   NULL);

注册中断服务函数,并且名称为gpio1_16_31_irqhandler,即产生GPIO中断后,会自动进入该中断服务函数。

gpio_enableint(GPIO1, 18);

使能GPIO1_IO18的中断功能

filtertimer_init(66000000/100);

初始化EPIT定时器,10ms

带有消抖功能的EPIT定时器初始化部分

void filtertimer_init(unsigned int value)
{
	EPIT1->CR = 0;
	
	EPIT1->CR = (1<<24 | 1<<3 | 1<<2 | 1<<1);

	EPIT1->LR = value;

	EPIT1->CMPR	= 0;	
	
	GIC_EnableIRQ(EPIT1_IRQn);	

	system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)filtertimer_irqhandler, NULL);	
}

好!按照老样子,接下来开始详细讲解每行代码的用处,以及为什么这样写!

EPIT1->CR = 0;	

//先清零

EPIT1->CR = (1<<24 | 1<<3 | 1<<2 | 1<<1);

该寄存器具体配置如下:

在这里插入图片描述
在讲为什么会这样配置该寄存器之前,我们先了解下EPIT定时器的工作原理。

在这里插入图片描述

由上图所示,我们分别说以下6点:

①、这是个多路选择器,用来选择 EPIT 定时器的时钟源,EPIT 共有 3 个时钟源可选择,ipg_clk、ipg_clk_32k 和 ipg_clk_highfreq

②、这是一个 12 位的分频器,负责对时钟源进行分频,12 位对应的值是 0~ 4095,对应着1~4096 分频

③、经过分频的时钟进入到 EPIT 内部,在 EPIT 内部有三个重要的寄存器:计数寄存器(EPIT_CNR)、加载寄存器(EPIT_LR)和比较寄存器(EPIT_CMPR),这三个寄存器都是 32 位的。

EPIT 是一个向下计数器,也就是说给它一个初值,它就会从这个给定的初值开始递减,直到减为 0,计数寄存器里面保存的就是当前的计数值。如果 EPIT 工作在 set-and-forget 模式下,当计数寄存器里面的值减少到 0EPIT 就会重新从加载寄存器读取数值到计数寄存器里面,重新开始向下计数。比较寄存器里面保存的数值用于和计数寄存器里面的计数值比较,如果相等的话就会产生一个比较事件。

④、比较器。

⑤、EPIT 可以设置引脚输出,如果设置了的话就会通过指定的引脚输出信号。

⑥、产生比较中断,也就是定时中断。

EPIT 定时器有两种工作模式:set-and-forgetfree-running,这两个工作模式的区别如下:

set-and-forget 模式:EPITx_CR(x=1,2)寄存器的 RLD 位置 1 的时候 EPIT 工作在此模式下,在此模式下 EPIT 的计数器从加载寄存器 EPITx_LR 中获取初始值,不能直接向计数器寄存器写入数据。不管什么时候,只要计数器计数到 0,那么就会从加载寄存器 EPITx_LR 中重新加载数据到计数器中,周而复始。

free-running 模式:EPITx_CR 寄存器的 RLD 位清零的时候 EPIT 工作在此模式下,当计数器计数到0以后会重新从0XFFFFFFFF开始计数,并不是从加载寄存器EPITx_LR中获取数据。

所以通过了解了上述功能后,我们来看下这行代码都做了些什么

EPIT1->CR = (1<<24 | 1<<3 | 1<<2 | 1<<1);
  1. 1<<1:当计数器在每次变为0之后,会从加载寄存器读取下一轮计数的初始值。
  2. 1<<2 :当为1时,代表使能比较中断,即当计数器的值现在和我们设定的比较值相等时,会触发定时器中断。
  3. 1<<3:为 1 的时候工作在 set-and-forget 模式,即会从会从加载寄存器读取下一轮计数的初始值。
  4. 1<<24:选择定时器的时钟源为Peripheral 时钟(ipg_clk),即66MHz。
EPIT1->LR = value;

EPIT1->LR 时加载寄存器。

EPIT1->CMPR	= 0;	

EPIT1->CMPR是比较值,意味着当加载寄存器从value减少到0之后,会触发定时器中断。

GIC_EnableIRQ(EPIT1_IRQn);	

使能GIC中对应的中断

system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)filtertimer_irqhandler, NULL);	

注册中断服务函数filtertimer_irqhandler

gpio中断服务函数部分

void gpio1_16_31_irqhandler(void)
{ 
	/* 开启定时器 */
	filtertimer_restart(66000000/100);

	/* 清除中断标志位 */
	gpio_clearintflags(GPIO1, 18);
}
void filtertimer_restart(unsigned int value)
{
	EPIT1->CR &= ~(1<<0);	/* 先关闭定时器 */
	EPIT1->LR = value;		/* 计数值 			*/
	EPIT1->CR |= (1<<0);	/* 打开定时器 		*/
}

按键消抖其实就是在按键按下以后延时一段时间再去读取按键值,如果此时按键值还有效那就表示这是一次有效的按键,中间的延时就是消抖的。因为中断服务函数最基本的要求就是快进快出!

当按键按下以后触发按键中断,在按键中断中开启一个定时器,定时周期为 10ms,当定时时间到了以后就会触发定时器中断,最后在定时器中断处理函数中读取按键的值,如果按键值还是按下状态那就表示这是一次有效的按键。

那我们如果想让其10ms触发一次定时器中断,我们应该设置多大的value

计算公式如下

Tout = ((frac +1 )* value) / Tclk;

其中:

Tclk:EPIT1 的输入时钟频率(单位 Hz)
Tout:EPIT1 的溢出时间(单位 S)。
frac:分频值,默认是0,代表是1分频

那么1ms = ((0+1)* 66000000/100)/66000000 = 1/100s=10ms

gpio_clearintflags(GPIO1, 18);

每次完成一次gpio中断响应后,我们需要手动清除中断标志位,方便下一次进入中断函数。

定时器中断服务函数部分

void filtertimer_irqhandler(void)
{ 
	static unsigned char state = OFF;

	if(EPIT1->SR & (1<<0)) 					/* 判断比较事件是否发生			*/
	{
		filtertimer_stop();					/* 关闭定时器 				*/
		if(gpio_pinread(GPIO1, 18) == 0)	/* KEY0 				*/
		{
			state = !state;
			beep_switch(state);				/* 反转蜂鸣器 				*/
		}
	}
		
	EPIT1->SR |= 1<<0; 						/* 清除中断标志位 				*/
}
if(EPIT1->SR & (1<<0)) 					/* 判断比较事件是否发生			*/
{
	filtertimer_stop();					/* 关闭定时器 				*/
	if(gpio_pinread(GPIO1, 18) == 0)	/* KEY0 				*/
	{
		state = !state;
		beep_switch(state);				/* 反转蜂鸣器 				*/
	}
}

此函数先读取 EPIT1_SR 寄存器,判断当前的中断是否为比较事件,如果是的话,并且此时gpio状态还是低电平状态,则代表此时按键按下,即我们将蜂鸣器翻转即可。

EPIT1->SR |= 1<<0; 						/* 清除中断标志位 				*/

每次完成一次定时器中断响应后,我们需要手动清除中断标志位,方便下一次进入中断函数。

while循环部分

while(1)			
	{	
		state = !state;
		led_switch(LED0, state);
		delay(500);
	}

每隔500msled灯亮灭。

最终编译验证

按下 KEY 就会控制蜂鸣器的开关,并且 LED0 不断的闪烁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/509761.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode2. 两数相加

写在前面&#xff1a; 题目链接&#xff1a;LeetCode2两数相加 编程语言&#xff1a;C 题目难度&#xff1a;中等 一、题目描述 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 …

光学液氮恒温器T9120-4W的技术参数

液氮型低温恒温器&#xff0c;利用液氮作为降温媒介&#xff0c;标准恒温器可实现快速降温至液氮温度&#xff08;约20min&#xff09;&#xff0c;其工作原理是在恒温器内部液氮腔内装入液氮&#xff0c;通过调整控温塞与冷指的间隙来保持冷指的漏热稳定在一定值上&#xff0c…

lua实战(1)

目录 IDELua中的名称Lua是一种区分大小写的语言 Lua 是一个小巧的脚本语言。它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo三人所组成的研究小组于1993年开…

vue3+electron开发桌面软件(9)——选中多个文件,右键上传

系列文章目录 系列第一篇&#xff1a; vue3electron开发桌面软件入门与实战&#xff08;0&#xff09;——创建electron应用 文章目录 系列文章目录前言一、我们如何思考二、解决问题1.选择方案2. 发现electron多开窗口监听3.查找可使用的官方参数4.示例代码 总结 前言 从本系…

Docker 安全及日志管理

Docker 安全及日志管理 Docker 容器与虚拟机的区别隔离与共享性能与损耗 Docker 存在的安全问题Docker 自身漏洞Docker 源码问题Docker 架构缺陷与安全机制Docker 安全基线标准 容器相关的常用安全配置方法容器最小化Docker 远程 API 访问控制重启 Docker在宿主机的 firewalld …

ASEMI代理ADI亚德诺ADXL345BCCZ-RL7车规级芯片

编辑-Z ADXL345BCCZ-RL7特点&#xff1a; 超低功率&#xff1a;在测量模式下低至23A 在VS2.5 V的待机模式下为0.1A&#xff08;典型&#xff09; 功耗会随带宽自动调整 用户可选分辨率 固定的10位分辨率 全分辨率&#xff0c;其中分辨率随着g范围的增加而增加&#xff0…

00后才是内卷之王,被卷的头皮发麻....

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;前段时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。…

Redis主从复制(搭建集群的一种方式)【故障转移,内存,回收】

做一个伪集群 配置文件&#xff1a; daemonize yes port 7777 logfile .redis-7777.log dir ./ bind 0.0.0.0启动6666 and 7777 现在设置主从表 但是有个问题我把服务器停掉 关系就会解除 还可以手动解除 slaveof no one 命令 配置Sentinel&#xff08;哨兵&#…

基于络达SOC AB1562A TWS蓝牙耳机设计

V hezkz17进数字音频答疑 一 原理框图 二 电子电路设计 (1)SOC主芯片 (2) 最小系统晶振电路设计26MHZ (3) 电池电路设计 4 充电电路与充电保护设计 5 LED输出电路设计</

hexo stellar设置目录跳转记录

1. 使用hexo-toc插件 一开始使用的是hexo-toc的插件&#xff1a;参考hexo安装toc插件 详细的可以看github的项目&#xff1a; github-hexo-toc 更加详细的配置&#xff1a; Hexo添加Toc支持&#xff0c;生成文章目录 2. 官网的方式&#xff08;推荐&#xff09; stellar博…

flink cdc原理与使用

flink cdc原理与使用 1 cdc 介绍1.1 cdc简介与对比1.2 基于日志的 CDC 方案介绍 2 基于 Flink SQL CDC 的数据同步方案实践2.1 案例 1 : Flink SQL CDC JDBC Connector2.2 案例 2 : CDC Streaming ETL2.3 案例 3 : Streaming Changes to Kafka 3 Flink SQL CDC 的更多应用场景…

Java EE企业级应用开发(SSM)第10章

第10章MyBatis核心配置及动态SQL 一.预习笔记 1.第九章的细节处理 1-1.mappers标签中的配置 1-2.jdbc属性文件的配置 1-3.包的别名配置 2.Mybatis核心配置文件 2-1&#xff1a;settings标签&#xff08;P145-146中的表10-1&#xff09; 2-2.类型别名 3.Mybatis映射文件 3-1…

项目集管理绩效领域

项目集管理绩效领域是对活动或职能相关领域的补充分组&#xff0c;这些活动或职能在项目集管理工作的 整个范围内&#xff0c;专门描述和区分一个绩效领域中的活动。 本章包括&#xff1a; 项目集管理绩效领域的定义项目集管理绩效领域的交互组织战略、项目组合管理和项目集管…

vue 水印组件

效果图展示 Watermark 参数说明类型默认值版本width水印的宽度&#xff0c;content 的默认值为自身的宽度number120height水印的高度&#xff0c;content 的默认值为自身的高度number64rotate水印绘制时&#xff0c;旋转的角度&#xff0c;单位 number-22zIndex追加的水印元素…

24.eslint

eslint是约束代码写法的插件&#xff0c;比如组件的命名必须要用驼峰命名这种 eslint官网 检测并修复 JavaScript 代码中的问题。 - ESLint - 插件化的 JavaScript 代码检查工具 目录 1 vue-cli的eslint 2 标准规则 2.1 不能连续出现两个空行 2.2 结尾必须有空行 2.3…

深入了解Dubbo SPI 工作机制——@Activate (5)

在上一篇Dubbo 基于xml文件分析主流程源码 &#xff08;4&#xff09;_chen_yao_kerr的博客-CSDN博客中, 我们已经初步了解了Dubbo SPI的 key - value 结构。接下来将会继续分享Dubbo SPI其他功能的使用方式&#xff0c;并且从源码的角度去一谈究竟。 Activate注解 参数名 …

【数据结构】链表OJ:力扣141.环形链表、142.环形链表II

今天要分享的关于链表的题目是环形链表 目录 题目141. 环形链表 - 力扣&#xff08;LeetCode&#xff09; 题解 关于快慢指针的深入研究 题目2&#xff1a;142. 环形链表 II - 力扣&#xff08;LeetCode&#xff09; 题解 以下是题目链接 141. 环形链表 - 力扣&#xff…

塑料回收---未来化工行业的新兴增长领域

大量的旧塑料被浪费 从南极洲到北极&#xff0c;在原始海岸线上冲刷的塑料废物&#xff0c;以及太平洋上巨大的塑料废物浮岛&#xff0c;得到了媒体的广泛报道&#xff0c;并促成了消费者消费意识发生转变。 研究表明&#xff0c;大多数废旧塑料被送往垃圾填埋场和焚烧&#…

Go语言设计模式之责任链模式

其实很多人不知道,责任链模式是我们工作中经常遇到的模式,特别是web后端工程师,我们工作中每时每刻都在用:因为市面上大部分的web框架的过滤器基本都是基于这个设计模式为基本模式搭建的。 1.模式介绍 我们先来看一下责任链模式(Chain Of Responsibility Design Pattern…

react实现点击获取json对象的jsonPath

准备 安装 react-json-view&#xff1a;npm install --save react-json-view 可参考的一些开源库&#xff1a;react-json-path-picker&#xff0c;json-path-picker 线上工具&#xff1a;jsonpath tool JsonPath JsonPath官方文档 用来解析多层嵌套的json数据。JsonPath 是一…