[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的相關暫存器 ![image](https://hackmd.io/_uploads/r1S_IAPAC.png =70%x) - 表中暫存器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暫存器較常設定功能 ![image](https://hackmd.io/_uploads/SJudZJdAA.png =80%x) ![image](https://hackmd.io/_uploads/BJ6xS1O00.png =80%x) ![image](https://hackmd.io/_uploads/H1vbBJ_CA.png =80%x) ### 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 - ![image](https://hackmd.io/_uploads/HJAZDkuRR.png =70%x) # EV的中斷 ## 中斷事件 - 特定事件滿足時會依據設定觸發對應中斷,下表列出所有EVA、EVB管理的中斷事件 - 其中<font color="red">TxPINT</font>是定時器周期中斷,發生於當TxCNT的值與TxPR值相同時,這個在達成Timer週期性觸發中斷時較常使用 ![image](https://hackmd.io/_uploads/ry4u7l_A0.png =80%x) ![image](https://hackmd.io/_uploads/HJwcXl_AC.png =80%x) ## 管理中斷的暫存器 - 上面表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; } ```