前言:
本文是根據嗶哩嗶哩網站上“正點原子[第二期]Linux之ARM(MX6U)裸機篇”視頻的學習筆記,在這里會記錄下正點原子 I.MX6ULL 開發板的配套視頻教程所作的實驗和學習筆記內容。本文大量引用了正點原子教學視頻和鏈接中的內容。
引用:
正點原子IMX6U倉庫 (GuangzhouXingyi) - Gitee.com
《【正點原子】I.MX6U嵌入式Linux驅動開發指南V1.5.2.pdf》
正點原子資料下載中心 — 正點原子資料下載中心 1.0.0 文檔
正文:
本文是 “正點原子[第二期]Linux之ARM(MX6U)裸機篇--第16?講” 的讀書筆記。第16講主要是介紹I.MX6U處理器的EPIT定時器。本節將參考正點原子的視頻教程第16講和配套的正點原子開發指南文檔進行學習。
0. 概述
定時器是最常見的外設,常常需要使用定時器來完成精準的定時功能,I.MX6U 提供了多種硬件定時器,有些定時器功能非常強大。本章我們從最基本的EPIT定時器開始,學習如何配置EPIT定時器,使其按照給定的時間,周期性的產生定時器中斷,在定時器中斷里面我們可以做其他處理,比如翻轉LED燈。
1. EPIT定時器原理
EPIT的全稱是:Enhanced Period Interrupt Timer,直譯過來就是增強的周期中斷定時器,它主要是完成周期性中斷的。當學過STM32的話應該知道,STM32里面的定時器還有很多其他的功能,比如輸入捕獲,PWM輸出等等。但是I.MX6U的EPIT定時器只是完成周期性中斷定時的功能,僅此一項功能。至于輸入捕獲,PWM輸出燈這些功能,I.MX6U由其他的外設來完成。
EPIT是一個32位定時器,在處理器幾乎不用介入的情況下提供精準的定時中斷,軟件使能以后EPIT就會開始運行,EPIT定時器有如下特點:
- 時鐘源可選的32位向下定時器
- 12位分頻值
- 當計數值和比較值相等的時候產生中斷
EPIT定時器的結構如下圖所示:
- 這是一個多路選擇器,用來選擇EPIT定時器的時鐘源,EPIT共有三個時鐘源可以選擇 ipg_clk, ipg_clk_32k, ipg_clk_highfrq
- 這是一個12位的分頻器,負責對時鐘源進行分頻,12位對應的值是0~4095,對應著1~4096分頻
- 經過分頻的時鐘進入到EPIT定時內部,在EPIT定時器內部有三個重要的寄存器:技術寄存器(EPIT_CR),加載寄存器(EPIT_LR)和比較寄存器(EPIT_CMPR),這3個寄存器都是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中獲取數據。
1.1 EPIT定時器關聯的?EPITx_XX 寄存器如下
寄存器 | 描述 |
EPITx_CR | Control Register 控制寄存器 |
EPITx_SR | Status Regisetr 狀態寄存器 |
EPITx_LR | Load Register 加載寄存器 |
EPITx_CMPR | Compare Register 比較寄存器 |
EPITx_CNR | Counter Register 計數寄存器 |
1.2 EPIT 比較重要的幾個寄存器
?加下來看一下GPIT重要的幾個寄存器,第一個就是EPIT的配置寄存器EPITx_CR,此寄存器的結構如下圖所示:
EPITx_CR控制寄存器 | 描述 |
CLKSRC bit[25:24] | EPIT時鐘源選擇位,為0時關閉時鐘源,1時使用ipg_clk時鐘源,2時使用ipg_clk_higrfreq時鐘源,3時使用 ipg_clk_32k 時鐘源。 在本例程中,我們設置為 1,也就是選擇 ipg_clk 作為 EPIT 的時鐘源, ipg_clk=66MHz。 |
IOVW bit[17] | EPIT計數值覆蓋寫使能。為0寫EPIT LR加載寄存器不影響計數寄存器里的值,為1寫EPIT LR加載寄存器會立即覆蓋寫計數寄存器。 |
PRESCALAR bit[15:4] | EPIT時鐘源分配值,可設置范圍0~4095,分別對應1~4096分頻。 |
RLD bit[3] | EPIT工作模式,為0的時候工作在free-running模式,為1的時候工作在set-and-forget模式。本章例程設置為1,也就是工作在set-and-forget模式 |
OCIEN bit[2] | 比較中斷使能位,為0時關閉比較中斷,為1的時候使能比較中斷。本章實驗使能比較中斷。 |
ENMOD bit[1] | 設置計數器初始值,為0時計數器初始值等于上次關閉EPIT定時器以后計數器里面的值,為1的時候來源于加載寄存器。 |
EN bit[0] | EPIT使能位,為0的時候關閉EPIT,為1的時候使能EPIT。 |
寄存器EPITx_SR 寄存器結構如下圖所示:
寄存器EPITx_SR寄存器只有一個有效位,那就是 OCIF(Outpurt Comparte Interrupt Flag)bit[0],為0時表示沒有比較事件發生,為1的時候表示有比較事件發生。當比較事件發生以后需要手動清除此位,此位是寫1清零。
關于 EPIT 的寄存器就介紹到這里,關于這些寄存器詳細的描述,請參考《I.MX6ULL 參考手冊》第 1174 頁的 24.6 小節。
2. EPIT定時器程序編寫
本章我們使用EPIT產生功能定時中斷,然后在中斷服務函數里面翻轉LED0,接下來以EPIT1為例,講解需要哪些步驟來實現這個功能。EPIT1的配置步驟如下
- 設置EPIT1的時鐘源
設置EPIT1_CR寄存器的 CLKSEL bit[25:24]位,選擇 EPIT1的時鐘源。- 設置分頻值
設置EPIT1_CR寄存器的 PRESCALER bit[15:4]位,設置分頻值- 設置工作模式
設置EPIT1_CR寄存器的 RLD bit[3] 位,設置計數器的初始值來源。- 設置計數值的初始值來源
設置EPIT1_CR寄存器的 ENMODE?bit[1] 位,設置計數器的初始值來源。- 使能比較中斷
我們要使用到比較中斷,因此需要設置EPIT1_CR寄存器的 OCIEN bit[2] 位,使能比較中斷。- 設置加載值和比較值
設置寄存器EPIT1_LR中國加載值和寄存器EPIT1_CMPR中的比較值,通過這兩個寄存器就可以決定計時器的中斷周期。- EPIT1中斷設置和中斷服務函數編寫
使能GIC中對應的EPIT1中斷,注冊中斷服務函數,如果需要的話還可以設置中斷優先級。最后編寫中斷服務函數。- 使能EPIT1定時器
配置好EPIT1以后就可以使能EPIT1了,通過EPIT1_CR寄存器的 EN bit[0] 位來設置。通過以上幾步我們就配置好EPIT了,通過EPIT的比較中斷來實現LED0的翻轉。
2.1 本節用到的硬件資源
- ?LED0
- 定時器EPIT1
本實驗通過EPIT的中斷來控制LED0的亮滅,LED0的硬件原理前面已經介紹過了。
EIPT定時器輸出比較中斷的中斷ID號為 88=56+32:
2.2 實驗程序編寫
經過上面的分析EPIT1定時器的使用方法和配置EPIT1寄存器的步驟已經清楚,接下來實現正點原子I.MX6U ALPHA/Mini 開發板上的EPIT定時器驅動程序。
#include "bsp_epittimer.h"
#include "bsp_beep.h"
#include "bsp_led.h"
#include "bsp_int.h"void epittimer_init(int frac, int counterValue)
{if(frac<0 || frac > 0xFFF){return;}/* 首先清零EPIT1->CR 控制寄存器.* I.MX6U手冊要求在修改EPIT定時器時鐘源之前必須先去使能EPIT定時器 */EPIT1->CR = 0x0;/* CLKSEL bit[25:24] EPIT時鐘源選擇, 1:ipg_clk */EPIT1->CR |= (1 << 24);/* PRESCALAR bit[15:4] EPIT分頻值,0:1分頻, 66MHz/1==66MHz */EPIT1->CR |= (frac << 4);/* RLD bit[3] EPIT工作模式,1:set-and-foret 模式 */EPIT1->CR |= (1 << 3);/* OCIEN bit[2] EPIT輸出比較中斷使能,1:使能比較中斷 */EPIT1->CR |= (1 << 2);/* ENMODE bit[1] EPIT使能模式,1:EPIT使能是計數器值從LR寄存器獲取 */EPIT1->CR |= (1 << 1);/* EPITx_LR 加載值寄存器設置 */EPIT1->LR = counterValue;/* EPIT_CMPR 比較值寄存器設置 */EPIT1->CMPR = 0;/* 使能GIC IRQn 中斷 */GIC_EnableIRQ(EPIT1_IRQn);/* 注冊EPIT1比較中斷 EPIT1_IRQn 的中斷處理函數 */system_irqhandler_register(EPIT1_IRQn, eptitimer_irq_handler, NULL);/* 使能EPIT1 EN bit[0], 1: 是能EPIT */EPIT1->CR |= (1 << 0);
}void eptitimer_irq_handler(IRQn_Type irq, void *userparam)
{static int beep_state = 0;static int led_state = 0;if((EPIT1->SR & (1 << 0))){ /* 判斷比較中斷事件發生 */beep_state = !beep_state;beep_switch(beep_state);led_state = !led_state;led_switch(LED_0, led_state);}/* 清除EPITx_ISR 中斷標志位 */EPIT1->SR |= (1 << 0);
}
3. 編譯燒寫SD卡驗證按鍵EPIT定時器中斷實驗結果
譯修改主頻后源碼燒錄SD卡驗證本節的EPIT定時器實驗是否生效。預期燒錄SD卡后正點原子I.MX6ULL ALPHA/Mini 開發板會周期性的每500ms鳴叫一次。
我本地驗證的結果是EPIT定時器正常工作每500ms觸發一次EPIT輸出比較事件中斷在EPIT定時器中斷里翻轉一次蜂鳴器的開關,蜂鳴器正常鳴叫。
4. 總結和實驗遇到的問題記錄
4.1 問題1:EPIT定時器驅動程序燒錄SD,開發板上電需要等待大概1分鐘之后蜂鳴器才會開始按照500ms的間隔鳴叫。
對照正點原子的示例源碼找到了問題原因:
原因:忘記了配置EPITx->CR寄存器的ENMODE bit[1] 位置1,這樣CR寄存器 ENMOE=0,EPIT計數寄存器就使用上一次殘留的寄存器值開始向下遞減,可能是從0XFFFFFFFF 開始遞減的所以需要等待大概1分鐘才能遞減到 0.
?5. 結束
本文至此結束