imx6ull-裸机学习实验10——定时器按键消抖实验

目录

前言

实验介绍

原理

实验步骤

实验资源

实验代码编写

keyfilter.h

keyfilter.c

函数filterkey_init

函数filtertimer_init

函数 filtertimer_stop

函数filtertimer_restart

函数filtertimer_irqhandler

函数 gpio1_16_31_irqhandler

main.c

前言

在学习实验6:按键输入实验 和学习实验9:EPIT定时器实验中,我们都用了按键输入的功能,用到按键就要处理因为机械结构带来的按键抖动问题,也就是按键消抖,但每一次按键消抖我们都用的软件延时函数,这对于CPU的性能来说无疑是很大的浪费。

本讲实验我们使用正点原子开发板imx6ull,跟着官方例程来学习如何使用定时器来实现按键消抖,使用定时器既可以实现按键消抖,而且也不会浪费CPU 性能,这个也是 Linux 驱动里面按键消抖的做法。

在学习实验9:EPIT定时器实验 中我们学习了 EPIT 定时器,已经详细地介绍了配置EPIT定时器地原理,这是一个最简单的定时器;这一讲我们也使用定时器 EPTI1来完成整个实验。

实验介绍

原理

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


由图可知: t1~t3 这一段时间就是按键抖动,是需要消除的。

设置按键为下降沿触发,因此会在 t1、 t2 和 t3 这三个时刻会触发按键中断,每次进入中断处理函数都会重新开器定时器中断,所以会在 t1、 t2 和 t3 这三个时刻开器定时器中断。

但是 t1~t2 和 t2~t3 这两个时间段是小于我们设置的定时器中断周期(也就是消抖时间,比如 10ms),所以虽然 t1 开启了定时器,但是定时器定时时间还没到呢 t2 时刻就重置了定时器,最终只有 t3 时刻开启的定时器能完整的完成整个定时周期并触发中断。

我们就可以在t3 时刻的中断处理函数里面做按键处理了,这就是定时器实现按键防抖的原理。

实验步骤

使用 EPIT1 来配合按键 KEY来实现具体的消抖,有3个步骤:

  1. 配置按键 IO 中断:配置按键所使用的 IO,使用中断模式。
  2. 初始化消抖用的定时器:EPIT1 定时器,定时周期为 10ms。
  3. 编写中断处理函数:按键对应的 GPIO 中断处理函数和 EPIT1 定时器的中断处理函数。
  •  按键的中断处理函数:主要用于开启 EPIT1 定时器,
  • EPIT1 的中断处理函数:完成按键要做的具体任务,比如控制蜂鸣器打开或关闭。

实验资源

本讲实验效果:按下 KEY 会打开蜂鸣器,再次按下 KEY 就会关闭蜂鸣器。 LED0 作为系统提示灯不断的闪烁。

需要的硬件资源:

  • 一个 LED 灯 LED0。
  • 定时器 EPTI1。
  • 一个按键 KEY。
  • 一个蜂鸣器。

具体的原理分析、外设驱动,不理解的朋友们可以去我主页看看之前的几篇实验。

实验代码编写

bsp 文件夹下创建名为“keyfilter”的文件夹,然后在 bsp/keyfilter 中新建 bsp_keyfilter.c 和 bsp_keyfilter.h 这两个文件。

keyfilter.h

keyfilter.h 文件很简单,只是函数声明。

#ifndef _BSP_KEYFILTER_H
#define _BSP_KEYFILTER_H


/* 函数声明 */
void filterkey_init(void);
void filtertimer_init(unsigned int value);
void filtertimer_stop(void);
void filtertimer_restart(unsigned int value);
void filtertimer_irqhandler(void);
void gpio1_16_31_irqhandler(void);

#endif

keyfilter.c

文件 bsp_keyfilter.c 一共有 6 个函数:

  • filterkey_init ( 按键初始化 )
  • filtertimer_init(定时器 EPIT1 的初始化函数)
  • filtertimer_stop(定时器EPIT1 关闭)
  • filtertimer_restart(定时器EPIT1 重启)
  • filtertimer_irqhandler(EPTI1 的中断处理函数)
  • gpio1_16_31_irqhandler(GPIO1_IO18 的中断处理函数)

函数filterkey_init

此函数首先初始化了 KEY 所使用的 UART1_CTS 这个 IO,设置这个 IO 的中断模式,并且注册中断处理函数,最后调用函数 filtertimer_init 初始化定时器 EPIT1 定时周期为10ms。

/*
 * @description		: 按键初始化
 * @param			: 无
 * @return 			: 无
 */
void filterkey_init(void)
{	
	gpio_pin_config_t key_config;
	
	/* 1、初始化IO复用 */
	IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);	/* 复用为GPIO1_IO18 */

	/* 2、、配置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 低转换率
	 */
	IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);
	
	/* 3、初始化GPIO为中断 */
	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); /* 使能GIC中对应的中断   		  */
	
	/* 注册中断服务函数 */
	system_register_irqhandler(GPIO1_Combined_16_31_IRQn, 
							   (system_irq_handler_t)gpio1_16_31_irqhandler, 
							   NULL);
	
	gpio_enableint(GPIO1, 18);		/* 使能GPIO1_IO18的中断功能 */

	filtertimer_init(66000000/100);	/* 初始化定时器,10ms */
}

函数filtertimer_init

函数 filtertimer_init 是定时器 EPIT1 的初始化函数,内容基本和学习实验9中的 EPIT1 初始化函数一样。

/*
 * @description		: 初始化用于消抖的定时器,默认关闭定时器
 * @param - value	: 定时器EPIT计数值
 * @return 			: 无
 */
void filtertimer_init(unsigned int value)
{
	EPIT1->CR = 0;	//先清零
	
	/*
     * CR寄存器:
     * bit25:24 01 时钟源选择Peripheral clock=66MHz
     * bit15:4  0  1分频
     * bit3:	1  当计数器到0的话从LR重新加载数值
     * bit2:	1  比较中断使能
     * bit1:    1  初始计数值来源于LR寄存器值
     * bit0:    0  先关闭EPIT1
     */
	EPIT1->CR = (1<<24 | 1<<3 | 1<<2 | 1<<1);

	/* 计数值    */
	EPIT1->LR = value;
	
	/* 比较寄存器,当计数器值和此寄存器值相等的话就会产生中断 */
	EPIT1->CMPR	= 0;	
	
	GIC_EnableIRQ(EPIT1_IRQn);	/* 使能GIC中对应的中断 */
	
	/* 注册中断服务函数		    */
	system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)filtertimer_irqhandler, NULL);	
}

函数 filtertimer_stop

函数 filtertimer_stop是定时器EPIT1 的关闭函数。

/*
 * @description		: 关闭定时器
 * @param 			: 无
 * @return 			: 无
 */
void filtertimer_stop(void)
{
	EPIT1->CR &= ~(1<<0);	/* 关闭定时器 */
}

函数filtertimer_restart

函数filtertimer_restart是定时器EPIT1 的重启函数。

/*
 * @description		: 重启定时器
 * @param - value	: 定时器EPIT计数值
 * @return 			: 无
 */
void filtertimer_restart(unsigned int value)
{
	EPIT1->CR &= ~(1<<0);	/* 先关闭定时器 */
	EPIT1->LR = value;		/* 计数值 			*/
	EPIT1->CR |= (1<<0);	/* 打开定时器 		*/
}

函数filtertimer_irqhandler

函数filtertimer_irqhandler 是 EPTI1 的中断处理函数,此函数里面就是开启或者关闭蜂鸣器。

/*
 * @description		: 定时器中断处理函数 
 * @param			: 无
 * @return 			: 无
 */
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; 						/* 清除中断标志位 				*/
}

函数 gpio1_16_31_irqhandler

函数 gpio1_16_31_irqhandler 是 GPIO1_IO18 的中断处理函数,此函数负责重启定时。

/*
 * @description		: GPIO中断处理函数
 * @param			: 无
 * @return 			: 无
 */
void gpio1_16_31_irqhandler(void)
{ 
	/* 开启定时器 */
	filtertimer_restart(66000000/100);

	/* 清除中断标志位 */
	gpio_clearintflags(GPIO1, 18);
}

main.c

main.c 文件只有一个 main 函数,在第 23 行调用函数 filterkey_init 来初始化带有消抖的按键,最后在 while 循环里面翻转 LED0,周期大约为 500ms。

整个实验代码就算是修改完成啦,是不是很简单。修改一下makefile,将我们的代码烧录试一试吧~

烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板。

本例程的效果:按下 KEY 就会控制蜂鸣器的开关,并且 LED0 不断的闪烁,提示系统正在运行。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值