[TOC]
# 前言
- 本篇以實現Event Manager的Timer周期中斷為目標,介紹EV、Timer、以及EV的中斷
# 事件管理器 (EV) 簡介
- 詳見 <手把手教你DSP>第12章、EV Reference Guide手冊
- DSP的EV module相當於STM32的通用計時器,除了計時也包含其他QEP、PWM、捕獲比較功能,只是DSP把幾個通用計時器使用EV一起管理
- DSP有兩個EV,EVA與EVB。這兩個EV module功能相同,每個EV有兩個定時器,3個比較單元、3個捕獲單元、一個QEP電路等等,這些額外功能都基於EV內的計時器,所以本筆記一樣先看看EV的計時器使用方法吧。
- EV Reference Guide手冊的Figure 1-2 有完整的EV架構
# EV 的 Timer
## 簡介
- 每個EV有兩個16位的Timer
- EVA : TIM1、TIM2
- EVB : TIM3、TIM4
- Timer的計時提供其他EV功能時鐘基礎
## 計時原理與暫存器
- 想讓計時器開始計數並不難,只要有Timer的時鐘源輸入,Timer 的 Counter 暫存器就會依據 Control 暫存器的設置開始計時,下表是EV Timer的相關暫存器

- 表中暫存器GPTCONx (x=A, B)控制EVx的所有Timer。而每個Timer基本上由以下暫存器組成
- x=1,2,3,4
- **TxCNT** : 儲存計數值,依據TxCON與Timer 時鐘源增減計數值
- **TxCMPR** : 儲存比較值,用於比較功能時TxCNT的比較對象
- **TxPR** : 儲存週期值,這個週期值是TxCNT的計數上限,因此能依據計數模式推算timer週期
- **TxCON** : 控制timer運作方式,包含計數模式、時鐘源選擇、Timer使能等等
- 因為TxCMPR用於比較功能,單論計時的話並不會用到,這邊先不提
- TxCON暫存器做為控制TIMx將會是需要設置最多的,以下只提及TxCON暫存器較常設定功能



### TIMx使能 (TENABLE)
- 設為1後Timer才會啟用,要記得檢查這個
### 時鐘源 (TCLKS1、TCLKS0)
- 內部時鐘 : 為HSPCLK高速時鐘
- 外部時鐘 : 由外部給入
- QEP電路 : 使用QEP功能時由QEP做觸發
### Prescaler (TPS2、TPS1、TPS0)
- 將輸入的內部時鐘或外部時鐘做分頻
### 計數模式(TMODE1、TMODE0)
- 兩個bit控制,所以總共有4個計數模式
- 以Timer中斷來說最常用的是<font color="red">連續遞增上數模式</font>,其餘見書或手冊
- **連續遞增上數模式**
- 當TIMx時鐘源被觸發,TxCNT在此模式下就會往上加1,並且在與TxPR值相同時,下次觸發就會歸0
- 
# EV的中斷
## 中斷事件
- 特定事件滿足時會依據設定觸發對應中斷,下表列出所有EVA、EVB管理的中斷事件
- 其中<font color="red">TxPINT</font>是定時器周期中斷,發生於當TxCNT的值與TxPR值相同時,這個在達成Timer週期性觸發中斷時較常使用


## 管理中斷的暫存器
- 上面表12-23、12-24這麼多種EV內的中斷事件,每個都需要有對應的中斷旗標與使能(Enable)位做控制
- **Interrupt Flag Register**
- 由<font color="red">EVAIFRx(x=A,B,C)</font>和<font color="blue">EVBIFRx(x=A,B,C)</font>共6個暫存器對應所有中斷事件的中斷旗標
- **Interrupt Mask Register**
- 由<font color="red">EVAIMRx(x=A,B,C)</font>和<font color="blue">EVBIMRx(x=A,B,C)</font>共6個暫存器對應所有中斷事件的使能位
- 6個暫存器各個bit的對應中斷見書或者[DSP_EV IFR、IMR暫存器表](https://hackmd.io/3mgMb261SEW9gmO7IGNqRQ?both#EVB),表內也含有優先級和對應PIE中斷組別
## 中斷流程
- EV的中斷信號在整個中斷系統中相當於外設部分的中斷,詳見[DSP的中斷系統](https://hackmd.io/xCWvBIXRRpeKnSnIzPNkOQ)
- 發生中斷事件後,對應的中段旗標會立起,如果對應的中斷有使能的話,則會直接將中斷信號送往PIE中斷,如果後續中斷"開關"都有開的話,那麼CPU就會響應這個中斷,執行ISR函式
- 執行ISR函式後記得要<font color="red">手動把EV的中斷旗標IFR復位</font>,否則會重複觸發
</br>
</br>
# 使用EV Timer實現週期中斷實作
- <font color="red">範例專案、腳位見FTP</font>
- <font color="red">LED記得接電阻</font>
- 目標 : 使用EV Timer計時功能創建一個週期觸發的中斷,每0.001秒觸發一次(1 kHz),並每觸發500次後切換LED燈亮暗,相當於每0.5秒LED亮暗反轉
- 使用外設 :
- EVA的TIM1
- GPIOF2作為輸出控制LED燈
- 流程 :
- 依據所需頻率,設定高速時鐘的預除器以決定HSPCLK高速時鐘頻率
- 設定PIE級中斷
- 設定CPU級中斷
- 對T1CON暫存器做設定,設定EV TIM1的prescaler和Period、其他計數模式、中斷等設定
- 設定GPIOF2
- 寫ISR函數
## 查詢與設定步驟
- 以下程式皆添加在初始化範例的 User Initilization Step
### 高速時鐘頻率
- 首先設定高速時鐘的預除器為10,對HISPCP寫入 0b101
``` c++=
#define HSPCLK_PSC_10 5
EALLOW; //This is needed to write to EALLOW protected registers
SysCtrlRegs.HISPCP.all = HSPCLK_PSC_10; // HSPCLK = SYSCLKOUT/HSPCLK_PSC_10
EDIS;
```
### TIM1設定
- 設定計數器為上數模式,所以對 T1CON 的 TMODE 兩個 bit 寫入 0b10
- 設定TIM1的預除器為1,所以對 T1CON 的 TPS 寫 0
- 選擇內部時鐘,對 T1CON 的 TCLKS10 兩個 bit 寫入 0b00
- 啟用Timer,對 T1CON 的 TENABLE 寫 1
- 設定TIM1的T1PR暫存器使觸發頻率為1 kHz,設定值計算為 (150M/10)/1000=15000。注意暫存器只有16 bits,設定值小於65535
- 將T1CNT清0
- TIM1中斷
- 我們使用的是周期中斷,依據[IFR、IMR暫存器表](https://hackmd.io/3mgMb261SEW9gmO7IGNqRQ?both)表12-23,TIM1周期中斷對應T1PINT中斷信號
- 清除T1PINT中斷旗標,查詢EVA的IFR,發現位於EVAIFRA中,表12-26,
- 使能T1PINT中斷,查詢EVA的IMR,發現位於EVAIMARA中,表12-29
``` c++=
#define UP_COUNTING_MODE 2
#define GPTIM_PRESCALAR_1 0
#define GPTIM_ENABLE 1
#define GPTIM_CLK_SRC_INTERNAL 0
#define HSPCLK_FREQ SYSCLKOUT / (2*HSPCLK_PSC_10)
#define SAMPLING_FREQ_1k 1000
void init_eva_timer1(void){
// Using for 1ms Timer.
//---------------------------------------------------------------------------
// Count up, internal clk/1 (HSPCLK/10 = 15MHz), enable Timer
// EvaRegs.T1CON.all = 0x1040; // 00-0-10-000-0-1-00-00-0-0
EvaRegs.T1CON.bit.TMODE = UP_COUNTING_MODE;
EvaRegs.T1CON.bit.TPS = GPTIM_PRESCALAR_1;
EvaRegs.T1CON.bit.TCLKS10 =GPTIM_CLK_SRC_INTERNAL;
EvaRegs.T1CON.bit.TENABLE = GPTIM_ENABLE;
// Set the Period as 1ms
EvaRegs.T1PR = HSPCLK_FREQ / SAMPLING_FREQ_1k;
// Clear the counter for GP timer 1
EvaRegs.T1CNT = 0x0000;
// Enable Period interrupt bits for GP timer 1
EvaRegs.EVAIFRA.bit.T1PINT = 1; // Resets T1PINT FLAG
// (EVA Interrupt Flag Register A)
EvaRegs.EVAIMRA.bit.T1PINT = 1; // T1PINT enable
// (EVA Interrupt Mask Register A)
}
```
### PIE級中斷設定
- 已中斷信號為 T1PINT,查詢[F2812中斷查詢](https://hackmd.io/-naxV-upSE6WgtpQUBUrKw)表11-4,找到T1PINT對應INT2.4,即PIE組2的第4條
- 使能該PIE中斷,對PIEIER2的第四個bit寫1
- 將中斷向量表的T1PINT的ISR改成自己的函式
``` c++=
// 前綴的interrupt表明是ISR,要記得加
interrupt void eva_timer1_isr(void);
PieCtrlRegs.PIEIER2.all = M_INT4;
EALLOW; registers
PieVectTable.T1PINT = &eva_timer1_isr; // Timer1 ISR
EDIS;
```
### CPU級中斷設定
- 使能INT2的中斷
``` c++=
IER |= M_INT2;
```
### GPIO設定
- 將GPIOF2設為一般I/O,對GPFMUX的第2個bit寫0
- 將GPIOF2設為Output,對GPFDIR的第2個bit寫1
``` c++=
void GPIO_select(void){ // for IMU GPIO pin select
EALLOW;
//------------------------
// Set GPIO F pin 2 as output I/O.
GpioMuxRegs.GPFMUX.bit.SPICLKA_GPIOF2 = 0;
GpioMuxRegs.GPFDIR.bit.GPIOF2 = 1;
EDIS;
}
```
## 撰寫ISR
- 宣告一個全域counter,2; 當counter數了500次後,就反轉GPIOF2的輸出
- 把EVAIFRA 的 <font color="red">T1PINT中斷旗標清0</font>
- 因為是INT2,所以是PIEACK的 第2個bit 負責把關。將通路打開以繼續接收該PIE2組中斷,對PIEACK的第二個bit寫1
``` c++=
interrupt void eva_timer1_isr(void){
// ISR Task
//---------------------------------------------------------------------------
count++;
if(count>500){
// GpioDataRegs.GPFDAT.bit.GPIOF2 =1;
GpioDataRegs.GPFDAT.bit.GPIOF2 = ~GpioDataRegs.GPFDAT.bit.GPIOF2;
//GpioDataRegs.GPFTOGGLE.bit.GPIOF2 = 1;
count =0;
}
// Enable more interrupts from this timer
//---------------------------------------------------------------------------
// EvaRegs.EVAIMRA.bit.T1PINT = 1;
// Clear Interrupt Flag
//---------------------------------------------------------------------------
// Note: To be safe, use a mask value to write to the entire
// EVAIFRA register. Writing to one bit will cause a read-modify-write
// operation that may have the result of writing 1's to clear
// bits other then those intended.
EvaRegs.EVAIFRA.bit.T1PINT = 1;
//---------------------------------------------------------------------------
// Acknowledge interrupt to receive more interrupts from PIE group 2
PieCtrlRegs.PIEACK.all = PIEACK_GROUP2;
return;
}
```