【正点原子Linux连载】第十九章 定时器按键消抖实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
3)对正点原子Linux感兴趣的同学可以加群讨论:935446741
4)关注正点原子公众号,获取最新资料更新
在这里插入图片描述

第十九章 定时器按键消抖实验

在第十五章和第十七章实验中都用到了按键,用到按键就要处理因为机械结构带来的按键抖动问题,也就是按键消抖。前面的实验中都是直接使用了延时函数来实现消抖,因为简单,但是直接用延时函数来实现消抖会浪费CPU性能,因为在延时函数里面CPU什么都做不了。如果按键使用中断的话更不能在中断里面使用延时函数,因为中断服务函数要快进快出!本章我们学习如何使用定时器来实现按键消抖,使用定时器既可以实现按键消抖,而且也不会浪费CPU性能,这个也是Linux驱动里面按键消抖的做法。
在这里插入图片描述

19.1 定时器按键消抖简介
按键消抖的原理在第十五章已经详细的讲解了,其实就是在按键按下以后延时一段时间再去读取按键值,如果此时按键值还有效那就表示这是一次有效的按键,中间的延时就是消抖的。但是这有一个缺点,就是延时函数会浪费CPU性能,因为延时函数就是空跑。如果按键是用中断方式实现的,那就更不能在中断服务函数里面使用延时函数,因为中断服务函数最基本的要求就是快进快出!上一章我们学习了EPIT定时器,定时器设置好定时时间,然后CPU就可以做其他事情去了,定时时间到了以后就会触发中断,然后在中断中做相应的处理即可。因此,我们可以借助定时器来实现消抖,按键采用中断驱动方式,当按键按下以后触发按键中断,在按键中断中开启一个定时器,定时周期为10ms,当定时时间到了以后就会触发定时器中断,最后在定时器中断处理函数中读取按键的值,如果按键值还是按下状态那就表示这是一次有效的按键。定时器按键消抖如图19.1.1所示:

图19.1.1 定时器消抖示意图
在图19.1.1中t1t3这一段时间就是按键抖动,是需要消除的。设置按键为下降沿触发,因此会在t1、t2和t3这三个时刻会触发按键中断,每次进入中断处理函数都会重新开器定时器中断,所以会在t1、t2和t3这三个时刻开器定时器中断。但是t1t2和t2~t3这两个时间段是小于我们设置的定时器中断周期(也就是消抖时间,比如10ms),所以虽然t1开启了定时器,但是定时器定时时间还没到呢t2时刻就重置了定时器,最终只有t3时刻开启的定时器能完整的完成整个定时周期并触发中断,我们就可以在中断处理函数里面做按键处理了,这就是定时器实现按键防抖的原理,Linux里面的按键驱动用的就是这个原理!
关于定时器按键消抖的原理就介绍到这里,接下来讲解如何使用EPIT1来配合按键KEY来实现具体的消抖,步骤如下:
1、配置按键IO中断
配置按键所使用的IO,因为要使用到中断驱动按键,所以要配置IO的中断模式。
2、初始化消抖用的定时器
上面已经讲的很清楚了,消抖要用定时器来完成,所以需要初始化一个定时器,这里使用上一章讲解的EPIT1定时器,也算是对EPIT1定时器的一次巩固。定时器的定时周期为10ms,也可根据实际情况调整定时周期。
3、编写中断处理函数
需要编写两个中断处理函数:按键对应的GPIO中断处理函数和EPIT1定时器的中断处理函数。在按键的中断处理函数中主要用于开启EPIT1定时器,EPIT1的中断处理函数才是重点,按键要做的具体任务都是在定时器EPIT1的中断处理函数中完成的,比如控制蜂鸣器打开或关闭。
19.2 硬件原理分析
本试验用到的资源如下:
①、一个LED灯LED0。
②、定时器EPTI1。
③、一个按键KEY。
④、一个蜂鸣器。
本试验效果和第十五章的试验效果一样,按下KEY会打开蜂鸣器,再次按下KEY就会关闭蜂鸣器。LED0作为系统提示灯不断的闪烁。
19.3 试验程序编写
本实验对应的例程路径为:开发板光盘-> 1、裸机例程-> 11_key_filter。
本章实验在上一章例程的基础上完成,更改工程名字为“key_filter”,然后在bsp文件夹下创建名为“keyfilter”的文件夹,然后在bsp/keyfilter中新建bsp_keyfilter.c和bsp_keyfilter.h这两个文件。在bsp_keyfilter.h中输入如下内容:
示例代码19.3.1 bsp_keyfilter.h 文件代码

1  #ifndef _BSP_KEYFILTER_H
2  #define _BSP_KEYFILTER_H
3  /***************************************************************
4  Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
5  文件名    : bsp_keyfilter.h
6  作者      : 左忠凯
7  版本      : V1.0
8  描述      : 定时器按键消抖驱动头文件。
9  其他      : 无
10 论坛      : www.openedv.com
11 日志      : 初版V1.0 2019/1/5 左忠凯创建
12 ***************************************************************/
13 
14 /* 函数声明 */
15 void filterkey_init(void);
16 void filtertimer_init(unsigned int value);
17 void filtertimer_stop(void);
18 void filtertimer_restart(unsigned int value);
19 void filtertimer_irqhandler(void);
20 void gpio1_16_31_irqhandler(void);
21 
22 #endif
bsp_keyfilter.h文件很简单,只是函数声明。在bsp_keyfilter.c中输入如下内容:

示例代码19.3.2 bsp_keyfilter.c 文件代码

/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名   : bsp_keyfilter.c
作者     : 左忠凯
版本     : V1.0
描述     : 定时器按键消抖驱动。
其他     : 按键采用中断方式,按下按键触发按键中断,在按键中断里面
           使能定时器定时中断。使用定时器定时中断来完成消抖延时,
           定时器中断周期就是延时时间。如果定时器定时中断触发,
           表示消抖完成(延时周期完成),即可执行按键处理函数。
论坛     : www.openedv.com
日志     : 初版V1.0 2019/1/5 左忠凯创建
***************************************************************/
1   #include "bsp_key.h"
2   #include "bsp_gpio.h"
3   #include "bsp_int.h"
4   #include "bsp_beep.h"
5   #include "bsp_keyfilter.h"
6   
7   /*
8    * @description 	: 按键初始化
9    * @param         	: 无
10   * @return        	: 无
11   */
12  void filterkey_init(void)
13  {   
14      gpio_pin_config_t key_config;
15      
16      /* 1、初始化IO */
17      IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);
18      IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF080);
19      
20      /* 2、初始化GPIO为中断 */
21      key_config.direction = kGPIO_DigitalInput;
22      key_config.interruptMode = kGPIO_IntFallingEdge;
23      key_config.outputLogic = 1;
24      gpio_init(GPIO1, 18, &key_config);
25  
26      /* 3、 使能GPIO中断,并且注册中断处理函数         */
27      GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn); 
28      system_register_irqhandler(GPIO1_Combined_16_31_IRQn, 
							(system_irq_handler_t)gpio1_16_31_irqhandler, 
							NULL);
29      
30      gpio_enableint(GPIO1, 18);      	/* 使能GPIO1_IO18的中断功能 	*/
31      filtertimer_init(66000000/100);	/* 初始化定时器,10ms 			*/
32  }
33  
34  /*
35   * @description		: 初始化用于消抖的定时器,默认关闭定时器
36   * @param - value   	: 定时器EPIT计数值
37   * @return          	: 无
38   */
39  void filtertimer_init(unsigned int value)
40  {
41      EPIT1->CR = 0;      	/* 先清零        	*/
42      EPIT1->CR = (1<<24 | 1<<3 | 1<<2 | 1<<1);
43      EPIT1->LR = value;  	/* 计数值       		*/
44      EPIT1->CMPR = 0;    	/* 比较寄存器为0	*/
45      
46      /* 使能EPIT1中断并注册中断处理函数*/
47      GIC_EnableIRQ(EPIT1_IRQn);  
48      system_register_irqhandler(EPIT1_IRQn, 
				   	(system_irq_handler_t)filtertimer_irqhandler, 
							NULL); 
49  }
50  
51  /*
52   * @description	: 关闭定时器
53   * @param         	: 无
54   * @return       	: 无
55   */
56  void filtertimer_stop(void)
57  {
58      EPIT1->CR &= ~(1<<0);   	/* 关闭定时器 	*/
59  }
60  
61  /*
62   * @description	: 重启定时器
63   * @param – value	: 定时器EPIT计数值
64   * @return        	: 无
65   */
66  void filtertimer_restart(unsigned int value)
67  {
68      EPIT1->CR &= ~(1<<0); 	/* 先关闭定时器 	*/
69      EPIT1->LR = value;      	/* 计数值      	*/
70      EPIT1->CR |= (1<<0);    	/* 打开定时器  	*/
71  }
72  
73  /*
74   * @description 	: 定时器中断处理函数 
75   * @param        	: 无
76   * @return       	: 无
77   */
78  void filtertimer_irqhandler(void)
79  { 
80      static unsigned char state = OFF;
81  
82      if(EPIT1->SR & (1<<0))             		/* 判断比较事件是否发生 	*/
83      {
84          filtertimer_stop();          			/* 关闭定时器        	*/
85          if(gpio_pinread(GPIO1, 18) == 0)    /* KEY0按下           	*/
86          {
87              state = !state;
88              beep_switch(state);             	/* 反转蜂鸣器           	*/
89          }
90      }
91      EPIT1->SR |= 1<<0;                      	/* 清除中断标志位      	*/
92  }
93  
94  /*
95   * @description  	: GPIO中断处理函数
96   * @param         	: 无
97   * @return        	: 无
98   */
99  void gpio1_16_31_irqhandler(void)
100 { 
101     filtertimer_restart(66000000/100);	/* 开启定时器 		*/
102     gpio_clearintflags(GPIO1, 18);  	/* 清除中断标志位 	*/
103 }
文件bsp_keyfilter.c一共有6个函数,这6个函数其实都很简单。filterkey_init是本试验的初始化函数,此函数首先初始化了KEY所使用的UART1_CTS这个IO,设置这个 IO的中断模式,并且注册中断处理函数,最后调用函数filtertimer_init初始化定时器EPIT1定时周期为10ms。函数filtertimer_init是定时器EPIT1的初始化函数,内容基本和上一章实验的EPIT1初始化函数一样。函数filtertimer_stop和filtertimer_restart分别是EPIT1的关闭和重启函数。filtertimer_irqhandler是EPTI1的中断处理函数,此函数里面就是按键要做的工作,在本例程里面就是开启或者关闭蜂鸣器。函数gpio1_16_31_irqhandler是GPIO1_IO18的中断处理函数,此函数只有一个工作,那就是重启定时器EPIT1。
bsp_keyfilter.c文件内容总体来说并不难,基本就是第十七章和第十八章实验的综合。最后在main.c中输入如下所示代码:
示例代码19.3.3 main.c 文件代码
/**************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名   : main.c
作者     : 左忠凯
版本     : V1.0
描述     : I.MX6U开发板裸机实验11 定时器实现按键消抖实验
其他     : 本实验主要学习如何使用定时器来实现按键消抖,以前的按键
           消抖都是直接使用延时函数来完成的,这种做法效率不高,因为
           延时函数完全是浪费CPU资源的。使用按键中断+定时器来实现按键
           驱动效率是最好的,这也是Linux驱动所使用的方法!
论坛     : www.openedv.com
日志     : 初版V1.0 2019/1/5 左忠凯创建
**************************************************************/
1  #include "bsp_clk.h"
2  #include "bsp_delay.h"
3  #include "bsp_led.h"
4  #include "bsp_beep.h"
5  #include "bsp_key.h"
6  #include "bsp_int.h"
7  #include "bsp_keyfilter.h"
8  
9  /*
10  * @description  : main函数
11  * @param        : 无
12  * @return       : 无
13  */
14 int main(void)
15 {
16  	unsigned char state = OFF;
17 
18  	int_init();              	/* 初始化中断(一定要最先调用!) 	*/
19  	imx6u_clkinit();        	/* 初始化系统时钟            	*/
20  	clk_enable();            	/* 使能所有的时钟            	*/
21  	led_init();              	/* 初始化led             		*/
22  	beep_init();              	/* 初始化beep            		*/
23  	filterkey_init();       	/* 带有消抖功能的按键 			*/
24 
25  	while(1)            
26  	{   
27     		state = !state;
28      	led_switch(LED0, state);
29      	delay(500);
30  	}
31 
32  	return 0;
33 }
main.c文件只有一个main函数,在第23行调用函数filterkey_init来初始化带有消抖的按键,最后在while循环里面翻转LED0,周期大约为500ms。

19.4 编译下载验证
19.4.1 编写Makefile和链接脚本
修改Makefile中的TARGET为keyfilter,在INCDIRS和SRCDIRS中加入“bsp/keyfilter”,修改后的Makefile如下:

示例代码19.4.1 Makefile代码
1   CROSS_COMPILE		?= arm-linux-gnueabihf-
2   TARGET          	?= keyfilter
3   
4   /* 省略掉其它代码...... */
5   
6   INCDIRS         	:=	imx6ul \
7                      		bsp/clk \
8                     		bsp/led \
9                      		bsp/delay  \
10                     		bsp/beep \
11                     		bsp/gpio \
12                     		bsp/key \
13                     		bsp/exit \
14                     		bsp/int \
15                     		bsp/epittimer \
16                     		bsp/keyfilter
17                                 
18  SRCDIRS         	:=	project \
19                     		bsp/clk \
20                    		bsp/led \
21                     		bsp/delay \
22                     		bsp/beep \
23                    		bsp/gpio \
24                     		bsp/key \
25                    		bsp/exit \
26                     		bsp/int \
27                     		bsp/epittimer \
28                     		bsp/keyfilter        
29  
30  /* 省略掉其它代码...... */
31  
32  clean:
33    rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

第2行修改变量TARGET为“keyfilter”,也就是目标名称为“keyfilter”。
第16行在变量INCDIRS中添加按键消抖驱动头文件(.h)路径。
第28行在变量SRCDIRS中添加按键消抖驱动文件(.c)路径。
链接脚本保持不变。
19.4.2 编译下载
使用Make命令编译代码,编译成功以后使用软件imxdownload将编译完成的keyfilter.bin文件下载到SD卡中,命令如下:
chmod 777 imxdownload //给予imxdownload可执行权限,一次即可
./imxdownload keyfilter.bin /dev/sdd //烧写到SD卡中,不能烧写到/dev/sda或sda1里面!
烧写成功以后将SD卡插到开发板的SD卡槽中,然后复位开发板。本例程的效果和第十五章一样,按下KEY就会控制蜂鸣器的开关,并且LED0不断的闪烁,提示系统正在运行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值