/**************************************************************************//** * @file timer.c * @version V1.00 * $Revision: 2 $ * $Date: 16/02/24 15:37 $ * @brief Panchip series TIMER driver source file * * @note * Copyright (C) 2016 Panchip Technology Corp. All rights reserved. *****************************************************************************/ #include "PanSeries.h" #include "pan_timer.h" #include "pan_clk.h" /** @addtogroup Panchip_Device_Driver Panchip Device Driver @{ */ /** @addtogroup Panchip_TIMER_Driver TIMER Driver @{ */ /** @addtogroup Panchip_TIMER_EXPORTED_FUNCTIONS TIMER Exported Functions @{ */ /** * @brief This API is used to configure timer to operate in specified mode * and frequency. If timer cannot work in target frequency, a closest * frequency will be chose and returned. * @param[in] timer The base address of Timer module * @param[in] cntMode Operation mode. Possible options are * - \ref TIMER_ONESHOT_MODE * - \ref TIMER_PERIODIC_MODE * - \ref TIMER_TOGGLE_MODE * - \ref TIMER_CONTINUOUS_MODE * @param[in] u32Freq Timer Target working frequency * @return Real Timer working frequency * @note After calling this API, Timer is \b NOT running yet. But could start timer running be calling * \ref TIMER_Start macro or program registers directly */ uint32_t TIMER_Open(TIMER_T *timer, TIMER_CntModeDef cntMode, uint32_t u32Freq) { uint32_t u32Clk = CLK_GetPeripheralFreq(timer); uint32_t u32Prescale = 0; uint32_t u32RealFreq = u32Freq; if (u32RealFreq > u32Clk) u32RealFreq = u32Clk; else if (u32RealFreq == 0) u32RealFreq = 1; u32Prescale = u32Clk / u32RealFreq - 1; if (u32Prescale > 0xFF) u32Prescale = 0xFF; //Prescale is 8bit in reg timer->CTL = ((timer->CTL & ~TIMER_CTL_PSC_Msk) |u32Prescale); timer->CTL = (( timer->CTL& ~TIMER_CTL_OPMODE_Msk)|cntMode); // Calc real frequency and return u32RealFreq = u32Clk / (u32Prescale + 1); return u32RealFreq; } /** * @brief This API stops Timer counting and disable the Timer interrupt function * @param[in] timer The base address of Timer module * @return None */ void TIMER_Close(TIMER_T *timer) { timer->CTL = 0; timer->EXTCTL = 0; } /** * @brief This API is used to create a delay loop for u32usec micro seconds * @param[in] timer The base address of Timer module * @param[in] u32Usec Delay period in micro seconds with 10 usec every step. Valid values are between 10~1000000 (10 micro second ~ 1 second) * @return None * @note This API overwrites the register setting of the timer used to count the delay time. * @note This API use polling mode. So there is no need to enable interrupt for the timer module used to generate delay */ void TIMER_Delay(TIMER_T *timer, uint32_t u32Usec) { uint32_t u32Clk = CLK_GetPeripheralFreq(timer); uint32_t u32Prescale = 0, delay = SystemCoreClock / u32Clk; float fCmpr; // Clear current timer configuration timer->CTL = 0; timer->EXTCTL = 0; if(u32Clk == 10000) { // min delay is 100us if timer clock source is LIRC 10k u32Usec = ((u32Usec + 99) / 100) * 100; } else { // 10 usec every step u32Usec = ((u32Usec + 9) / 10) * 10; } 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; } // u32Usec * u32Clk might overflow if using uint32_t fCmpr = (float)u32Usec * u32Clk / 1000000.0f; timer->CMP = (uint32_t)fCmpr; timer->CTL = TIMER_CTL_CNTEN_Msk | u32Prescale; // one shot mode // When system clock is faster than timer clock, it is possible timer active bit cannot set in time while we check it. // And the while loop below return immediately, so put a tiny delay here allowing timer start counting and raise active flag. for(; delay > 0; delay--) { __NOP(); } while(timer->CTL & TIMER_CTL_ACTSTS_Msk); } void TIMER_SetCmpValue(TIMER_T *timer, TIMER0_CmpSelDef cmp_seq, uint32_t u32Value) { uint32_t cnt_per_apb_clks = 0, clk_src = 0; uint8_t psc_store_value = timer->CTL & TIMER_CTL_PSC_Msk; uint8_t compare_compensate = 0, remainder = FIXED_DEVIATION; /*count freq = apbclock / (psc + 1), so 1 hw timer count = (psc + 1) apb clks */ cnt_per_apb_clks = psc_store_value + 1; if (timer == TIMER0) { clk_src = CLK->APB1_CLK_CTRL & CLK_APB1CLK_TMR0SRC_SEL_Msk; } else if(timer == TIMER1){ clk_src = CLK->APB2_CLK_CTRL & CLK_APB2CLK_TMR1SRC_SEL_Msk; } else if(timer == TIMER2){ clk_src = CLK->APB2_CLK_CTRL & CLK_APB2CLK_TMR2SRC_SEL_Msk; } /*timer clk source is apb clk */ if (clk_src == 0) { if (FIXED_DEVIATION >= cnt_per_apb_clks) { compare_compensate = FIXED_DEVIATION / cnt_per_apb_clks; remainder = FIXED_DEVIATION - compare_compensate * cnt_per_apb_clks; } if (remainder * 2 >= cnt_per_apb_clks) { compare_compensate += 1; } } /*here has 12 apb clk for hw timer sync operation*/ if (timer == TIMER0) { switch (cmp_seq) { case TMR0_COMPARATOR_SEL_CMP: timer->CMP = u32Value - compare_compensate; TIMER0->CTL |= (BIT0 << 8); break; case TMR0_COMPARATOR_SEL_CMP1: timer->CMP1 = u32Value - compare_compensate; TIMER0->CTL |= (BIT1 << 8); break; case TMR0_COMPARATOR_SEL_CMP2: timer->CMP2 = u32Value - compare_compensate; TIMER0->CTL |= (BIT2 << 8); break; default: timer->CMP = u32Value - compare_compensate; break; } } else if ((timer == TIMER1) || (timer == TIMER2)){ timer->CMP = u32Value - compare_compensate; } } /*@}*/ /* end of group Panchip_TIMER_EXPORTED_FUNCTIONS */ /*@}*/ /* end of group Panchip_TIMER_Driver */ /*@}*/ /* end of group Panchip_Device_Driver */ /*** (C) COPYRIGHT 2016 Panchip Technology Corp. ***/