/* * Copyright (C) 2018 Gunar Schorcht * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup cpu_esp32 * @ingroup drivers_periph_timer * @{ * * @file * @brief Low-level timer driver implementation for ESP32 SDK * * @author Gunar Schorcht * @} */ /* * WARNING! enable debugging will have timing side effects and can lead * to timer underflows, system crashes or system dead locks in worst case. */ #define ENABLE_DEBUG (0) #include "debug.h" #include "periph/timer.h" #include "driver/periph_ctrl.h" #include "esp/common_macros.h" #include "rom/ets_sys.h" #include "soc/rtc.h" #include "soc/timer_group_struct.h" #include "xtensa/hal.h" #include "xtensa/xtensa_api.h" #include "esp_common.h" #include "irq_arch.h" #include "syscalls.h" #include "xtimer.h" #define RTC_PLL_480M 480 /* PLL with 480 MHz at maximum */ #define RTC_PLL_320M 320 /* PLL with 480 MHz at maximum */ #ifndef MODULE_ESP_HW_COUNTER /* hardware timer modules used */ /** * ESP32 has four 64 bit hardware timers: * two timer groups TMG0 and TMG1 with 2 timers each * * TMG0, timer 0 is used for system time in us and is therefore not * available as low level timer. Timers have only one channel. Timer device * are mapped to hardware timer as following: * * 0 -> TMG0 timer 1 * 1 -> TMG1 timer 0 * 2 -> TMG1 timer 1 * * The reason for this mapping is, that if only one timer is needed, * TMG1 is left disabled. TMG1 is only enabled when more than one * timer device is needed. * * PLEASE NOTE: Don't use ETS timer functions ets_timer_* in and this hardware * timer implementation together! */ #define HW_TIMER_NUMOF 3 #define HW_TIMER_CHANNELS 1 #define HW_TIMER_CLK_DIV (rtc_clk_apb_freq_get() / 1000000) #define HW_TIMER_CORRECTION (RTC_PLL_320M / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) #define HW_TIMER_DELTA_MIN (MAX(HW_TIMER_CORRECTION << 1, 5)) struct hw_timer_regs_t { /* see Technical Reference, section 17.4 */ struct { uint32_t unused : 10; uint32_t ALARM_EN : 1; /* alarms are enabled */ uint32_t LEVEL_INT_EN: 1; /* alarms will generate level type interrupt */ uint32_t EDGE_INT_EN : 1; /* alarms will generate edge type interrupt */ uint32_t DIVIDER : 16; /* timer clock prescale value (basis is ABP) */ uint32_t AUTORELOAD : 1; /* auto-reload on alarms */ uint32_t INCREASE : 1; /* count up */ uint32_t EN : 1; /* timer is enabled */ } CONFIG_REG; uint32_t LO_REG; /* time-base counter value low 32 bits */ uint32_t HI_REG; /* time-base counter value high 32 bits */ uint32_t UPDATE_REG; /* time-base counter value update trigger */ uint32_t ALARMLO_REG; /* alarm trigger time-base counter value, low 32 bits */ uint32_t ALARMHI_REG; /* alarm trigger time-base counter value, high 32 bits */ uint32_t LOADLO_REG; /* reload value, low 32 bits */ uint32_t LOADHI_REG; /* reload value, high 32 bits */ uint32_t LOAD_REG; /* reload trigger */ }; struct hw_timer_ints_t { /* see Technical Reference, section 17.4 */ uint32_t INT_ENA_REG; /* interrupt enable bits */ uint32_t INT_RAW_REG; /* raw interrupt status */ uint32_t INT_STA_REG; /* masked interrupt status */ uint32_t INT_CLR_REG; /* interrupt clear bits */ }; struct hw_timer_t { bool initialized; /* indicates whether timer is already initialized */ bool started; /* indicates whether timer is already started */ timer_isr_ctx_t isr_ctx; }; struct hw_timer_hw_t { volatile struct hw_timer_regs_t* regs; /* timer configuration regs */ volatile struct hw_timer_ints_t* int_regs; /* timer interrupt regs */ uint8_t int_mask; /* timer interrupt bit mask in interrupt regs */ uint8_t int_src; /* timer interrupt source */ }; static struct hw_timer_t timers[HW_TIMER_NUMOF] = { }; static const struct hw_timer_hw_t timers_hw[HW_TIMER_NUMOF] = { { .regs = (struct hw_timer_regs_t*)&TIMERG0.hw_timer[1], .int_regs = (struct hw_timer_ints_t*)&TIMERG0.int_ena, .int_mask = BIT(1), .int_src = ETS_TG0_T1_LEVEL_INTR_SOURCE }, { .regs = (struct hw_timer_regs_t*)&TIMERG1.hw_timer[0], .int_regs = (struct hw_timer_ints_t*)&TIMERG1.int_ena, .int_mask = BIT(0), .int_src = ETS_TG1_T0_LEVEL_INTR_SOURCE }, { .regs = (struct hw_timer_regs_t*)&TIMERG1.hw_timer[1], .int_regs = (struct hw_timer_ints_t*)&TIMERG1.int_ena, .int_mask = BIT(1), .int_src = ETS_TG1_T1_LEVEL_INTR_SOURCE } }; /** Latches the current counter value and return only the low part */ static inline uint32_t timer_get_counter_lo(tim_t dev) { /* latch the current timer value by writing any value to the update reg */ timers_hw[dev].regs->UPDATE_REG = 0; /* read high and low part of counter */ return timers_hw[dev].regs->LO_REG; } /** Latches the current counter value and return the high and the low part */ static inline void timer_get_counter(tim_t dev, uint32_t* hi, uint32_t* lo) { /* parameter check */ if (!hi || !lo) { return; } /* latch the current timer value by writing any value to the update reg */ timers_hw[dev].regs->UPDATE_REG = 0; /* read high and low part of counter */ *hi = timers_hw[dev].regs->HI_REG; *lo = timers_hw[dev].regs->LO_REG; } void IRAM hw_timer_handler(void* arg) { (void)arg; /* since all timer interrupt sources are routed to the same cpu interrupt */ /* signal, we can't use arg to identify the timer which caused the it */ irq_isr_enter(); for (unsigned dev = 0; dev < HW_TIMER_NUMOF; dev++) { /* iterate over all devices and check what interrupt flags are set */ if (timers_hw[dev].int_regs->INT_STA_REG & timers_hw[dev].int_mask) { DEBUG("%s dev=%d\n", __func__, dev); /* disable alarms */ timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0; timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0; /* clear the bit in interrupt enable and status register */ timers_hw[dev].int_regs->INT_ENA_REG &= ~timers_hw[dev].int_mask; timers_hw[dev].int_regs->INT_CLR_REG |= timers_hw[dev].int_mask; /* execute the callback function */ timers[dev].isr_ctx.cb(timers[dev].isr_ctx.arg, 0); } } irq_isr_exit(); } int timer_init (tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) { DEBUG("%s dev=%u freq=%lu cb=%p arg=%p\n", __func__, dev, freq, cb, arg); CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); CHECK_PARAM_RET (freq == XTIMER_HZ_BASE, -1); CHECK_PARAM_RET (cb != NULL, -1); if (timers[dev].initialized) { DEBUG("%s timer dev=%u is already initialized (used)\n", __func__, dev); return -1; } /* initialize timer data structure */ timers[dev].initialized = true; timers[dev].started = false; timers[dev].isr_ctx.cb = cb; timers[dev].isr_ctx.arg = arg; /* route all timer interrupt sources to the same level type interrupt */ intr_matrix_set(PRO_CPU_NUM, timers_hw[dev].int_src, CPU_INUM_TIMER); /* we have to enable therefore the interrupt here */ xt_set_interrupt_handler(CPU_INUM_TIMER, hw_timer_handler, NULL); xt_ints_on(BIT(CPU_INUM_TIMER)); if (dev) { /* if dev > 0 we have to enable TMG1 module */ periph_module_enable(PERIPH_TIMG1_MODULE); } /* hardware timer configuration */ timers_hw[dev].regs->CONFIG_REG.EN = 0; timers_hw[dev].regs->CONFIG_REG.AUTORELOAD = 0; timers_hw[dev].regs->CONFIG_REG.INCREASE = 1; timers_hw[dev].regs->CONFIG_REG.DIVIDER = HW_TIMER_CLK_DIV; timers_hw[dev].regs->CONFIG_REG.EDGE_INT_EN = 0; timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0; timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0; /* start the timer */ timer_start(dev); return 0; } int IRAM timer_set(tim_t dev, int chn, unsigned int delta) { DEBUG("%s dev=%u channel=%d delta=%u\n", __func__, dev, chn, delta); CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1); /* disable interrupts */ int state = irq_disable (); /* disable alarms */ timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0; timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0; delta = (delta > HW_TIMER_DELTA_MIN) ? delta : HW_TIMER_DELTA_MIN; delta = (delta > HW_TIMER_CORRECTION) ? delta - HW_TIMER_CORRECTION : HW_TIMER_CORRECTION; /* read the current value */ uint32_t count_lo; uint32_t count_hi; timer_get_counter(dev, &count_hi, &count_lo); /* determine the alarm time */ uint64_t alarm; alarm = count_lo; alarm += ((uint64_t)count_hi) << 32; alarm += delta; timers_hw[dev].regs->ALARMHI_REG = (uint32_t)(alarm >> 32); timers_hw[dev].regs->ALARMLO_REG = (uint32_t)(alarm & 0xffffffff); /* enable alarms */ timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 1; timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 1; /* wait until instructions have been finished */ timers_hw[dev].regs->CONFIG_REG.EN = 1; /* clear the bit in status and set the bit in interrupt enable */ timers_hw[dev].int_regs->INT_CLR_REG |= timers_hw[dev].int_mask; timers_hw[dev].int_regs->INT_ENA_REG |= timers_hw[dev].int_mask; /* restore interrupts enabled state */ irq_restore (state); return 0; } int IRAM timer_set_absolute(tim_t dev, int chn, unsigned int value) { DEBUG("%s dev=%u channel=%d value=%u\n", __func__, dev, chn, value); return timer_set (dev, chn, value - timer_read(dev)); } int timer_clear(tim_t dev, int chn) { DEBUG("%s dev=%u channel=%d\n", __func__, dev, chn); CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1); /* disable alarms */ timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0; timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0; /* clear the bit in interrupt enable and status register */ timers_hw[dev].int_regs->INT_ENA_REG &= ~timers_hw[dev].int_mask; timers_hw[dev].int_regs->INT_CLR_REG |= timers_hw[dev].int_mask; return 0; } unsigned int IRAM timer_read(tim_t dev) { CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); #if ENABLE_DEBUG uint32_t count_lo = timer_get_counter_lo(dev); DEBUG("%s %u\n", __func__, count_lo); return count_lo; #else return timer_get_counter_lo(dev); #endif } void IRAM timer_start(tim_t dev) { DEBUG("%s dev=%u @%u\n", __func__, dev, system_get_time()); CHECK_PARAM (dev < HW_TIMER_NUMOF); timers_hw[dev].regs->CONFIG_REG.EN = 1; } void IRAM timer_stop(tim_t dev) { DEBUG("%s dev=%u\n", __func__, dev); CHECK_PARAM (dev < HW_TIMER_NUMOF); timers_hw[dev].regs->CONFIG_REG.EN = 0; } #else /* MODULE_ESP_HW_COUNTER */ /* hardware counter used as timer */ /** * ESP32 has 3 ccompare registers. Each of them can generate an interrupt * at different levels: * * CCOMPARE INT Level Priority * 0 6 XCHAL_TIMER0_INTERRUPT 1 low * 1 15 XCHAL_TIMER1_INTERRUPT 3 medium * 2 16 XCHAL_TIMER2_INTERRUPT 5 high * * PLEASE NOTE: High level interrupts are not disabled in any case. So be * careful to to use CCOMPARE register 2 and timer num 2, respectively. * By default, TIMER_NUMOF is therefore set to only 2 in periph_conf.h. */ #define HW_TIMER_NUMOF XCHAL_NUM_TIMERS #define HW_TIMER_CHANNELS 1 #define HW_TIMER_MASK 0xffffffff #define HW_TIMER_DELTA_MAX 0x00ffffff /* in us */ #define HW_TIMER_DELTA_MASK 0x00ffffff #define HW_TIMER_DELTA_RSHIFT 24 #define HW_TIMER_CORRECTION (RTC_PLL_480M / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) #define HW_TIMER_DELTA_MIN (MAX(HW_TIMER_CORRECTION, 5)) #define US_TO_HW_TIMER_TICKS(t) (t * system_get_cpu_freq()) #define HW_TIMER_TICKS_TO_US(t) (t / system_get_cpu_freq()) struct hw_channel_t { bool used; /* indicates whether the channel is used */ uint32_t start_time; /* physical time when the timer channel has been started */ uint32_t delta_time; /* timer delta value (delta = cycles * timer_max + remainder) */ uint32_t cycles; /* number of complete max timer cycles */ uint32_t remainder; /* remainder timer value */ }; struct hw_timer_t { tim_t dev; /* the timer device num */ bool initialized; /* indicates whether timer is already initialized */ bool started; /* indicates whether timer is already started */ timer_isr_ctx_t isr_ctx; struct hw_channel_t channels[HW_TIMER_CHANNELS]; }; static struct hw_timer_t timers[HW_TIMER_NUMOF] = { }; static const uint8_t timers_int[HW_TIMER_NUMOF] = { XCHAL_TIMER0_INTERRUPT, XCHAL_TIMER1_INTERRUPT, XCHAL_TIMER2_INTERRUPT }; static void __timer_channel_start (struct hw_timer_t* timer, struct hw_channel_t* channel); static void __timer_channel_stop (struct hw_timer_t* timer, struct hw_channel_t* channel); static uint32_t __hw_timer_ticks_max; static uint32_t __hw_timer_ticks_min; void IRAM hw_timer_handler(void* arg) { uint32_t dev = (uint32_t)arg; uint32_t chn = 0; if (dev >= HW_TIMER_NUMOF && chn >= HW_TIMER_CHANNELS) { return; } irq_isr_enter(); DEBUG("%s arg=%p\n", __func__, arg); struct hw_timer_t* timer = &timers[dev]; struct hw_channel_t* channel = &timer->channels[chn]; if (channel->cycles) { channel->cycles--; xthal_set_ccompare(dev, xthal_get_ccount() + __hw_timer_ticks_max); } else if (channel->remainder >= HW_TIMER_DELTA_MIN) { xthal_set_ccompare (dev, xthal_get_ccount() + US_TO_HW_TIMER_TICKS(channel->remainder)); channel->remainder = 0; } else { channel->remainder = 0; channel->used = false; xt_ints_off(BIT(timers_int[dev])); xthal_set_ccompare (dev, 0); timer->isr_ctx.cb(timer->isr_ctx.arg, chn); } irq_isr_exit(); } int timer_init (tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) { DEBUG("%s dev=%u freq=%lu cb=%p arg=%p\n", __func__, dev, freq, cb, arg); CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); CHECK_PARAM_RET (freq == XTIMER_HZ_BASE, -1); CHECK_PARAM_RET (cb != NULL, -1); if (timers[dev].initialized) { DEBUG("%s timer dev=%u is already initialized (used)\n", __func__, dev); return -1; } timers[dev].dev = dev; timers[dev].initialized = true; timers[dev].started = false; timers[dev].isr_ctx.cb = cb; timers[dev].isr_ctx.arg = arg; xt_set_interrupt_handler(timers_int[dev], hw_timer_handler, (void *)dev); for (int i = 0; i < HW_TIMER_CHANNELS; i++) { timers[dev].channels[i].used = false; timers[dev].channels[i].cycles = 0; timers[dev].channels[i].remainder = 0; } timer_start(dev); return 0; } int IRAM timer_set(tim_t dev, int chn, unsigned int delta) { DEBUG("%s dev=%u channel=%d delta=%u\n", __func__, dev, chn, delta); CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1); int state = irq_disable (); struct hw_timer_t* timer = &timers[dev]; struct hw_channel_t* channel = &timer->channels[chn]; /* set delta time and channel used flag */ channel->delta_time = delta > HW_TIMER_CORRECTION ? delta - HW_TIMER_CORRECTION : 0; channel->used = true; /* start channel with new delta time */ __timer_channel_start (timer, channel); irq_restore (state); return 0; } int IRAM timer_set_absolute(tim_t dev, int chn, unsigned int value) { DEBUG("%s dev=%u channel=%d value=%u\n", __func__, dev, chn, value); return timer_set (dev, chn, value - timer_read(dev)); } int timer_clear(tim_t dev, int chn) { DEBUG("%s dev=%u channel=%d\n", __func__, dev, chn); CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1); int state = irq_disable (); /* stop running timer channel */ __timer_channel_stop (&timers[dev], &timers[dev].channels[chn]); irq_restore (state); return 0; } unsigned int IRAM timer_read(tim_t dev) { (void)dev; return system_get_time (); } void IRAM timer_start(tim_t dev) { DEBUG("%s dev=%u @%u\n", __func__, dev, system_get_time()); CHECK_PARAM (dev < HW_TIMER_NUMOF); CHECK_PARAM (!timers[dev].started); int state = irq_disable (); __hw_timer_ticks_max = US_TO_HW_TIMER_TICKS(HW_TIMER_DELTA_MAX); __hw_timer_ticks_min = US_TO_HW_TIMER_TICKS(HW_TIMER_DELTA_MIN); struct hw_timer_t* timer = &timers[dev]; timer->started = true; for (int i = 0; i < HW_TIMER_CHANNELS; i++) { __timer_channel_start (timer, &timer->channels[i]); } irq_restore (state); } void IRAM timer_stop(tim_t dev) { DEBUG("%s dev=%u\n", __func__, dev); CHECK_PARAM (dev < HW_TIMER_NUMOF); int state = irq_disable (); struct hw_timer_t* timer = &timers[dev]; timer->started = false; for (int i = 0; i < HW_TIMER_CHANNELS; i++) { __timer_channel_stop (timer, &timer->channels[i]); } irq_restore (state); } static void IRAM __timer_channel_start (struct hw_timer_t* timer, struct hw_channel_t* channel) { if (!timer->started || !channel->used) { return; } /* save channel starting time */ channel->start_time = timer_read (0); channel->cycles = channel->delta_time >> HW_TIMER_DELTA_RSHIFT; channel->remainder = channel->delta_time & HW_TIMER_DELTA_MASK; DEBUG("%s cycles=%u remainder=%u @%u\n", __func__, channel->cycles, channel->remainder, system_get_time()); /* start timer either with full cycles, remaining or minimum time */ if (channel->cycles) { channel->cycles--; xthal_set_ccompare(timer->dev, xthal_get_ccount() + __hw_timer_ticks_max); } else if (channel->remainder > HW_TIMER_DELTA_MIN) { xthal_set_ccompare(timer->dev, xthal_get_ccount() + US_TO_HW_TIMER_TICKS(channel->remainder)); channel->remainder = 0; } else { channel->remainder = 0; xthal_set_ccompare(timer->dev, xthal_get_ccount() + __hw_timer_ticks_min); } xt_ints_on(BIT(timers_int[timer->dev])); } static void IRAM __timer_channel_stop (struct hw_timer_t* timer, struct hw_channel_t* channel) { if (!channel->used) { return; } xt_ints_off(BIT(timers_int[timer->dev])); /* compute elapsed time */ uint32_t elapsed_time = timer_read (0) - channel->start_time; if (channel->delta_time > elapsed_time) { /* compute new delta time if the timer has no been expired */ channel->delta_time -= elapsed_time; } else { /* otherwise deactivate the channel */ channel->used = false; } } #endif /* MODULE_ESP_HW_COUNTER */