ARM(I.MX6ULL) EPIT定时器中断实验、定时器按键消抖

参考:Linux之ARM (I.MX6ULL) EPIT定时器详解
作者:一只青木呀
发布时间: 2020-09-20 10:03:37
网址:https://blog.csdn.net/weixin_45309916/article/details/108689629

参考:Linux驱动中按键消抖原理
作者:一只青木呀
发布时间: 2020-09-20 10:15:32
网址:https://blog.csdn.net/weixin_45309916/article/details/108690002

目录

  • EPIT定时器简介
    • 寄存器EPITx_CR(配置EPIT)
    • 寄存器EPITx_SR(状态寄存器,需手动清零此位)
    • 寄存器EPITx_LR(加载寄存器)
    • 寄存器EPITx_CMPR(比较寄存器)
    • EPIT 的配置步骤总结
  • 硬件原理分析
  • 实验程序编写
    • bsp_epittimer.h
    • bsp_epittimer.c(GIC使能中断、注册中断服务函数)
    • main.c
  • 编译下载验证
    • 编写Makefile 和链接脚本
    • 编译下载
  • 定时器按键消抖
    • 定时器来做按键消抖原理
    • 硬件原理分析
    • 试验程序编写
      • bsp_keyfilter.h
      • bsp_keyfilter.c(初始化中断触发方式、GIC使能注册GPIO按键中断和定时器中断服务函数)
      • main.c
    • 编译下载验证

定时器是最常用的外设,常常需要使用定时器来完成精准的定时功能, I.MX6U 提供了多种硬件定时器,有些定时器功能非常强大。我们从最基本的 EPIT 定时器开始,学习如何配置 EPIT 定时器,使其按照给定的时间,周期性的产生定时器中断,在定时器中断里面我们可以做其它的处理,比如翻转 LED 灯。

EPIT定时器简介

EPIT 的全称是: Enhanced Periodic Interrupt Timer,直译过来就是增强的周期中断定时器,它主要是完成周期性中断定时的。学过 STM32 的话应该知道, STM32 里面的定时器还有很多其它的功能,比如输入捕获、 PWM 输出等等但是 I.MX6U 的 EPIT 定时器只是完成周期性中断定时的,仅此一项功能!至于输入捕获、 PWM 输出等这些功能, I.MX6U 由其它的外设来完成(下一节讲的GPT)。

EPIT 是一个 32 位定时器,在处理器几乎不用介入的情况下提供精准的定时中断,软件使能以后 EPIT 就会开始运行, EPIT 定时器有如下特点:

  • ①、时钟源可选的 32 位向下计数器(每次时钟周期减1一直减到0)
  • ②、 12 位的分频值。
  • ③、当计数值和比较值相等的时候产生中断。

EPIT 定时器结构如下图所示:
在这里插入图片描述
在这里插入图片描述

上图中各部分的功能如下:

  • ①、这是个多路选择器,用来选择 EPIT 定时器的时钟源, EPIT 共有 3 个时钟源可选择,ipg_clk(选这个,66MHz,前面时钟系统配置讲过)、 ipg_clk_32k 和 ipg_clk_highfreq。
  • ②、这是一个 12 位的分频器,负责对时钟源进行分频, 12 位对应的值是 0 ~ 4095(2^12),对应着1~4096 分频。
  • ③、经过分频的时钟进入到 EPIT 内部,在 EPIT 内部有三个重要的寄存器:计数寄存器(EPIT_CNR)、加载寄存器(EPIT_LR)和比较寄存器(EPIT_CMPR),这三个寄存器都是 32 位的。EPIT 是一个向下计数器,也就是说给它一个初值,它就会从这个给定的初值开始递减,直到减为 0,计数寄存器里面保存的就是当前的计数值。如果 EPIT 工作在 set-and-forget 模式下,当计数寄存器里面的值减少到 0, EPIT 就会重新从加载寄存器读取数值到计数寄存器里面,重新开始向下计数。比较寄存器里面保存的数值用于和计数寄存器里面的计数值比较,如果相等的话就会产生一个比较事件
  • ④、比较器。
  • ⑤、 EPIT 可以设置引脚输出,如果设置了的话就会通过指定的引脚输出信号。
  • ⑥、产生比较中断,也就是定时中断。

EPIT 定时器有两种工作模式:
set-and-forget 和 free-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中获取数据。

6ULL有两个EPIT定时器,接下来看一下 EPIT 重要的几个寄存器。

寄存器EPITx_CR(配置EPIT)

第一个就是 EPIT 的配置寄存器 EPITx_CR,此寄存器的结构如下图所示:
在这里插入图片描述
在这里插入图片描述
寄存器 EPITx_CR 我们用到的重要位如下:

描述
CLKSRC(bit25:24)EPIT时钟源选择位,为 0 的时候关闭时钟源, 1 的时候选择选择Peripheral 时钟(ipg_clk),为 2 的时候选择 High-frequency 参考时钟(ipg_clk_highfreq),为 3 的时候选择 Low-frequency 参考时钟(ipg_clk_32k)。在本例程中,我们设置为 1,也就是选择 ipg_clk作为 EPIT 的时钟源, ipg_clk=66MHz。
PRESCALAR(bit15:4)EPIT 时钟源分频值,可设置范围 0~4095,分别对应 1~4096 分频(12位)。
RLD(bit3)EPIT 工作模式,为 0 的时候工作在 free-running 模式,为 1 的时候工作在 setand-forget 模式。本章例程设置为 1,也就是工作在 set-and-forget 模式。
OCIEN(bit2)比较中断使能位,为 0 的时候关闭比较中断,为 1 的时候使能比较中断,本章试验要使能比较中断
ENMOD(bit1)设置计数器初始值,为 0 时计数器初始值等于上次关闭 EPIT 定时器以后计数器里面的值,为 1 的时候来源于加载寄存器
EN(bit0)EPIT 使能位,为 0 的时候关闭 EPIT,为 1 的时候使能 EPIT。

寄存器EPITx_SR(状态寄存器,需手动清零此位)

寄存器 EPITx_SR 结构体如下图所示:
在这里插入图片描述
寄存器 EPITx_SR 只有一个位有效,那就是 OCIF(bit0),这个位是比较中断标志位,为 0 的时候表示没有比较事件发生,为 1 的时候表示有比较事件发生当比较中断发生以后需要手动清除此位,此位是写 1 清零的。

寄存器 EPITx_LR、 EPITx_CMPR 和 EPITx_CNR 分别为加载寄存器、比较寄存器和计数寄存器,这三个寄存器都是用来存放数据的,很简单。

寄存器EPITx_LR(加载寄存器)

寄存器 EPITx_LR结构体如下图所示:
在这里插入图片描述

寄存器EPITx_CMPR(比较寄存器)

寄存器 EPITx_CMPR 结构体如下图所示:

一般配置为0。
在这里插入图片描述

EPIT 的配置步骤总结

这里是引用
在这里插入图片描述

通过上面的分析,EPIT 的配置步骤如下:

1、设置 EPIT1 的时钟源设置寄存器 EPIT1_CR 寄存器的 CLKSRC(bit25:24)位,选择 EPIT1 的时钟源。

2、设置分频值设置寄存器 EPIT1_CR 寄存器的 PRESCALAR(bit15:4)位,设置分频值。

3、设置工作模式设置寄存器 EPIT1_CR 的 RLD(bit3)位,设置 EPTI1 的工作模式。

4、设置计数器的初始值来源设置寄存器 EPIT1_CR 的 ENMOD(bit1)位, 设置计数器的初始值来源。

5、 使能比较中断我们要使用到比较中断,因此需要设置寄存器 EPIT1_CR 的 OCIEN(bit2)位,使能比较中断。

6、设置加载值和比较值设置寄存器 EPIT1_LR 中的加载值和寄存器 EPIT1_CMPR 中的比较值,通过这两个寄存器就可以决定定时器的中断周期。

7、 EPIT1 中断设置和中断服务函数编写使能 GIC 中对应的 EPIT1 中断,注册中断服务函数,如果需要的话还可以设置中断优先级。最后编写中断服务函数。

8、使能 EPIT1 定时器配置好 EPIT1 以后就可以使能 EPIT1 了,通过寄存器 EPIT1_CR 的 EN(bit0)位来设置。通过以上几步我们就配置好 EPIT 了,通过 EPIT 的比较中断来实现 LED0 的翻转。

硬件原理分析

本试验用到的资源如下:
①、LED0。
②、定时器EPTI1。

本实验通过EPTI1 的中断来控制LED0 的亮灭,LED0 的硬件原理前面已经介绍过了。

实验程序编写

实现功能:设置EPIT1中断周期为500ms,在EPIT中断服务函数里让LED灯亮灭。

本实验对应的例程路径为:开发板光盘-> 1、裸机例程-> 10_epit_timer。
本章实验在上一章例程的基础上完成,更改工程名字为“epit_timer”,然后在bsp 文件夹下创建名为“epittimer”的文件夹,然后在bsp/epittimer 中新建bsp_epittimer.c 和bsp_epittimer.h 这两个文件。在bsp_epittimer.h 中输入如下内容:

bsp_epittimer.h

#ifndef _BSP_EPITTIMER_H
#define _BSP_EPITTIMER_H
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_epittimer.h
作者	   : 左忠凯
版本	   : V1.0
描述	   : EPIT定时器驱动头文件。
其他	   : 无
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/5 左忠凯创建
***************************************************************/
#include "imx6ul.h"/* 函数声明 */               //分频             //加载
void epit1_init(unsigned int frac, unsigned int value);//初始化EPIT 
void epit1_irqhandler(void);#endif

bsp_epittimer.c(GIC使能中断、注册中断服务函数)

bsp_epittimer.h 文件很简单,就是一些函数声明。然后在bsp_epittimer.c 中输入如下内容:

/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_epittimer.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : EPIT定时器驱动文件。
其他	   : 配置EPIT定时器,实现EPIT定时器中断处理函数
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/5 左忠凯创建
***************************************************************/
#include "bsp_epittimer.h"
#include "bsp_int.h"
#include "bsp_led.h"/** @description		: 初始化EPIT定时器.*					  EPIT定时器是32位向下计数器,时钟源使用ipg=66Mhz		 * @param - frac	: 分频值,范围为0~4095,分别对应1~4096分频。* @param - value	: 倒计数值。* @return 			: 无*/
void epit1_init(unsigned int frac, unsigned int value)
{if(frac > 0XFFF)//4095frac = 0XFFF;EPIT1->CR = 0;	/* 先清零CR寄存器 *//** CR寄存器:* bit25:24 01 时钟源选择Peripheral clock=66MHz* bit15:4  frac 分频值* bit3:	1  当计数器到0的话从LR重新加载数值* bit2:	1  比较中断使能* bit1:    1  初始计数值来源于LR寄存器值* bit0:    0  先关闭EPIT1*/EPIT1->CR = (1<<24 | frac << 4 | 1<<3 | 1<<2 | 1<<1);EPIT1->LR = value;	/* 加载数值 相当于倒计数值 */EPIT1->CMPR	= 0;	/* 比较寄存器,当计数器值和此寄存器值相等的话就会产生中断 *//* 使能GIC中对应的中断 	*/GIC_EnableIRQ(EPIT1_IRQn);//宏定义这个中断号是88/* 注册中断服务函数 	*/   system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)epit1_irqhandler, NULL);	EPIT1->CR |= 1<<0;	/* 使能EPIT1 */ 
}/** @description			: EPIT中断处理函数* @param				: 无* @return 				: 无*/
void epit1_irqhandler(unsigned int giccIar, void *userParam)//这里的两个参数没有用到
{ static unsigned char state = 0;state = !state;if(EPIT1->SR & (1<<0)) 			/* 判断比较事件发生 */{led_switch(LED0, state); 	/* 定时器周期到,反转LED */}EPIT1->SR |= 1<<0; 				/* 清除中断标志位 */
}

bsp_epittimer.c 里面有两个函数epit1_init 和epit1_irqhandler,分别是EPIT1 初始化函数和EPIT1 中断处理函数。epit1_init 有两个参数frac 和value,其中frac 是分频值,value 是加载值。

在第29 行设置比较寄存器为0,也就是当计数器倒计数到0 以后就会触发比较中断,因此分频值frac 和加载值value 就可以决定中断频率,计算公式如下:

Tout = ((frac +1 )* value) / Tclk;
其中:
Tclk:EPIT1 的输入时钟频率(单位Hz)。
Tout:EPIT1 的溢出时间(单位S)。
第38 行设置了EPIT1 工作模式为set-and-forget,并且时钟源为ipg_clk=66MHz。
假如我们现在要设置EPIT1 中断周期为500ms,可以设置分频值为0,也就是1 分频,这样进入EPIT1的时钟就是66MHz 。如果要实现500ms 的中断周期,EPIT1 的加载寄存器就应该为66000000/2=33000000。

函数epit1_irqhandler 是EPIT1 的中断处理函数,此函数先读取EPIT1_SR 寄存器,判断当前的中断是否为比较事件,如果是的话就翻转LED 灯。最后在退出中断处理函数的时候需要清除中断标志位。

最后就是main.c 文件了,在main.c 里面输入如下内容:

main.c

#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_epittimer.h"/** @description	: main函数* @param 		: 无* @return 		: 无*/
int main(void)
{int_init(); 				/* 初始化中断(一定要最先调用!) */imx6u_clkinit();			/* 初始化系统时钟 			*/clk_enable();				/* 使能所有的时钟 			*/led_init();					/* 初始化led 			*/beep_init();				/* 初始化beep	 		*/key_init();					/* 初始化key 			*/epit1_init(0, 66000000/2);	/* 初始化EPIT1定时器,1分频* 计数值为:66000000/2,也就是定时周期为500ms触发一次中断* */while(1)			{	delay(500);}return 0;
}

main.c 里面就一个main 函数,第22 行调用函数epit1_init 来初始化EPIT1,分频值为0,也就是1 分频,加载寄存器值为66000000/2=33000000,EPIT1 定时器中断周期为500ms。第26~29 行的while 循环里面就只有一个延时函数,没有做其他处理,延时函数都可以取掉。

编译下载验证

编写Makefile 和链接脚本

修改Makfile 中的TARGET 为epit,在INCDIRS 和SRCDIRS 中加入“bsp/epittimer”,修改后的Makefile 如下:

CROSS_COMPILE 	?= arm-linux-gnueabihf-
TARGET		  	?= epitCC 				:= $(CROSS_COMPILE)gcc
LD				:= $(CROSS_COMPILE)ld
OBJCOPY 		:= $(CROSS_COMPILE)objcopy
OBJDUMP 		:= $(CROSS_COMPILE)objdumpINCDIRS 		:= imx6ul \bsp/clk \bsp/led \bsp/delay  \bsp/beep \bsp/gpio \bsp/key \bsp/exit \bsp/int \bsp/epittimerSRCDIRS			:= project \bsp/clk \bsp/led \bsp/delay \bsp/beep \bsp/gpio \bsp/key \bsp/exit \bsp/int \bsp/epittimerINCLUDE			:= $(patsubst %, -I %, $(INCDIRS))SFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))SFILENDIR		:= $(notdir  $(SFILES))
CFILENDIR		:= $(notdir  $(CFILES))SOBJS			:= $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS			:= $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS			:= $(SOBJS) $(COBJS)VPATH			:= $(SRCDIRS).PHONY: clean$(TARGET).bin : $(OBJS)$(LD) -Timx6ul.lds -o $(TARGET).elf $^$(OBJCOPY) -O binary -S $(TARGET).elf $@$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis$(SOBJS) : obj/%.o : %.S$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<$(COBJS) : obj/%.o : %.c$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<clean:rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

第2 行修改变量TARGET 为“epit”,也就是目标名称为“epit”。
第15 行在变量INCDIRS 中添加EPIT1 驱动头文件(.h)路径。
第26 行在变量SRCDIRS 中添加EPIT1 驱动文件(.c)路径。
链接脚本保持不变。

编译下载

使用Make 命令编译代码,编译成功以后使用软件imxdownload 将编译完成的epit.bin 文件下载到SD 卡中,命令如下:

chmod 777 imxdownload //给予imxdownload 可执行权限,一次即可
./imxdownload epit.bin /dev/sdd //烧写到SD 卡中,不能烧写到/dev/sda 或sda1 设备里面!

烧写成功以后将SD 卡插到开发板的SD 卡槽中,然后复位开发板。程序运行正常的话LED0 会以500ms 为周期不断的亮、灭闪烁。

定时器按键消抖

定时器来做按键消抖原理

用到按键就要处理因为机械结构带来的按键抖动问题,也就是按键消抖。前面的实验中都是直接使用了延时函数来实现消抖,因为简单,但是直接用延时函数来实现消抖会浪费 CPU 性能,因为在延时函数里面 CPU 什么都做不了。另外,不允许在中断里面使用延时函数,因为中断服务函数要快进快出

本次我们学习如何使用定时器来实现按键消抖,使用定时器既可以实现按键消抖,而且也不会浪费CPU 性能,这个也是 Linux 驱动里面按键消抖的做法。

上一篇博文我们学习了 EPIT 定时器,定时器设置好定时时间,然后 CPU 就可以做其他事情去了,定时时间到了以后就会触发中断,然后在中断中做相应的处理即可。因此,我们可以借助定时器来实现消抖,按键采用中断驱动方式:
当按键按下以后触发按键中断,在按键中断服务函数中开启一个定时器,定时周期为 10ms,当定时时间到了以后就会触发定时器中断,最后在定时器中断处理函数中读取按键的值,如果按键值还是按下状态那就表示这是一次有效的按键。定时器按键消抖如下图所示:
在这里插入图片描述
在图 19.1.1 中 t1~ t3 这一段时间就是按键抖动,是需要消除的。设置按键为下降沿触发,因此会在 t1、 t2 和 t3 这三个时刻会触发三次按键中断,每次进入中断处理函数都会重新开器定时器中断,所以会在 t1、 t2 和 t3 这三个时刻开器定时器中断。但是 t1~t2 和 t2~t3 这两个时间段是小于我们设置的定时器中断周期(也就是消抖时间,比如 10ms),所以虽然 t1 开启了定时器,但是定时器定时时间还没到呢 ,t2 时刻就重置了定时器,最终只有 t3 时刻开启的定时器能完整的完成整个定时周期并触发定时器中断,我们就可以在中断处理函数里面做按键处理了,这就是定时器实现按键防抖的原理, Linux 里面的按键驱动用的就是这个原理!

这里是引用

硬件原理分析

本试验用到的资源如下:
①、一个LED 灯LED0。
②、定时器EPTI1。
③、一个按键KEY。
④、一个蜂鸣器。

本试验效果和第十五章的试验效果一样,按下KEY 会打开蜂鸣器,再次按下KEY 就会关闭蜂鸣器。LED0 作为系统提示灯不断的闪烁。

试验程序编写

本实验对应的例程路径为:开发板光盘-> 1、裸机例程-> 11_key_filter。
本章实验在上一章例程的基础上完成,更改工程名字为“key_filter”,然后在bsp 文件夹下创建名为“keyfilter”的文件夹,然后在bsp/keyfilter 中新建bsp_keyfilter.c 和bsp_keyfilter.h 这两个文件。在bsp_keyfilter.h 中输入如下内容:

bsp_keyfilter.h

#ifndef _BSP_KEYFILTER_H
#define _BSP_KEYFILTER_H
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_keyfilter.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : 定时器按键消抖驱动头文件。
其他	   : 无
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/5 左忠凯创建
***************************************************************//* 函数声明 */
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

bsp_keyfilter.h 文件很简单,只是函数声明。在bsp_keyfilter.c 中输入如下内容:

bsp_keyfilter.c(初始化中断触发方式、GIC使能注册GPIO按键中断和定时器中断服务函数)

#include "bsp_key.h"
#include "bsp_gpio.h"
#include "bsp_int.h"
#include "bsp_beep.h"
#include "bsp_keyfilter.h"/** @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中对应的中断   		   16_31引脚用的同一个中断号*//* 注册中断服务函数 */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 */
}/** @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);	
}/** @description		: 关闭定时器* @param 			: 无* @return 			: 无*/
void filtertimer_stop(void)
{EPIT1->CR &= ~(1<<0);	/* 关闭定时器 */
}/** @description		: 重启定时器* @param - value	: 定时器EPIT计数值* @return 			: 无*/
void filtertimer_restart(unsigned int value)
{EPIT1->CR &= ~(1<<0);	/* 先关闭定时器 */EPIT1->LR = value;		/* 计数值 			*/EPIT1->CR |= (1<<0);	/* 打开定时器 		*/
}/** @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; 						/* 清除中断标志位 				*/
}/** @description		: GPIO按键中断处理函数* @param			: 无* @return 			: 无*/
void gpio1_16_31_irqhandler(void)
{ /* 开启定时器 */filtertimer_restart(66000000/100);/* 清除中断标志位 */gpio_clearintflags(GPIO1, 18);
}

文件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 中输入如下所示代码:

main.c

#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_keyfilter.h"/** @description	: main函数* @param 		: 无* @return 		: 无*/
int main(void)
{unsigned char state = OFF;int_init(); 				/* 初始化中断(一定要最先调用!) */imx6u_clkinit();			/* 初始化系统时钟 			*/clk_enable();				/* 使能所有的时钟 			*/led_init();					/* 初始化led 			*/beep_init();				/* 初始化beep	 		*/filterkey_init();			/* 带有消抖功能的按键 */while(1)			{	state = !state;led_switch(LED0, state);delay(500);}return 0;
}

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

编译下载验证

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

CROSS_COMPILE 	?= arm-linux-gnueabihf-
TARGET		  	?= keyfilterCC 				:= $(CROSS_COMPILE)gcc
LD				:= $(CROSS_COMPILE)ld
OBJCOPY 		:= $(CROSS_COMPILE)objcopy
OBJDUMP 		:= $(CROSS_COMPILE)objdumpINCDIRS 		:= imx6ul \bsp/clk \bsp/led \bsp/delay  \bsp/beep \bsp/gpio \bsp/key \bsp/exit \bsp/int \bsp/epittimer \bsp/keyfilterSRCDIRS			:= project \bsp/clk \bsp/led \bsp/delay \bsp/beep \bsp/gpio \bsp/key \bsp/exit \bsp/int \bsp/epittimer \bsp/keyfilterINCLUDE			:= $(patsubst %, -I %, $(INCDIRS))SFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))SFILENDIR		:= $(notdir  $(SFILES))
CFILENDIR		:= $(notdir  $(CFILES))SOBJS			:= $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS			:= $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS			:= $(SOBJS) $(COBJS)VPATH			:= $(SRCDIRS).PHONY: clean$(TARGET).bin : $(OBJS)$(LD) -Timx6ul.lds -o $(TARGET).elf $^$(OBJCOPY) -O binary -S $(TARGET).elf $@$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis$(SOBJS) : obj/%.o : %.S$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<$(COBJS) : obj/%.o : %.c$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<clean:rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

第2 行修改变量TARGET 为“keyfilter”,也就是目标名称为“keyfilter”。
第16 行在变量INCDIRS 中添加按键消抖驱动头文件(.h)路径。
第28 行在变量SRCDIRS 中添加按键消抖驱动文件(.c)路径。
链接脚本保持不变。

使用Make 命令编译代码,编译成功以后使用软件imxdownload 将编译完成的keyfilter.bin文件下载到SD 卡中,命令如下:

chmod 777 imxdownload //给予imxdownload 可执行权限,一次即可
./imxdownload keyfilter.bin /dev/sdd //烧写到SD 卡中,不能烧写到/dev/sda 或sda1 里面!

烧写成功以后将SD 卡插到开发板的SD 卡槽中,然后复位开发板。本例程的效果和第十五章一样,按下KEY 就会控制蜂鸣器的开关,并且LED0 不断的闪烁,提示系统正在运行。

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

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

相关文章

ThinkPHP模型连接数据库 查询 ajax

1.连接数据库 在模板配置文件里面写 2.创建Model模型 让Home和Admin共同使用 3.实例化模型的方法 第一种&#xff1a; 第二种&#xff1a; M()方法 第三种&#xff1a;D()方法 M()方法和D()方法的区别&#xff1a; 在tp3.2以后&#xff0c;M()和D()是一样的&#xff0c;在3.2…

NXP (I.MX6ULL) GPT高精度延时定时器

参考&#xff1a;Linux NXP (I.MX6ULL) GPT高精度延时定时器 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2020-09-20 11:50:14 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/108690475 目录GPT 定时器简介GPT 定时器特性GPT 定时器时钟源选择…

oracle容错,Oracle DML容错处理方法

关于DML Error Logging效率的问题&#xff0c;摘自网上一篇文章&#xff0c;作为单独一篇说明&#xff0c;原文如下&#xff1a;DML Error Logging in Oracle 10g Database Release 2In some situations the most obvious solution to a problem is a DML statement (INSERT ..…

图解快速区别——串口、COM口、UART、TTL、RS-232、RS-485

参考&#xff1a;串口、COM口、UART口, TTL、RS-232、RS-485区别详解 作者&#xff1a;flyingju 发布时间&#xff1a; 2017-09-16 10:30:31 网址&#xff1a;https://blog.csdn.net/zhuyongxin_6688/article/details/78001767 目录串口名词区分COM口串口UARTD型9针串口USB转TT…

这份代码是不是应该有BUG!!

为什么80%的码农都做不了架构师&#xff1f;>>> #include"stdio.h" #include"stdlib.h" #include"math.h" #include"io.h" #include"time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #de…

NXP(I.MX6uLL) UART串口通信原理————这个未复习

参考&#xff1a;Linux NXP &#xff08;I.MX6uLL&#xff09; UART串口通信原理 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2020-09-20 16:48:33 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/108694634 目录1、UART简介1.1、UART串口通讯…

linux内核pwn,Linux Kernel Pwn 学习笔记 (UAF)

原标题&#xff1a;Linux Kernel Pwn 学习笔记 (UAF)本文为看雪论坛优秀文章看雪论坛作者ID&#xff1a;Vinadiak0x01 背景知识UAF漏洞&#xff1a;UAF 漏洞是当我们 free 掉某个指针变量所指向的堆块的时候&#xff0c;未将该指针变量置0&#xff0c;导致该指针依然指着该堆块…

NXP(I.MX6uLL)DDR3实验——DDR发展历史

目录何为RAM 和ROMSRAM 简介①、地址线②、数据线③、控制线SDRAM 简介①、控制线(时钟、片选)②、A10 地址线③、地址线④、BANK 选择线⑤、BANK 区域⑥、数据线⑦、高低字节选择DDR 简介①、控制线②、地址线③、BANK 选择线④、BANK 区域⑤、数据线⑥、数据选通引脚⑦、数据…

超赞!12套你没见过的社交媒体 社交网站图标

如今&#xff0c;社交网络成为我们信息获取和传播的重要途径&#xff0c;很多网站都有把内容分享到社交媒体的功能。社交媒体图标作为向用户传递信息的重要媒介&#xff0c;不管是在网页还是 Web 应用程序中都非常需要。今天这篇文章和大家分享12套你没见过的社交媒体 & 社…

I2C实验

参考&#xff1a;I2C 总线协议详解 作者&#xff1a;一只青木呀 发布时间&#xff1a;2020-09-21 11:41:25 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/108705297 目录I2C 简介I2C 协议I2C协议有关术语1、起始位2、停止位3、数据传输4、应答信号5…

linux安装ffmpeg版本太多,Linux安装FFmpeg的方法

FFmpeg是一款优秀的播放器解码插件&#xff0c;可以跨平台&#xff0c;有不同平台的版本&#xff0c;对于解码&#xff0c;编码和录制以及转换作用的很多的软件都是使用FFmpeg的插件来实现的.FFmpeg&#xff1a;https://www.ffmpeg.org/download.html安装方法&#xff1a;#wget…

官方系统镜像烧写(windows下使用OTG)

目录OTG系统烧写为什么能通过VBS将系统烧写进去呢&#xff1f;OTG系统烧写 选择对应的烧写工具&#xff0c;USB_OTG线连接好&#xff0c;双击即可进行烧写。 注意&#xff1a; 当然也可以烧写到SD卡里面。前面我们烧写裸机代码都是选择从SD卡启动。Mfgtool这个工具先向板子下载…

SQL Server中通用数据库角色权限的处理详解

SQL Server中通用数据库角色权限的处理详解 前言 安全性是所有数据库管理系统的一个重要特征。理解安全性问题是理解数据库管理系统安全性机制的前提。 最近和同事在做数据库权限清理的事情&#xff0c;主要是删除一些账号&#xff1b;取消一些账号的较大的权限等&#xff0c;例…

linux之xargs命令用途

实现文件删除的方法大致有以下几种&#xff1a;1&#xff0e;rm find /a -type f 2&#xff0e;find /a -type f -exec|-ok rm -rf { } \;3&#xff0e;find /a -type f -exec|-ok rm -rf { } ;本例中xargs将find产生的长串文件列表拆散成多个子串&#xff0c;然后…

什么是U-Boot以及如何下载U-Boot源码

参考&#xff1a;什么是U-Boot以及如何下载U-Boot源码&#xff1f; 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2020-10-20 11:05:59 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/109176510 目录前言系统的启动过程uboot简介uboot源码下载选…

linux系统重装win系统教程,Win10如何重装linux系统 Win10重装linux系统教程

Windows10如何重装linux系统?下面小编就给大家介绍一下Win10重装linux系统教程。1、我们按下wini打开设置 后点击【更新和安全】->【windows更新】;2、我们在左侧点击【针对开发人员】&#xff0c;在右侧点击【发开人员模式】;3、我们按winx键->选择【程序和功能】;4、我…

Uboot初次编译、烧写、启动(启动界面log简析)

目录U-Boot 初次编译U-Boot 烧写与启动上述笔记第三点就是建立shell脚本实现的 第四点就是修改Makefile文件实现的&#xff0c;下面均有实现步骤讲解。 U-Boot 初次编译 先编译体验一下正点原子提供的UBOOT。 首先在Ubuntu 中安装ncurses 库&#xff0c;否则编译会报错&#…

360 linux 扩展文件夹,360签具名工Linux下载0907 官方版

腾牛网提供的这款360签名工具Linux版&#xff0c;共有两个版本&#xff0c;分别是图形界面版和命令行界面版&#xff0c;两种选择为apk签名具有一样的正版效果。相关下载&#xff1a;360加固助手pc端Linux签名工具使用流程(图形界面)使用方法&#xff1a;一、准备工作&#xff…

锁优化

前言 高效并发是从JDK1.5到JDK1.6的一个重要改进&#xff0c;HotSpot虚拟机开发团队在这个版本上花费了大量的精力去实现各种锁优化技术&#xff0c;如适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等&#xff0c;这些技术都是为了在线程之间高效的共享数据&#xff0c;以及解…

WPF--ComboBox数据绑定

0-在ComboBox中显示图片&#xff1a; <ComboBox Height"33" HorizontalAlignment"Right" Margin"0,94,31,0" x:Name"comboBox1" VerticalAlignment"Top" Width"142" SelectedIndex"0"> <Co…