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);#endifbsp_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);#endifbsp_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 如下:
第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 不斷的閃爍,提示系統正在運行。
總結
以上是生活随笔為你收集整理的ARM(I.MX6ULL) EPIT定时器中断实验、定时器按键消抖的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【JSP】jsp报错:Syntax er
- 下一篇: i.MX283A移植mt7601--小米