日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2023/12/10 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ARM(I.MX6ULL) EPIT定时器中断实验、定时器按键消抖 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

參考:Linux之ARM (I.MX6ULL) EPIT定時(shí)器詳解
作者:一只青木呀
發(fā)布時(shí)間: 2020-09-20 10:03:37
網(wǎng)址:https://blog.csdn.net/weixin_45309916/article/details/108689629

參考:Linux驅(qū)動(dòng)中按鍵消抖原理
作者:一只青木呀
發(fā)布時(shí)間: 2020-09-20 10:15:32
網(wǎng)址:https://blog.csdn.net/weixin_45309916/article/details/108690002

目錄

  • EPIT定時(shí)器簡(jiǎn)介
    • 寄存器EPITx_CR(配置EPIT)
    • 寄存器EPITx_SR(狀態(tài)寄存器,需手動(dòng)清零此位)
    • 寄存器EPITx_LR(加載寄存器)
    • 寄存器EPITx_CMPR(比較寄存器)
    • EPIT 的配置步驟總結(jié)
  • 硬件原理分析
  • 實(shí)驗(yàn)程序編寫(xiě)
    • bsp_epittimer.h
    • bsp_epittimer.c(GIC使能中斷、注冊(cè)中斷服務(wù)函數(shù))
    • main.c
  • 編譯下載驗(yàn)證
    • 編寫(xiě)Makefile 和鏈接腳本
    • 編譯下載
  • 定時(shí)器按鍵消抖
    • 定時(shí)器來(lái)做按鍵消抖原理
    • 硬件原理分析
    • 試驗(yàn)程序編寫(xiě)
      • bsp_keyfilter.h
      • bsp_keyfilter.c(初始化中斷觸發(fā)方式、GIC使能注冊(cè)GPIO按鍵中斷和定時(shí)器中斷服務(wù)函數(shù))
      • main.c
    • 編譯下載驗(yàn)證

定時(shí)器是最常用的外設(shè),常常需要使用定時(shí)器來(lái)完成精準(zhǔn)的定時(shí)功能, I.MX6U 提供了多種硬件定時(shí)器,有些定時(shí)器功能非常強(qiáng)大。我們從最基本的 EPIT 定時(shí)器開(kāi)始,學(xué)習(xí)如何配置 EPIT 定時(shí)器,使其按照給定的時(shí)間,周期性的產(chǎn)生定時(shí)器中斷,在定時(shí)器中斷里面我們可以做其它的處理,比如翻轉(zhuǎn) LED 燈。

EPIT定時(shí)器簡(jiǎn)介

EPIT 的全稱(chēng)是: Enhanced Periodic Interrupt Timer,直譯過(guò)來(lái)就是增強(qiáng)的周期中斷定時(shí)器,它主要是完成周期性中斷定時(shí)的。學(xué)過(guò) STM32 的話應(yīng)該知道, STM32 里面的定時(shí)器還有很多其它的功能,比如輸入捕獲、 PWM 輸出等等。但是 I.MX6U 的 EPIT 定時(shí)器只是完成周期性中斷定時(shí)的,僅此一項(xiàng)功能!至于輸入捕獲、 PWM 輸出等這些功能, I.MX6U 由其它的外設(shè)來(lái)完成(下一節(jié)講的GPT)。

EPIT 是一個(gè) 32 位定時(shí)器,在處理器幾乎不用介入的情況下提供精準(zhǔn)的定時(shí)中斷,軟件使能以后 EPIT 就會(huì)開(kāi)始運(yùn)行, EPIT 定時(shí)器有如下特點(diǎn):

  • ①、時(shí)鐘源可選的 32 位向下計(jì)數(shù)器(每次時(shí)鐘周期減1一直減到0)
  • ②、 12 位的分頻值。
  • ③、當(dāng)計(jì)數(shù)值和比較值相等的時(shí)候產(chǎn)生中斷。

EPIT 定時(shí)器結(jié)構(gòu)如下圖所示:

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

  • ①、這是個(gè)多路選擇器,用來(lái)選擇 EPIT 定時(shí)器的時(shí)鐘源, EPIT 共有 3 個(gè)時(shí)鐘源可選擇,ipg_clk(選這個(gè),66MHz,前面時(shí)鐘系統(tǒng)配置講過(guò))、 ipg_clk_32k 和 ipg_clk_highfreq。
  • ②、這是一個(gè) 12 位的分頻器,負(fù)責(zé)對(duì)時(shí)鐘源進(jìn)行分頻, 12 位對(duì)應(yīng)的值是 0 ~ 4095(2^12),對(duì)應(yīng)著1~4096 分頻。
  • ③、經(jīng)過(guò)分頻的時(shí)鐘進(jìn)入到 EPIT 內(nèi)部,在 EPIT 內(nèi)部有三個(gè)重要的寄存器:計(jì)數(shù)寄存器(EPIT_CNR)、加載寄存器(EPIT_LR)和比較寄存器(EPIT_CMPR),這三個(gè)寄存器都是 32 位的。EPIT 是一個(gè)向下計(jì)數(shù)器,也就是說(shuō)給它一個(gè)初值,它就會(huì)從這個(gè)給定的初值開(kāi)始遞減,直到減為 0,計(jì)數(shù)寄存器里面保存的就是當(dāng)前的計(jì)數(shù)值。如果 EPIT 工作在 set-and-forget 模式下,當(dāng)計(jì)數(shù)寄存器里面的值減少到 0, EPIT 就會(huì)重新從加載寄存器讀取數(shù)值到計(jì)數(shù)寄存器里面,重新開(kāi)始向下計(jì)數(shù)。比較寄存器里面保存的數(shù)值用于和計(jì)數(shù)寄存器里面的計(jì)數(shù)值比較,如果相等的話就會(huì)產(chǎn)生一個(gè)比較事件
  • ④、比較器。
  • ⑤、 EPIT 可以設(shè)置引腳輸出,如果設(shè)置了的話就會(huì)通過(guò)指定的引腳輸出信號(hào)。
  • ⑥、產(chǎn)生比較中斷,也就是定時(shí)中斷。

EPIT 定時(shí)器有兩種工作模式:
set-and-forget 和 free-running,這兩個(gè)工作模式的區(qū)別如下:

  • set-and-forget 模式(常用)
    EPITx_CR(x=1, 2)寄存器的 RLD 位置 1 的時(shí)候 EPIT 工作在此模式下,在此模式下 EPIT 的計(jì)數(shù)器從加載寄存器 EPITx_LR 中獲取初始值,不能直接向計(jì)數(shù)器寄存器寫(xiě)入數(shù)據(jù)。不管什么時(shí)候,只要計(jì)數(shù)器計(jì)數(shù)到 0,那么就會(huì)從加載寄存器 EPITx_LR 中重新加載數(shù)據(jù)到計(jì)數(shù)器中,周而復(fù)始。
  • free-running 模式
    EPITx_CR 寄存器的 RLD 位清零的時(shí)候 EPIT 工作在此模式下,當(dāng)計(jì)數(shù)器計(jì)數(shù)到0以后會(huì)重新從0XFFFFFFFF開(kāi)始計(jì)數(shù),并不是從加載寄存器EPITx_LR中獲取數(shù)據(jù)。

6ULL有兩個(gè)EPIT定時(shí)器,接下來(lái)看一下 EPIT 重要的幾個(gè)寄存器。

寄存器EPITx_CR(配置EPIT)

第一個(gè)就是 EPIT 的配置寄存器 EPITx_CR,此寄存器的結(jié)構(gòu)如下圖所示:


寄存器 EPITx_CR 我們用到的重要位如下:

位描述
CLKSRC(bit25:24)EPIT時(shí)鐘源選擇位,為 0 的時(shí)候關(guān)閉時(shí)鐘源, 1 的時(shí)候選擇選擇Peripheral 時(shí)鐘(ipg_clk),為 2 的時(shí)候選擇 High-frequency 參考時(shí)鐘(ipg_clk_highfreq),為 3 的時(shí)候選擇 Low-frequency 參考時(shí)鐘(ipg_clk_32k)。在本例程中,我們?cè)O(shè)置為 1,也就是選擇 ipg_clk作為 EPIT 的時(shí)鐘源, ipg_clk=66MHz。
PRESCALAR(bit15:4)EPIT 時(shí)鐘源分頻值,可設(shè)置范圍 0~4095,分別對(duì)應(yīng) 1~4096 分頻(12位)。
RLD(bit3)EPIT 工作模式,為 0 的時(shí)候工作在 free-running 模式,為 1 的時(shí)候工作在 setand-forget 模式。本章例程設(shè)置為 1,也就是工作在 set-and-forget 模式。
OCIEN(bit2)比較中斷使能位,為 0 的時(shí)候關(guān)閉比較中斷,為 1 的時(shí)候使能比較中斷,本章試驗(yàn)要使能比較中斷
ENMOD(bit1)設(shè)置計(jì)數(shù)器初始值,為 0 時(shí)計(jì)數(shù)器初始值等于上次關(guān)閉 EPIT 定時(shí)器以后計(jì)數(shù)器里面的值,為 1 的時(shí)候來(lái)源于加載寄存器
EN(bit0)EPIT 使能位,為 0 的時(shí)候關(guān)閉 EPIT,為 1 的時(shí)候使能 EPIT。

寄存器EPITx_SR(狀態(tài)寄存器,需手動(dòng)清零此位)

寄存器 EPITx_SR 結(jié)構(gòu)體如下圖所示:

寄存器 EPITx_SR 只有一個(gè)位有效,那就是 OCIF(bit0),這個(gè)位是比較中斷標(biāo)志位,為 0 的時(shí)候表示沒(méi)有比較事件發(fā)生,為 1 的時(shí)候表示有比較事件發(fā)生。當(dāng)比較中斷發(fā)生以后需要手動(dòng)清除此位,此位是寫(xiě) 1 清零的。

寄存器 EPITx_LR、 EPITx_CMPR 和 EPITx_CNR 分別為加載寄存器、比較寄存器和計(jì)數(shù)寄存器,這三個(gè)寄存器都是用來(lái)存放數(shù)據(jù)的,很簡(jiǎn)單。

寄存器EPITx_LR(加載寄存器)

寄存器 EPITx_LR結(jié)構(gòu)體如下圖所示:

寄存器EPITx_CMPR(比較寄存器)

寄存器 EPITx_CMPR 結(jié)構(gòu)體如下圖所示:

一般配置為0。

EPIT 的配置步驟總結(jié)


通過(guò)上面的分析,EPIT 的配置步驟如下:

1、設(shè)置 EPIT1 的時(shí)鐘源設(shè)置寄存器 EPIT1_CR 寄存器的 CLKSRC(bit25:24)位,選擇 EPIT1 的時(shí)鐘源。

2、設(shè)置分頻值設(shè)置寄存器 EPIT1_CR 寄存器的 PRESCALAR(bit15:4)位,設(shè)置分頻值。

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

4、設(shè)置計(jì)數(shù)器的初始值來(lái)源設(shè)置寄存器 EPIT1_CR 的 ENMOD(bit1)位, 設(shè)置計(jì)數(shù)器的初始值來(lái)源。

5、 使能比較中斷我們要使用到比較中斷,因此需要設(shè)置寄存器 EPIT1_CR 的 OCIEN(bit2)位,使能比較中斷。

6、設(shè)置加載值和比較值設(shè)置寄存器 EPIT1_LR 中的加載值和寄存器 EPIT1_CMPR 中的比較值,通過(guò)這兩個(gè)寄存器就可以決定定時(shí)器的中斷周期。

7、 EPIT1 中斷設(shè)置和中斷服務(wù)函數(shù)編寫(xiě)使能 GIC 中對(duì)應(yīng)的 EPIT1 中斷,注冊(cè)中斷服務(wù)函數(shù),如果需要的話還可以設(shè)置中斷優(yōu)先級(jí)。最后編寫(xiě)中斷服務(wù)函數(shù)。

8、使能 EPIT1 定時(shí)器配置好 EPIT1 以后就可以使能 EPIT1 了,通過(guò)寄存器 EPIT1_CR 的 EN(bit0)位來(lái)設(shè)置。通過(guò)以上幾步我們就配置好 EPIT 了,通過(guò) EPIT 的比較中斷來(lái)實(shí)現(xiàn) LED0 的翻轉(zhuǎn)。

硬件原理分析

本試驗(yàn)用到的資源如下:
①、LED0。
②、定時(shí)器EPTI1。

本實(shí)驗(yàn)通過(guò)EPTI1 的中斷來(lái)控制LED0 的亮滅,LED0 的硬件原理前面已經(jīng)介紹過(guò)了。

實(shí)驗(yàn)程序編寫(xiě)

實(shí)現(xiàn)功能:設(shè)置EPIT1中斷周期為500ms,在EPIT中斷服務(wù)函數(shù)里讓LED燈亮滅。

本實(shí)驗(yàn)對(duì)應(yīng)的例程路徑為:開(kāi)發(fā)板光盤(pán)-> 1、裸機(jī)例程-> 10_epit_timer。
本章實(shí)驗(yàn)在上一章例程的基礎(chǔ)上完成,更改工程名字為“epit_timer”,然后在bsp 文件夾下創(chuàng)建名為“epittimer”的文件夾,然后在bsp/epittimer 中新建bsp_epittimer.c 和bsp_epittimer.h 這兩個(gè)文件。在bsp_epittimer.h 中輸入如下內(nèi)容:

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定時(shí)器驅(qū)動(dòng)頭文件。 其他 : 無(wú) 論壇 : www.wtmembed.com 日志 : 初版V1.0 2019/1/5 左忠凱創(chuàng)建 ***************************************************************/ #include "imx6ul.h"/* 函數(shù)聲明 */ //分頻 //加載 void epit1_init(unsigned int frac, unsigned int value);//初始化EPIT void epit1_irqhandler(void);#endif

bsp_epittimer.c(GIC使能中斷、注冊(cè)中斷服務(wù)函數(shù))

bsp_epittimer.h 文件很簡(jiǎn)單,就是一些函數(shù)聲明。然后在bsp_epittimer.c 中輸入如下內(nèi)容:

/*************************************************************** Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved. 文件名 : bsp_epittimer.c 作者 : 左忠凱 版本 : V1.0 描述 : EPIT定時(shí)器驅(qū)動(dòng)文件。 其他 : 配置EPIT定時(shí)器,實(shí)現(xiàn)EPIT定時(shí)器中斷處理函數(shù) 論壇 : www.wtmembed.com 日志 : 初版V1.0 2019/1/5 左忠凱創(chuàng)建 ***************************************************************/ #include "bsp_epittimer.h" #include "bsp_int.h" #include "bsp_led.h"/** @description : 初始化EPIT定時(shí)器.* EPIT定時(shí)器是32位向下計(jì)數(shù)器,時(shí)鐘源使用ipg=66Mhz * @param - frac : 分頻值,范圍為0~4095,分別對(duì)應(yīng)1~4096分頻。* @param - value : 倒計(jì)數(shù)值。* @return : 無(wú)*/ void epit1_init(unsigned int frac, unsigned int value) {if(frac > 0XFFF)//4095frac = 0XFFF;EPIT1->CR = 0; /* 先清零CR寄存器 *//** CR寄存器:* bit25:24 01 時(shí)鐘源選擇Peripheral clock=66MHz* bit15:4 frac 分頻值* bit3: 1 當(dāng)計(jì)數(shù)器到0的話從LR重新加載數(shù)值* bit2: 1 比較中斷使能* bit1: 1 初始計(jì)數(shù)值來(lái)源于LR寄存器值* bit0: 0 先關(guān)閉EPIT1*/EPIT1->CR = (1<<24 | frac << 4 | 1<<3 | 1<<2 | 1<<1);EPIT1->LR = value; /* 加載數(shù)值 相當(dāng)于倒計(jì)數(shù)值 */EPIT1->CMPR = 0; /* 比較寄存器,當(dāng)計(jì)數(shù)器值和此寄存器值相等的話就會(huì)產(chǎn)生中斷 *//* 使能GIC中對(duì)應(yīng)的中斷 */GIC_EnableIRQ(EPIT1_IRQn);//宏定義這個(gè)中斷號(hào)是88/* 注冊(cè)中斷服務(wù)函數(shù) */ system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)epit1_irqhandler, NULL); EPIT1->CR |= 1<<0; /* 使能EPIT1 */ }/** @description : EPIT中斷處理函數(shù)* @param : 無(wú)* @return : 無(wú)*/ void epit1_irqhandler(unsigned int giccIar, void *userParam)//這里的兩個(gè)參數(shù)沒(méi)有用到 { static unsigned char state = 0;state = !state;if(EPIT1->SR & (1<<0)) /* 判斷比較事件發(fā)生 */{led_switch(LED0, state); /* 定時(shí)器周期到,反轉(zhuǎn)LED */}EPIT1->SR |= 1<<0; /* 清除中斷標(biāo)志位 */ }

bsp_epittimer.c 里面有兩個(gè)函數(shù)epit1_init 和epit1_irqhandler,分別是EPIT1 初始化函數(shù)和EPIT1 中斷處理函數(shù)。epit1_init 有兩個(gè)參數(shù)frac 和value,其中frac 是分頻值,value 是加載值。

在第29 行設(shè)置比較寄存器為0,也就是當(dāng)計(jì)數(shù)器倒計(jì)數(shù)到0 以后就會(huì)觸發(fā)比較中斷,因此分頻值frac 和加載值value 就可以決定中斷頻率,計(jì)算公式如下:

Tout = ((frac +1 )* value) / Tclk;
其中:
Tclk:EPIT1 的輸入時(shí)鐘頻率(單位Hz)。
Tout:EPIT1 的溢出時(shí)間(單位S)。
第38 行設(shè)置了EPIT1 工作模式為set-and-forget,并且時(shí)鐘源為ipg_clk=66MHz。
假如我們現(xiàn)在要設(shè)置EPIT1 中斷周期為500ms,可以設(shè)置分頻值為0,也就是1 分頻,這樣進(jìn)入EPIT1的時(shí)鐘就是66MHz 。如果要實(shí)現(xiàn)500ms 的中斷周期,EPIT1 的加載寄存器就應(yīng)該為66000000/2=33000000。

函數(shù)epit1_irqhandler 是EPIT1 的中斷處理函數(shù),此函數(shù)先讀取EPIT1_SR 寄存器,判斷當(dāng)前的中斷是否為比較事件,如果是的話就翻轉(zhuǎn)LED 燈。最后在退出中斷處理函數(shù)的時(shí)候需要清除中斷標(biāo)志位。

最后就是main.c 文件了,在main.c 里面輸入如下內(nèi)容:

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函數(shù)* @param : 無(wú)* @return : 無(wú)*/ int main(void) {int_init(); /* 初始化中斷(一定要最先調(diào)用!) */imx6u_clkinit(); /* 初始化系統(tǒng)時(shí)鐘 */clk_enable(); /* 使能所有的時(shí)鐘 */led_init(); /* 初始化led */beep_init(); /* 初始化beep */key_init(); /* 初始化key */epit1_init(0, 66000000/2); /* 初始化EPIT1定時(shí)器,1分頻* 計(jì)數(shù)值為:66000000/2,也就是定時(shí)周期為500ms觸發(fā)一次中斷* */while(1) { delay(500);}return 0; }

main.c 里面就一個(gè)main 函數(shù),第22 行調(diào)用函數(shù)epit1_init 來(lái)初始化EPIT1,分頻值為0,也就是1 分頻,加載寄存器值為66000000/2=33000000,EPIT1 定時(shí)器中斷周期為500ms。第26~29 行的while 循環(huán)里面就只有一個(gè)延時(shí)函數(shù),沒(méi)有做其他處理,延時(shí)函數(shù)都可以取掉。

編譯下載驗(yàn)證

編寫(xiě)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”,也就是目標(biāo)名稱(chēng)為“epit”。
第15 行在變量INCDIRS 中添加EPIT1 驅(qū)動(dòng)頭文件(.h)路徑。
第26 行在變量SRCDIRS 中添加EPIT1 驅(qū)動(dòng)文件(.c)路徑。
鏈接腳本保持不變。

編譯下載

使用Make 命令編譯代碼,編譯成功以后使用軟件imxdownload 將編譯完成的epit.bin 文件下載到SD 卡中,命令如下:

chmod 777 imxdownload //給予imxdownload 可執(zhí)行權(quán)限,一次即可 ./imxdownload epit.bin /dev/sdd //燒寫(xiě)到SD 卡中,不能燒寫(xiě)到/dev/sda 或sda1 設(shè)備里面!

燒寫(xiě)成功以后將SD 卡插到開(kāi)發(fā)板的SD 卡槽中,然后復(fù)位開(kāi)發(fā)板。程序運(yùn)行正常的話LED0 會(huì)以500ms 為周期不斷的亮、滅閃爍。

定時(shí)器按鍵消抖

定時(shí)器來(lái)做按鍵消抖原理

用到按鍵就要處理因?yàn)闄C(jī)械結(jié)構(gòu)帶來(lái)的按鍵抖動(dòng)問(wèn)題,也就是按鍵消抖。前面的實(shí)驗(yàn)中都是直接使用了延時(shí)函數(shù)來(lái)實(shí)現(xiàn)消抖,因?yàn)楹?jiǎn)單,但是直接用延時(shí)函數(shù)來(lái)實(shí)現(xiàn)消抖會(huì)浪費(fèi) CPU 性能,因?yàn)樵谘訒r(shí)函數(shù)里面 CPU 什么都做不了。另外,不允許在中斷里面使用延時(shí)函數(shù),因?yàn)橹袛喾?wù)函數(shù)要快進(jìn)快出

本次我們學(xué)習(xí)如何使用定時(shí)器來(lái)實(shí)現(xiàn)按鍵消抖,使用定時(shí)器既可以實(shí)現(xiàn)按鍵消抖,而且也不會(huì)浪費(fèi)CPU 性能,這個(gè)也是 Linux 驅(qū)動(dòng)里面按鍵消抖的做法。

上一篇博文我們學(xué)習(xí)了 EPIT 定時(shí)器,定時(shí)器設(shè)置好定時(shí)時(shí)間,然后 CPU 就可以做其他事情去了,定時(shí)時(shí)間到了以后就會(huì)觸發(fā)中斷,然后在中斷中做相應(yīng)的處理即可。因此,我們可以借助定時(shí)器來(lái)實(shí)現(xiàn)消抖,按鍵采用中斷驅(qū)動(dòng)方式:
當(dāng)按鍵按下以后觸發(fā)按鍵中斷,在按鍵中斷服務(wù)函數(shù)中開(kāi)啟一個(gè)定時(shí)器,定時(shí)周期為 10ms,當(dāng)定時(shí)時(shí)間到了以后就會(huì)觸發(fā)定時(shí)器中斷,最后在定時(shí)器中斷處理函數(shù)中讀取按鍵的值,如果按鍵值還是按下?tīng)顟B(tài)那就表示這是一次有效的按鍵。定時(shí)器按鍵消抖如下圖所示:

在圖 19.1.1 中 t1~ t3 這一段時(shí)間就是按鍵抖動(dòng),是需要消除的。設(shè)置按鍵為下降沿觸發(fā),因此會(huì)在 t1、 t2 和 t3 這三個(gè)時(shí)刻會(huì)觸發(fā)三次按鍵中斷,每次進(jìn)入中斷處理函數(shù)都會(huì)重新開(kāi)器定時(shí)器中斷,所以會(huì)在 t1、 t2 和 t3 這三個(gè)時(shí)刻開(kāi)器定時(shí)器中斷。但是 t1~t2 和 t2~t3 這兩個(gè)時(shí)間段是小于我們?cè)O(shè)置的定時(shí)器中斷周期(也就是消抖時(shí)間,比如 10ms),所以雖然 t1 開(kāi)啟了定時(shí)器,但是定時(shí)器定時(shí)時(shí)間還沒(méi)到呢 ,t2 時(shí)刻就重置了定時(shí)器,最終只有 t3 時(shí)刻開(kāi)啟的定時(shí)器能完整的完成整個(gè)定時(shí)周期并觸發(fā)定時(shí)器中斷,我們就可以在中斷處理函數(shù)里面做按鍵處理了,這就是定時(shí)器實(shí)現(xiàn)按鍵防抖的原理, Linux 里面的按鍵驅(qū)動(dòng)用的就是這個(gè)原理!

硬件原理分析

本試驗(yàn)用到的資源如下:
①、一個(gè)LED 燈LED0。
②、定時(shí)器EPTI1。
③、一個(gè)按鍵KEY。
④、一個(gè)蜂鳴器。

本試驗(yàn)效果和第十五章的試驗(yàn)效果一樣,按下KEY 會(huì)打開(kāi)蜂鳴器,再次按下KEY 就會(huì)關(guān)閉蜂鳴器。LED0 作為系統(tǒng)提示燈不斷的閃爍。

試驗(yàn)程序編寫(xiě)

本實(shí)驗(yàn)對(duì)應(yīng)的例程路徑為:開(kāi)發(fā)板光盤(pán)-> 1、裸機(jī)例程-> 11_key_filter。
本章實(shí)驗(yàn)在上一章例程的基礎(chǔ)上完成,更改工程名字為“key_filter”,然后在bsp 文件夾下創(chuàng)建名為“keyfilter”的文件夾,然后在bsp/keyfilter 中新建bsp_keyfilter.c 和bsp_keyfilter.h 這兩個(gè)文件。在bsp_keyfilter.h 中輸入如下內(nèi)容:

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 描述 : 定時(shí)器按鍵消抖驅(qū)動(dòng)頭文件。 其他 : 無(wú) 論壇 : www.wtmembed.com 日志 : 初版V1.0 2019/1/5 左忠凱創(chuàng)建 ***************************************************************//* 函數(shù)聲明 */ 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 文件很簡(jiǎn)單,只是函數(shù)聲明。在bsp_keyfilter.c 中輸入如下內(nèi)容:

bsp_keyfilter.c(初始化中斷觸發(fā)方式、GIC使能注冊(cè)GPIO按鍵中斷和定時(shí)器中斷服務(wù)函數(shù))

#include "bsp_key.h" #include "bsp_gpio.h" #include "bsp_int.h" #include "bsp_beep.h" #include "bsp_keyfilter.h"/** @description : 按鍵初始化* @param : 無(wú)* @return : 無(wú)*/ void filterkey_init(void) { gpio_pin_config_t key_config;/* 1、初始化IO復(fù)用 */IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0); /* 復(fù)用為GPIO1_IO18 *//* 2、、配置GPIO1_IO18的IO屬性 *bit 16:0 HYS關(guān)閉*bit [15:14]: 11 默認(rèn)22K上拉*bit [13]: 1 pull功能*bit [12]: 1 pull/keeper使能*bit [11]: 0 關(guān)閉開(kāi)路輸出*bit [7:6]: 10 速度100Mhz*bit [5:3]: 000 關(guān)閉輸出*bit [0]: 0 低轉(zhuǎn)換率*/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中對(duì)應(yīng)的中斷 16_31引腳用的同一個(gè)中斷號(hào)*//* 注冊(cè)中斷服務(wù)函數(shù) */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); /* 初始化定時(shí)器,10ms */ }/** @description : 定時(shí)器初始化* @param - value : 定時(shí)器EPIT計(jì)數(shù)值* @return : 無(wú)*/ void filtertimer_init(unsigned int value) {EPIT1->CR = 0; //先清零/** CR寄存器:* bit25:24 01 時(shí)鐘源選擇Peripheral clock=66MHz* bit15:4 0 1分頻* bit3: 1 當(dāng)計(jì)數(shù)器到0的話從LR重新加載數(shù)值* bit2: 1 比較中斷使能* bit1: 1 初始計(jì)數(shù)值來(lái)源于LR寄存器值* bit0: 0 先關(guān)閉EPIT1*/EPIT1->CR = (1<<24 | 1<<3 | 1<<2 | 1<<1);/* 計(jì)數(shù)值 */EPIT1->LR = value;/* 比較寄存器,當(dāng)計(jì)數(shù)器值和此寄存器值相等的話就會(huì)產(chǎn)生中斷 */EPIT1->CMPR = 0; GIC_EnableIRQ(EPIT1_IRQn); /* 使能GIC中對(duì)應(yīng)的中斷 *//* 注冊(cè)中斷服務(wù)函數(shù) */system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)filtertimer_irqhandler, NULL); }/** @description : 關(guān)閉定時(shí)器* @param : 無(wú)* @return : 無(wú)*/ void filtertimer_stop(void) {EPIT1->CR &= ~(1<<0); /* 關(guān)閉定時(shí)器 */ }/** @description : 重啟定時(shí)器* @param - value : 定時(shí)器EPIT計(jì)數(shù)值* @return : 無(wú)*/ void filtertimer_restart(unsigned int value) {EPIT1->CR &= ~(1<<0); /* 先關(guān)閉定時(shí)器 */EPIT1->LR = value; /* 計(jì)數(shù)值 */EPIT1->CR |= (1<<0); /* 打開(kāi)定時(shí)器 */ }/** @description : 定時(shí)器中斷處理函數(shù) * @param : 無(wú)* @return : 無(wú)*/ void filtertimer_irqhandler(void) { static unsigned char state = OFF;if(EPIT1->SR & (1<<0)) /* 判斷比較事件是否發(fā)生 */{filtertimer_stop(); /* 關(guān)閉定時(shí)器 */if(gpio_pinread(GPIO1, 18) == 0) /* KEY0 */{state = !state;beep_switch(state); /* 反轉(zhuǎn)蜂鳴器 */}}EPIT1->SR |= 1<<0; /* 清除中斷標(biāo)志位 */ }/** @description : GPIO按鍵中斷處理函數(shù)* @param : 無(wú)* @return : 無(wú)*/ void gpio1_16_31_irqhandler(void) { /* 開(kāi)啟定時(shí)器 */filtertimer_restart(66000000/100);/* 清除中斷標(biāo)志位 */gpio_clearintflags(GPIO1, 18); }

文件bsp_keyfilter.c 一共有6 個(gè)函數(shù),這6 個(gè)函數(shù)其實(shí)都很簡(jiǎn)單。filterkey_init 是本試驗(yàn)的初始化函數(shù),此函數(shù)首先初始化了KEY 所使用的UART1_CTS 這個(gè)IO,設(shè)置這個(gè)IO 的中斷模式,并且注冊(cè)中斷處理函數(shù),最后調(diào)用函數(shù)filtertimer_init 初始化定時(shí)器EPIT1 定時(shí)周期為10ms。函數(shù)filtertimer_init 是定時(shí)器EPIT1 的初始化函數(shù),內(nèi)容基本和上一章實(shí)驗(yàn)的EPIT1 初始化函數(shù)一樣。函數(shù)filtertimer_stop 和filtertimer_restart 分別是EPIT1 的關(guān)閉和重啟函數(shù)。filtertimer_irqhandler 是EPTI1 的中斷處理函數(shù),此函數(shù)里面就是按鍵要做的工作,在本例程里面就是開(kāi)啟或者關(guān)閉蜂鳴器。函數(shù)gpio1_16_31_irqhandler 是GPIO1_IO18 的中斷處理函數(shù),此函數(shù)只有一個(gè)工作,那就是重啟定時(shí)器EPIT1。

bsp_keyfilter.c 文件內(nèi)容總體來(lái)說(shuō)并不難,基本就是第十七章和第十八章實(shí)驗(yàn)的綜合。最后在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函數(shù)* @param : 無(wú)* @return : 無(wú)*/ int main(void) {unsigned char state = OFF;int_init(); /* 初始化中斷(一定要最先調(diào)用!) */imx6u_clkinit(); /* 初始化系統(tǒng)時(shí)鐘 */clk_enable(); /* 使能所有的時(shí)鐘 */led_init(); /* 初始化led */beep_init(); /* 初始化beep */filterkey_init(); /* 帶有消抖功能的按鍵 */while(1) { state = !state;led_switch(LED0, state);delay(500);}return 0; }

main.c 文件只有一個(gè)main 函數(shù),在第23 行調(diào)用函數(shù)filterkey_init 來(lái)初始化帶有消抖的按鍵,最后在while 循環(huán)里面翻轉(zhuǎn)LED0,周期大約為500ms。

編譯下載驗(yàn)證

編寫(xiě)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”,也就是目標(biāo)名稱(chēng)為“keyfilter”。
第16 行在變量INCDIRS 中添加按鍵消抖驅(qū)動(dòng)頭文件(.h)路徑。
第28 行在變量SRCDIRS 中添加按鍵消抖驅(qū)動(dòng)文件(.c)路徑。
鏈接腳本保持不變。

使用Make 命令編譯代碼,編譯成功以后使用軟件imxdownload 將編譯完成的keyfilter.bin文件下載到SD 卡中,命令如下:

chmod 777 imxdownload //給予imxdownload 可執(zhí)行權(quán)限,一次即可 ./imxdownload keyfilter.bin /dev/sdd //燒寫(xiě)到SD 卡中,不能燒寫(xiě)到/dev/sda 或sda1 里面!

燒寫(xiě)成功以后將SD 卡插到開(kāi)發(fā)板的SD 卡槽中,然后復(fù)位開(kāi)發(fā)板。本例程的效果和第十五章一樣,按下KEY 就會(huì)控制蜂鳴器的開(kāi)關(guān),并且LED0 不斷的閃爍,提示系統(tǒng)正在運(yùn)行。

總結(jié)

以上是生活随笔為你收集整理的ARM(I.MX6ULL) EPIT定时器中断实验、定时器按键消抖的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。