mbed library sources. Supersedes mbed-src.
Dependents: Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more
Diff: targets/TARGET_NUVOTON/TARGET_M480/lp_ticker.c
- Revision:
- 187:0387e8f68319
- Parent:
- 186:707f6e361f3e
- Child:
- 188:bcfe06ba3d64
--- a/targets/TARGET_NUVOTON/TARGET_M480/lp_ticker.c Fri Jun 22 16:45:37 2018 +0100 +++ b/targets/TARGET_NUVOTON/TARGET_M480/lp_ticker.c Thu Sep 06 13:40:20 2018 +0100 @@ -42,17 +42,42 @@ #define TIMER_MODINIT timer1_modinit -static int ticker_inited = 0; +/* Timer interrupt enable/disable + * + * Because Timer interrupt enable/disable (TIMER_EnableInt/TIMER_DisableInt) needs wait for lp_ticker, + * we call NVIC_DisableIRQ/NVIC_EnableIRQ instead. + */ + +/* Track ticker status */ +static volatile uint16_t ticker_inited = 0; #define TMR_CMP_MIN 2 #define TMR_CMP_MAX 0xFFFFFFu -/* NOTE: When system clock is higher than timer clock, we need to add 3 engine clock - * (recommended by designer) delay to wait for above timer control to take effect. */ +/* Synchronization issue with LXT/LIRC-clocked Timer + * + * PCLK : typical HCLK/2 + * ECLK (engine clock) : LXT/LIRC for Timer used to implement lp_ticker + * + * When system clock is higher than Timer clock (LXT/LIRC), we need to add delay for ECLK + * domain to take effect: + * 1. Write : typical 1PCLK + 2ECLK + * Read-check doesn't work because it just checks PCLK domain and doesn't check into + * ECLK domain. + * 2. Clear interrupt flag : typical 2PCLK + * It is very rare that we would meet dummy interrupt and get stuck in ISR until + * 'clear interrupt flag' takes effect. The issue is ignorable because the pending + * time is very short (at most 1 dummy interrupt). We won't take special handling for it. + */ void lp_ticker_init(void) { if (ticker_inited) { + /* By HAL spec, ticker_init allows the ticker to keep counting and disables the + * ticker interrupt. */ + lp_ticker_disable_interrupt(); + lp_ticker_clear_interrupt(); + NVIC_ClearPendingIRQ(TIMER_MODINIT.irq_n); return; } ticker_inited = 1; @@ -86,7 +111,7 @@ // Set vector NVIC_SetVector(TIMER_MODINIT.irq_n, (uint32_t) TIMER_MODINIT.var); - NVIC_EnableIRQ(TIMER_MODINIT.irq_n); + NVIC_DisableIRQ(TIMER_MODINIT.irq_n); TIMER_EnableInt(timer_base); wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3); @@ -101,6 +126,33 @@ while(! (timer_base->CTL & TIMER_CTL_ACTSTS_Msk)); } +void lp_ticker_free(void) +{ + TIMER_T *timer_base = (TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname); + + /* Stop counting */ + TIMER_Stop(timer_base); + wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3); + + /* Wait for timer to stop counting and unset active flag */ + while((timer_base->CTL & TIMER_CTL_ACTSTS_Msk)); + + /* Disable wakeup */ + TIMER_DisableWakeup(timer_base); + wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3); + + /* Disable interrupt */ + TIMER_DisableInt(timer_base); + wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3); + + NVIC_DisableIRQ(TIMER_MODINIT.irq_n); + + /* Disable IP clock */ + CLK_DisableModuleClock(TIMER_MODINIT.clkidx); + + ticker_inited = 0; +} + timestamp_t lp_ticker_read() { if (! ticker_inited) { @@ -129,20 +181,29 @@ uint32_t cmp_timer = timestamp * NU_TMRCLK_PER_TICK; cmp_timer = NU_CLAMP(cmp_timer, TMR_CMP_MIN, TMR_CMP_MAX); + /* NOTE: Rely on LPTICKER_DELAY_TICKS to be non-blocking. */ timer_base->CMP = cmp_timer; - wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3); + + /* We can call ticker_irq_handler now. */ + NVIC_EnableIRQ(TIMER_MODINIT.irq_n); } void lp_ticker_disable_interrupt(void) { - TIMER_DisableInt((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname)); - wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3); + /* We cannot call ticker_irq_handler now. */ + NVIC_DisableIRQ(TIMER_MODINIT.irq_n); } void lp_ticker_clear_interrupt(void) { - TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname)); - wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3); + /* To avoid sync issue, we clear TIF/TWKF simultaneously rather than call separate + * driver API: + * + * TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname)); + * TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname)); + */ + TIMER_T *timer_base = (TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname); + timer_base->INTSTS = TIMER_INTSTS_TIF_Msk | TIMER_INTSTS_TWKF_Msk; } void lp_ticker_fire_interrupt(void) @@ -150,6 +211,9 @@ // NOTE: This event was in the past. Set the interrupt as pending, but don't process it here. // This prevents a recursive loop under heavy load which can lead to a stack overflow. NVIC_SetPendingIRQ(TIMER_MODINIT.irq_n); + + /* We can call ticker_irq_handler now. */ + NVIC_EnableIRQ(TIMER_MODINIT.irq_n); } const ticker_info_t* lp_ticker_get_info() @@ -163,11 +227,7 @@ static void tmr1_vec(void) { - TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname)); - wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3); - - TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname)); - wait_us((NU_US_PER_SEC / NU_TMRCLK_PER_SEC) * 3); + lp_ticker_clear_interrupt(); // NOTE: lp_ticker_set_interrupt() may get called in lp_ticker_irq_handler(); lp_ticker_irq_handler();