diff --git a/boards/ek-lm4f120xl/include/periph_conf.h b/boards/ek-lm4f120xl/include/periph_conf.h index 5756ccafe6..69d0d9c5b4 100644 --- a/boards/ek-lm4f120xl/include/periph_conf.h +++ b/boards/ek-lm4f120xl/include/periph_conf.h @@ -42,19 +42,26 @@ extern "C" { */ #define TIMER_NUMOF (2U) #define TIMER_0_EN 1 -#define TIMER_1_EN 0 +#define TIMER_1_EN 1 #define TIMER_IRQ_PRIO 1 -/* Timer 0 configuration */ +/* Timer 0 configuration + * + * WTIMER0 is a 32/64bits timer. + * We use timer_a as TIMER_0 + */ #define TIMER_0_CHANNELS 1 -#define TIMER_0_PRESCALER (39U) #define TIMER_0_MAX_VALUE (0xffffffff) #define TIMER_0_ISR isr_wtimer0a #define TIMER_0_IRQ_CHAN Timer0A_IRQn -/* Timer 1 configuration */ +/* Timer 1 configuration + * + * WTIMER1 is a 32/64bits timer. + * We use timer_a as TIMER_1 + */ + #define TIMER_1_CHANNELS 1 -#define TIMER_1_PRESCALER (39U) #define TIMER_1_MAX_VALUE (0xffffffff) #define TIMER_1_ISR isr_wtimer1a #define TIMER_1_IRQ_CHAN Timer1A_IRQn diff --git a/cpu/lm4f120/periph/timer.c b/cpu/lm4f120/periph/timer.c index 1aa896e19b..3a54ba1420 100644 --- a/cpu/lm4f120/periph/timer.c +++ b/cpu/lm4f120/periph/timer.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Rakendra Thapa * * 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 @@ -14,6 +15,7 @@ * @brief Implementation of the low-level timer driver for the LM4F120 * * @author Rakendra Thapa + * Marc Poulhiès */ #include @@ -27,126 +29,345 @@ #define ENABLE_DEBUG (0) #include "debug.h" + /* guard file in case no timers are defined */ -#if TIMER_0_EN +#if TIMER_NUMOF /** * @brief Struct holding the configuration data * @{ */ typedef struct { - void (*cb)(int); /**< timeout callback */ + void (*cb)(int); /**< timeout callback */ + unsigned int divisor; /**< software clock divisor */ } timer_conf_t; static timer_conf_t config[TIMER_NUMOF]; /**@}*/ +#include "hw_timer.h" + +/* Missing from driverlib */ +static inline unsigned long +PRIV_TimerPrescaleSnapshotGet(unsigned long ulbase, unsigned long ultimer) { + return((ultimer == TIMER_A) ? HWREG(ulbase + TIMER_O_TAPS) : + HWREG(ulbase + TIMER_O_TBPS)); +} + +static inline unsigned long long _scaled_to_ll_value(unsigned int uncorrected, unsigned int divisor) +{ + const unsigned long long scaledv = (unsigned long long) uncorrected * divisor; + return scaledv; +} + +static inline unsigned int _llvalue_to_scaled_value(unsigned long long corrected, unsigned int divisor) +{ + const unsigned long long scaledv = corrected / divisor; + return scaledv; +} + int timer_init(tim_t dev, unsigned int us_per_tick, void (*callback)(int)) { - if (dev == TIMER_0) { - config[dev].cb = callback; /* User Function */ - ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0); /* Activate Timer0 */ - WTIMER0_CTL_R &= ~0x00000001; /* Disable timer0A during setup */ - WTIMER0_CFG_R = TIMER_CFG_16_BIT; - WTIMER0_TAMR_R = TIMER_TAMR_TAMR_PERIOD; /* Configure for periodic mode */ - WTIMER0_TAPR_R = TIMER_0_PRESCALER; /* 1us timer0A */ - WTIMER0_ICR_R = 0x00000001; /* clear timer0A timeout flag */ - WTIMER0_IMR_R |= 0x00000001; /* arm timeout interrupt */ - ROM_IntPrioritySet(INT_WTIMER0A, 32); - timer_irq_enable(dev); - timer_start(dev); - DEBUG("startTimeout Value=0x%lx\n", ROM_TimerValueGet(WTIMER0_BASE, TIMER_A)); - return 1; + if (dev >= TIMER_NUMOF){ + return -1; } - return -1; + + config[dev].cb = callback; /* User Function */ + config[dev].divisor = us_per_tick * ROM_SysCtlClockGet()/1000000; + + unsigned int sysctl_timer; + unsigned int timer_base; + unsigned int timer_side = TIMER_A; + unsigned int timer_cfg = TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PERIODIC_UP | TIMER_TAMR_TAMIE; + unsigned int timer_max_val; + unsigned int timer_intbit = TIMER_TIMA_TIMEOUT | TIMER_TIMA_MATCH; + + switch(dev){ +#if TIMER_0_EN + case TIMER_0: + sysctl_timer = SYSCTL_PERIPH_WTIMER0; + timer_base = WTIMER0_BASE; + timer_max_val = TIMER_0_MAX_VALUE; + break; +#endif +#if TIMER_1_EN + case TIMER_1: + sysctl_timer = SYSCTL_PERIPH_WTIMER1; + timer_base = WTIMER1_BASE; + timer_max_val = TIMER_1_MAX_VALUE; + break; +#endif + default: + return -1; /* unreachable */ + } + + + ROM_SysCtlPeripheralEnable(sysctl_timer); + + ROM_TimerDisable(timer_base, timer_side); + ROM_TimerConfigure(timer_base, timer_cfg); + + unsigned long long lltimer_val_max = _scaled_to_ll_value(timer_max_val, config[dev].divisor); + + ROM_TimerPrescaleSet(timer_base, timer_side, lltimer_val_max >> 32); + ROM_TimerLoadSet(timer_base, timer_side, lltimer_val_max & 0xFFFFFFFF); + ROM_TimerIntClear(timer_base, timer_intbit); + + ROM_TimerIntEnable(timer_base, timer_intbit); + + timer_irq_enable(dev); + timer_start(dev); + + return 0; } int timer_set(tim_t dev, int channel, unsigned int timeout) { - if (dev == TIMER_0) { - unsigned int now = timer_read(dev); - DEBUG("timer_set now=0x%x\n",now); - DEBUG("timer_set timeout=0x%x\n", timeout); - return timer_set_absolute(dev, channel, now+timeout); + unsigned int corrected_now; + int retval; + + if (dev >= TIMER_NUMOF){ + return -1; } - return -1; + + corrected_now = timer_read(dev); + retval = timer_set_absolute(dev, channel, corrected_now+timeout); + + return retval; } int timer_set_absolute(tim_t dev, int channel, unsigned int value) { - if (dev == TIMER_0) { - WTIMER0_TAILR_R = 0x00000000 | value; /* period; Reload value */ - DEBUG("Setting timer absolute value=0x%x\n", value); - return 1; + unsigned int timer_base; + unsigned int timer_side = TIMER_A; + unsigned long long scaledv; + + if (dev >= TIMER_NUMOF){ + return -1; } - return -1; + + switch(dev){ +#if TIMER_0_EN + case TIMER_0: + timer_base = WTIMER0_BASE; + break; +#endif +#if TIMER_1_EN + case TIMER_1: + timer_base = WTIMER1_BASE; + break; +#endif + default: + return -1; /* unreachable */ + break; + } + ROM_TimerDisable(timer_base, timer_side); + + scaledv = _scaled_to_ll_value(value, config[dev].divisor); + + if (scaledv>>32){ + ROM_TimerPrescaleMatchSet(timer_base, timer_side, scaledv >> 32); + } + else { + ROM_TimerPrescaleMatchSet(timer_base, timer_side, 0); + } + + ROM_TimerMatchSet(timer_base, timer_side, (unsigned long) (scaledv & 0xFFFFFFFF)); + ROM_TimerEnable(timer_base, timer_side); + + return 1; } int timer_clear(tim_t dev, int channel) { - if (dev == TIMER_0){ - WTIMER0_ICR_R = TIMER_ICR_TATOCINT; - return 1; + unsigned int timer_intbit = TIMER_TIMA_TIMEOUT; + unsigned int timer_base; + + if (dev >= TIMER_NUMOF){ + return -1; } - return -1; + + switch(dev){ +#if TIMER_0_EN + case TIMER_0: + timer_base = WTIMER0_BASE; + break; +#endif +#if TIMER_1_EN + case TIMER_1: + timer_base = WTIMER1_BASE; + break; +#endif + default: + return -1; /* unreachable */ + break; + } + + ROM_TimerIntClear(timer_base, timer_intbit); + return 1; } unsigned int timer_read(tim_t dev) { - if (dev == TIMER_0) { - unsigned int currTimer0Val=0; - unsigned int loadTimer0Val=0; - currTimer0Val = (unsigned int)ROM_TimerValueGet(WTIMER0_BASE, TIMER_A); - loadTimer0Val = (unsigned int)ROM_TimerLoadGet(WTIMER0_BASE, TIMER_A); - DEBUG("WTIMER0_TAILR_R=0x%lx\t currTimer0Val=0x%x\t loadTimer0Val=0x%x\n", WTIMER0_TAILR_R, currTimer0Val, loadTimer0Val); - return (loadTimer0Val - currTimer0Val); + unsigned int timer_base; + unsigned int timer_side = TIMER_A; + unsigned long long high_bits, high_bits_dup; + unsigned long long low_bits; + unsigned long long total; + unsigned int scaled_value; + + if (dev >= TIMER_NUMOF){ + return -1; } - return 0; + + switch(dev){ +#if TIMER_0_EN + case TIMER_0: + timer_base = WTIMER0_BASE; + break; +#endif +#if TIMER_1_EN + case TIMER_1: + timer_base = WTIMER1_BASE; + break; +#endif + default: + return -1; /* unreachable */ + break; + } + + /* handle overflow happening between the 2 register reads */ + do { + high_bits = ((unsigned long long)PRIV_TimerPrescaleSnapshotGet(timer_base, timer_side)) << 32; + low_bits = (unsigned long long)ROM_TimerValueGet(timer_base, timer_side); + high_bits_dup = ((unsigned long long)PRIV_TimerPrescaleSnapshotGet(timer_base, timer_side)) << 32; + } while (high_bits != high_bits_dup); + + total = high_bits + low_bits; + DEBUG("Combined %lx:%lx\n", (unsigned long) (total>>32), (unsigned long) (total & 0xFFFFFFFF)); + + scaled_value = _llvalue_to_scaled_value(total, config[dev].divisor); + + return scaled_value; } void timer_start(tim_t dev) { - if (dev == TIMER_0) { - ROM_TimerEnable(WTIMER0_BASE, TIMER_A); + unsigned int timer_base; + unsigned int timer_side = TIMER_A; + + if (dev >= TIMER_NUMOF){ + return ; } + + switch(dev){ +#if TIMER_0_EN + case TIMER_0: + timer_base = WTIMER0_BASE; + break; +#endif +#if TIMER_1_EN + case TIMER_1: + timer_base = WTIMER1_BASE; + break; +#endif + default: + return; /* unreachable */ + } + + ROM_TimerEnable(timer_base, timer_side); } void timer_stop(tim_t dev) { - if (dev == TIMER_0) { - ROM_TimerDisable(WTIMER0_BASE, TIMER_A); + unsigned int timer_base; + unsigned int timer_side = TIMER_A; + + if (dev >= TIMER_NUMOF){ + return; } + + switch(dev){ +#if TIMER_0_EN + case TIMER_0: + timer_base = WTIMER0_BASE; + break; +#endif +#if TIMER_1_EN + case TIMER_1: + timer_base = WTIMER1_BASE; + break; +#endif + default: + return; /* unreachable */ + } + + ROM_TimerDisable(timer_base, timer_side); } void timer_irq_enable(tim_t dev) { - if (dev == TIMER_0) { - ROM_IntEnable(INT_WTIMER0A); - ROM_TimerIntEnable(WTIMER0_BASE, TIMER_TIMA_TIMEOUT); + unsigned int timer_intbase; + + if (dev >= TIMER_NUMOF){ + return; } + + switch(dev){ +#if TIMER_0_EN + case TIMER_0: + timer_intbase = INT_WTIMER0A; + break; +#endif +#if TIMER_1_EN + case TIMER_1: + timer_intbase = INT_WTIMER1A; + break; +#endif + default: + return; /* unreachable */ + } + + ROM_IntPrioritySet(timer_intbase, 32); + ROM_IntEnable(timer_intbase); } void timer_irq_disable(tim_t dev) { - if (dev == TIMER_0) { - ROM_IntDisable(INT_WTIMER0A); + unsigned int timer_base; + unsigned int timer_intbit = TIMER_TIMA_TIMEOUT; + unsigned int timer_intbase; + + if (dev >= TIMER_NUMOF){ + return; } + + switch(dev){ +#if TIMER_0_EN + case TIMER_0: + timer_base = WTIMER0_BASE; + timer_intbase = INT_WTIMER0A; + break; +#endif +#if TIMER_1_EN + case TIMER_1: + timer_base = WTIMER1_BASE; + timer_intbase = INT_WTIMER1A; + break; +#endif + default: + return; /* unreachable */ + } + + ROM_IntEnable(timer_intbase); + ROM_TimerIntDisable(timer_base, timer_intbit); } #if TIMER_0_EN -void isr_timer0a(void) -{ - TIMER0_ICR_R = TIMER_ICR_TATOCINT; - config[TIMER_0].cb(0); - - if (sched_context_switch_request){ - thread_yield(); - } -} void isr_wtimer0a(void) { - WTIMER0_ICR_R = TIMER_ICR_TATOCINT; - + /* Clears both IT */ + ROM_TimerIntClear(WTIMER0_BASE, TIMER_TIMA_TIMEOUT | TIMER_TIMA_MATCH); config[TIMER_0].cb(0); if (sched_context_switch_request){ thread_yield(); @@ -154,5 +375,17 @@ void isr_wtimer0a(void) } #endif /* TIMER_0_EN */ -#endif /* TIMER_0_EN */ +#if TIMER_1_EN +void isr_wtimer1a(void) +{ + ROM_TimerIntClear(WTIMER1_BASE, TIMER_TIMA_TIMEOUT | TIMER_TIMA_MATCH); + + config[TIMER_1].cb(0); + if (sched_context_switch_request){ + thread_yield(); + } +} +#endif /* TIMER_1_EN */ + +#endif /* TIMER_NUMOF */ /** @} */