To be completed!

# 嵌入式系統設計:實驗 4

# Part I. Clock & Timer

typedef struct
{
    __IO uint32_t PWRCON;        /*  System Power-down Control Register          */
    __IO uint32_t AHBCLK;        /*  AHB Devices Clock Enable Control Register   */
    __IO uint32_t APBCLK;        /*  APB Devices Clock Enable Control Register   */
    __IO uint32_t CLKSTATUS;     /*  Clock status monitor Register               */
    __IO uint32_t CLKSEL0;       /*  Clock Source Select Control Register 0      */
    __IO uint32_t CLKSEL1;       /*  Clock Source Select Control Register 1      */
    __IO uint32_t CLKDIV;        /*  Clock Divider Number Register               */
    __IO uint32_t CLKSEL2;       /*  Clock Source Select Control Register 2      */
    __IO uint32_t PLLCON;        /*  PLL Control Register                        */
    __IO uint32_t FRQDIV;        /*  Frequency Divider Control Register          */
    __IO uint32_t RESERVE[2];   
    __IO uint32_t APBCLK1;       /*  APB Devices Clock Enable Control Register 1 */
    __IO uint32_t CLKSEL3;       /*  Clock Source Select Control Register 3      */
    __IO uint32_t CLKDIV1;       /*  Clock Divider Number Register 1             */

} CLK_T;
typedef struct
{
    __IO uint32_t TCSR;      /* Timer Control and Status Register */
    __IO uint32_t TCMPR;     /* Timer Compare Register  */
    __IO uint32_t TISR;      /* Timer Interrupt Status Register */
    __I  uint32_t TDR;       /* Timer Data Register */
    __I  uint32_t TCAP;      /* Timer Capture Data Register */
    __IO uint32_t TEXCON;    /* Timer External Control Register */
    __IO uint32_t TEXISR;    /* Timer External Interrupt Status Register */
} TIMER_T;
/* Get Timer Clock Frequency */
uint32_t TIMER_GetModuleClock(TIMER_T *timer)
{
    uint32_t u32Src;
    const uint32_t au32Clk[] = {__HXT, __LXT, 0, 0, 0, __LIRC, 0, __HIRC};

    if(timer == TIMER0)
        u32Src = (CLK->CLKSEL1 & CLK_CLKSEL1_TMR0_S_Msk) >> CLK_CLKSEL1_TMR0_S_Pos;
    else if(timer == TIMER1)
        u32Src = (CLK->CLKSEL1 & CLK_CLKSEL1_TMR1_S_Msk) >> CLK_CLKSEL1_TMR1_S_Pos;
    else if(timer == TIMER2)
        u32Src = (CLK->CLKSEL1 & CLK_CLKSEL1_TMR2_S_Msk) >> CLK_CLKSEL1_TMR2_S_Pos;
    else  // Timer 3
        u32Src = (CLK->CLKSEL1 & CLK_CLKSEL1_TMR3_S_Msk) >> CLK_CLKSEL1_TMR3_S_Pos;

    if(u32Src == 2) return(SystemCoreClock);

    return(au32Clk[u32Src]);
}
uint32_t TIMER_Open(TIMER_T *timer, uint32_t u32Mode, uint32_t u32Freq)
{
    uint32_t u32Clk = TIMER_GetModuleClock(timer);
    uint32_t u32Cmpr = 0, u32Prescale = 0;

    /*
     * Fastest possible timer working freq is (u32Clk / 2). 
     * While cmpr = 2, pre-scale = 0.
     */
    if(u32Freq > (u32Clk / 2))
    {
        u32Cmpr = 2;
    }
    else
    {
        if(u32Clk >= 0x4000000)
        {
            u32Prescale = 7;    // real prescaler value is 8
            u32Clk >>= 3;
        }
        else if(u32Clk >= 0x2000000)
        {
            u32Prescale = 3;    // real prescaler value is 4
            u32Clk >>= 2;
        }
        else if(u32Clk >= 0x1000000)
        {
            u32Prescale = 1;    // real prescaler value is 2
            u32Clk >>= 1;
        }

        u32Cmpr = u32Clk / u32Freq;
    }

    timer->TCSR = u32Mode | u32Prescale;
    timer->TCMPR = u32Cmpr;

    return(u32Clk / (u32Cmpr * (u32Prescale + 1)));
}

這個函式較為複雜,但你可以仔細查看它。為了生成特定的計時器頻率,我們將根據以下公式計算所需的值,包括比較值(comparison value)和預分頻器(prescaler)。

Timer Freq.=Clock Freq.Comparison Value×(Prescaler+1)\text{Timer Freq. } = {\text{Clock Freq.}\over{\text{Comparison Value}×(\text{Prescaler}+1)}}

Timer Period=Clock Freq.×Comparison Value×(Prescaler+1)\text{Timer Period } = {\text{Clock Freq.}\times{\text{Comparison Value}×(\text{Prescaler}+1)}}

Questions:

  1. There are 4 modes of timer, which bitfield of TIMER_T stores the mode of the timer?
  2. Based on the comment for CLK_T::CLKSEL1 , try to guess the values of CLK_CLKSEL1_TMR0_S_Msk and CLK_CLKSEL1_TMR0_S_Pos , and explain the following code from TIMER_GetModuleClock()
    u32Src = (CLK->CLKSEL1 & CLK_CLKSEL1_TMR0_S_Msk) >> CLK_CLKSEL1_TMR0_S_Pos;

# Part II. Interrupt

...

volatile uint8_t ledState = 0;

void TMR1_IRQHandler(void)
{
    ledState = ~ledState; // changing ON/OFF state
    if (ledState)
        PC12 = 0;
    else
        PC12 = 1;
    TIMER_ClearIntFlag(TIMER1); // Clear Timer1 time-out interrupt flag
}

void Init_Timer1(void)
{
    TIMER_Open(TIMER1, TMR1_OPERATING_MODE, TMR1_OPERATING_FREQ);
    TIMER_EnableInt(TIMER1);
    NVIC_EnableIRQ(TMR1_IRQn);
    TIMER_Start(TIMER1);
}

int main(void)
{
    SYS_Init(); // Intialize System/Peripheral clocks & multi-function I/Os

    GPIO_SetMode(PC, BIT12, GPIO_MODE_OUTPUT); // set LED GPIO pin
    Init_Timer1();

    while (1);
}

你可以在 TMR_LED/main.c 中找到這個範例。這裡有一個奇怪的地方:沒有人在呼叫 TMR1_IRQHandler() ,但它卻能正常運作。為什麼會觸發呢?

秘密就在於 NVIC_EnableIRQ(TMR1_IRQn) 。當計時器發送中斷信號時,中斷處理函式會跳轉到向量表(vector table)來決定適當的動作。

/* startup_NUC100Series.s */
    MODULE  ?cstartup

    ;; Forward declaration of sections.
    SECTION CSTACK:DATA:NOROOT(3) ;; 8 bytes alignment

    SECTION .intvec:CODE:NOROOT(2);; 4 bytes alignment

    EXTERN  SystemInit	
    EXTERN  __iar_program_start
    PUBLIC  __vector_table

    DATA
__vector_table
    DCD     sfe(CSTACK)
    DCD     Reset_Handler

    DCD     NMI_Handler
    DCD     HardFault_Handler
    DCD     0
    ...
    DCD     SysTick_Handler

    ; External Interrupts
    
    ...
    
    DCD     TMR0_IRQHandler             ; Timer 0 interrupt                                      
    DCD     TMR1_IRQHandler             ; Timer 1 interrupt                                      
    DCD     TMR2_IRQHandler             ; Timer 2 interrupt                                      
    DCD     TMR3_IRQHandler             ; Timer 3 interrupt                                      
    DCD     UART02_IRQHandler           ; UART0 interrupt                                        
    DCD     UART1_IRQHandler            ; UART1 interrupt                                        
    DCD     SPI0_IRQHandler             ; SPI0 interrupt                                         
    DCD     SPI1_IRQHandler             ; SPI1 interrupt                                         
    DCD     SPI2_IRQHandler             ; SPI2 interrupt                                         
    DCD     SPI3_IRQHandler             ; SPI3 interrupt                                         
    
    ...
    
    END

處理函式會檢查你的 C 程式碼中是否定義了 TMR1_IRQHandler ;如果沒有,它將觸發預設的處理函式來處理計時器中斷。這就是為什麼 void TMR1_IRQHandler(void) 會被觸發的原因。


# 實作

我們複寫了系統預設的中斷處理函數,並做了 periodic counting 的練習。

補充
Lab 5: Mixed Topics

我們組別做了一個飛機射擊遊戲,我方是透過 keypad 左右移動,敵人則是自動生成,由上往下前進。
需要用 keypad 發射砲彈攻擊敵人,直到死亡為止。
使用到的原件: keypad, LCD, , 7-segment, timer