diff --git a/cpu/sam3/include/periph_cpu.h b/cpu/sam3/include/periph_cpu.h index 6a2d26edb2..2208afe2ba 100644 --- a/cpu/sam3/include/periph_cpu.h +++ b/cpu/sam3/include/periph_cpu.h @@ -60,6 +60,16 @@ typedef uint32_t gpio_t; */ #define CPUID_LEN (16U) +/** + * @brief All SAM3 timers are 32-bit wide + */ +#define TIMER_MAX_VAL (0xffffffff) + +/** + * @brief We use 3 channels for each defined timer + */ +#define TIMER_CHANNELS (3) + /** * @brief Override values for pull register configuration * @{ @@ -102,6 +112,14 @@ typedef enum { GPIO_MUX_B = 1, /**< alternate function B */ } gpio_mux_t; +/** + * @brief Timer configuration data + */ +typedef struct { + Tc *dev; /**< timer device */ + uint8_t id_ch0; /**< ID of the timer's first channel */ +} timer_conf_t; + /** * @brief UART configuration data */ diff --git a/cpu/sam3/periph/timer.c b/cpu/sam3/periph/timer.c index 600a709d7f..52e2147853 100644 --- a/cpu/sam3/periph/timer.c +++ b/cpu/sam3/periph/timer.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014-2016 Freie Universität Berlin * * 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 @@ -7,11 +7,11 @@ */ /** - * @ingroup driver_periph + * @ingroup cpu_sam3 * @{ * * @file - * @brief Low-level timer driver implementation for the SAM3X8E CPU + * @brief Low-level timer driver implementation * * @author Hauke Petersen * @@ -28,473 +28,186 @@ #include "periph/timer.h" #include "periph_conf.h" - -typedef struct { - void (*cb)(int); -} timer_conf_t; - /** - * @brief Timer state memory + * @brief Memory to store the interrupt context */ -static timer_conf_t timer_config[TIMER_NUMOF]; - +static timer_isr_ctx_t isr_ctx[TIMER_NUMOF]; /** - * @brief Setup the given timer + * @brief Enable the clock for the selected timer channels + */ +static inline void clk_en(tim_t tim) +{ + uint8_t id = timer_config[tim].id_ch0; + + if (id < 32) { + PMC->PMC_PCER0 = ((1 << id) | (1 << (id + 1))); + } else { + id -= 32; + PMC->PMC_PCER1 = ((1 << id) | (1 << (id + 1))); + } +} + +/** + * @brief Get the timer ID from the timer's base address + */ +static inline Tc *dev(tim_t tim) +{ + return timer_config[tim].dev; +} + +/** + * @brief Setup the given timer * - * The SAM3X8E has 3 timers. Each timer has 3 independent channels. - * RIOT uses the timers in WAVE mode with the following clock chaining: + * The SAM3X8E has 3 timers build of 3 independent channels. Each of these + * channels has 3 capture compare outputs (A-C). + * + * RIOT uses the 2 of the channels in WAVE mode with the following clock + * chaining: * * ---------- ---------- * | | | |-> IRQ-compareA - * | TCx[2] | ---- TIOA2 --->| TCx[0] |-> IRQ-compareB - * | | | | |-> IRQ-compareC - * ---------- | ---------- - * ^ | - * | | ---------- - * | | | |-> IRQ-compareA - * TIMER_CLOCK1 ------->| TCx[1] |-> IRQ-compareB - * | |-> IRQ-compareC - * ---------- + * | TCx[1] | ---- TIOA1 --->| TCx[0] |-> IRQ-compareB + * | | | |-> IRQ-compareC + * ---------- ---------- + * ^ + * TIMER_CLOCK1 * - * For each timer, channel 0 is used to implement a prescaler. Channel 0 is + * For each timer, channel 1 is used to implement a prescaler. Channel 1 is * driven by the MCK / 2 (42MHz) (TIMER_CLOCK1). */ -int timer_init(tim_t dev, unsigned long freq, void (*callback)(int)) +int timer_init(tim_t tim, unsigned long freq, void (*callback)(int)) { - Tc *tim; - - /* select the timer and enable the timer specific peripheral clocks */ - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - tim = TIMER_0_DEV; - PMC->PMC_PCER0 = (1 << ID_TC0) | (1 << ID_TC1) | (1 << ID_TC2); - break; -#endif -#if TIMER_1_EN - case TIMER_1: - tim = TIMER_1_DEV; - PMC->PMC_PCER0 = (1 << ID_TC3) | (1 << ID_TC4); - PMC->PMC_PCER1 = (1 << (ID_TC5 - 32)); - break; -#endif -#if TIMER_2_EN - case TIMER_2: - tim = TIMER_2_DEV; - PMC->PMC_PCER1 = (1 << (ID_TC6 - 32)) | (1 << (ID_TC7 - 32)) | (1 << (ID_TC8 - 32)); - break; -#endif - case TIMER_UNDEFINED: - default: - return -1; + /* check if device is valid */ + if (tim >= TIMER_NUMOF) { + return -1; } + /* enable the device clock */ + clk_en(tim); + /* save callback */ - timer_config[dev].cb = callback; + isr_ctx[tim].cb = callback; - /* configure the timer block by connecting TIOA2 to XC0 and XC1 */ - tim->TC_BMR = TC_BMR_TC0XC0S_TIOA2 | TC_BMR_TC1XC1S_TIOA2; + /* configure the timer block by connecting TIOA1 to XC0 */ + dev(tim)->TC_BMR = TC_BMR_TC0XC0S_TIOA1; + /* configure and enable channel 0 to use XC0 as input */ + dev(tim)->TC_CHANNEL[0].TC_CMR = (TC_CMR_TCCLKS_XC0 | + TC_CMR_WAVE | TC_CMR_EEVT_XC0); + dev(tim)->TC_CHANNEL[0].TC_CCR = (TC_CCR_CLKEN | TC_CCR_SWTRG); - /* configure and enable channels 0 and 1 to use XC0 and XC1 as input */ - tim->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_XC0 | TC_CMR_WAVE | TC_CMR_EEVT_XC0; - tim->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_XC1 | TC_CMR_WAVE | TC_CMR_EEVT_XC0; - tim->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; /* and start */ - tim->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; /* and start */ - - /* configure channel 2: + /* configure channel 1: * - select wave mode * - set input clock to TIMER_CLOCK1 (MCK/2) * - reload on TC_CV == TC_RC * - let TIOA2 signal be toggled when TC_CV == TC_RC */ - tim->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 | TC_CMR_WAVE - | TC_CMR_WAVSEL_UP_RC | TC_CMR_ACPC_TOGGLE; + dev(tim)->TC_CHANNEL[1].TC_CMR = (TC_CMR_TCCLKS_TIMER_CLOCK1 | TC_CMR_WAVE | + TC_CMR_WAVSEL_UP_RC | TC_CMR_ACPC_TOGGLE); - /* configure the frequency of channel 2 to freq * 4 + /* configure the frequency of channel 1 to freq * 4 * - * note: as channels 0 and 1 are only incremented on rising edges of TIOA2 line and - * channel 2 toggles this line on each timer tick, the actual frequency driving ch0/1 - * is f_ch2 / 2 --> f_ch0/1 = (MCK / 2) / 2 / freq. + * note: as channel 0 is only incremented on rising edges of TIOA1 line and + * channel 1 toggles this line on each timer tick, the actual frequency + * driving channel 0 is f_ch2 / 2 --> f_ch0/1 = (MCK / 2) / 2 / freq. */ - tim->TC_CHANNEL[2].TC_RC = (CLOCK_CORECLOCK / 4) / freq; + dev(tim)->TC_CHANNEL[1].TC_R[2] = (CLOCK_CORECLOCK / 4) / freq; - /* start channel 2 */ - tim->TC_CHANNEL[2].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; + /* start channel 1 */ + dev(tim)->TC_CHANNEL[1].TC_CCR = (TC_CCR_CLKEN | TC_CCR_SWTRG); - /* enable interrupts for given timer */ - timer_irq_enable(dev); + /* enable global interrupts for given timer */ + timer_irq_enable(tim); return 0; } -int timer_set(tim_t dev, int channel, unsigned int timeout) +int timer_set(tim_t tim, int channel, unsigned int timeout) { - return timer_set_absolute(dev, channel, timer_read(dev) + timeout); + return timer_set_absolute(tim, channel, timer_read(tim) + timeout); } -int timer_set_absolute(tim_t dev, int channel, unsigned int value) +int timer_set_absolute(tim_t tim, int channel, unsigned int value) { - Tc *tim; - - /* get timer base register address */ - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - tim = TIMER_0_DEV; - break; -#endif -#if TIMER_1_EN - case TIMER_1: - tim = TIMER_1_DEV; - break; -#endif -#if TIMER_2_EN - case TIMER_2: - tim = TIMER_2_DEV; - break; -#endif - case TIMER_UNDEFINED: - default: - return -1; + if (channel >=TIMER_CHANNELS) { + return -1; } - /* set timeout value */ - switch (channel) { - case 0: - tim->TC_CHANNEL[0].TC_RA = value; - tim->TC_CHANNEL[0].TC_IER = TC_IER_CPAS; - break; - case 1: - tim->TC_CHANNEL[0].TC_RB = value; - tim->TC_CHANNEL[0].TC_IER = TC_IER_CPBS; - break; - case 2: - tim->TC_CHANNEL[0].TC_RC = value; - tim->TC_CHANNEL[0].TC_IER = TC_IER_CPCS; - break; - case 3: - tim->TC_CHANNEL[1].TC_RA = value; - tim->TC_CHANNEL[1].TC_IER = TC_IER_CPAS; - break; - case 4: - tim->TC_CHANNEL[1].TC_RB = value; - tim->TC_CHANNEL[1].TC_IER = TC_IER_CPBS; - break; - case 5: - tim->TC_CHANNEL[1].TC_RC = value; - tim->TC_CHANNEL[1].TC_IER = TC_IER_CPCS; - break; - default: - return -1; + dev(tim)->TC_CHANNEL[0].TC_R[channel] = value; + dev(tim)->TC_CHANNEL[0].TC_IER = (TC_IER_CPAS << channel); + + return 0; +} + +int timer_clear(tim_t tim, int channel) +{ + if (channel >= TIMER_CHANNELS) { + return -1; } + dev(tim)->TC_CHANNEL[0].TC_IDR = (TC_IDR_CPAS << channel); + return 1; } -int timer_clear(tim_t dev, int channel) +unsigned int timer_read(tim_t tim) { - Tc *tim; - - /* get timer base register address */ - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - tim = TIMER_0_DEV; - break; -#endif -#if TIMER_1_EN - case TIMER_1: - tim = TIMER_1_DEV; - break; -#endif -#if TIMER_2_EN - case TIMER_2: - tim = TIMER_2_DEV; - break; -#endif - case TIMER_UNDEFINED: - default: - return -1; - } - - /* disable the channels interrupt */ - switch (channel) { - case 0: - tim->TC_CHANNEL[0].TC_IDR = TC_IDR_CPAS; - break; - case 1: - tim->TC_CHANNEL[0].TC_IDR = TC_IDR_CPBS; - break; - case 2: - tim->TC_CHANNEL[0].TC_IDR = TC_IDR_CPCS; - break; - case 3: - tim->TC_CHANNEL[1].TC_IDR = TC_IDR_CPAS; - break; - case 4: - tim->TC_CHANNEL[1].TC_IDR = TC_IDR_CPBS; - break; - case 5: - tim->TC_CHANNEL[1].TC_IDR = TC_IDR_CPCS; - break; - default: - return -1; - } - - return 1; + return dev(tim)->TC_CHANNEL[0].TC_CV; } -/* - * The timer channels 1 and 2 are configured to run with the same speed and - * have the same value (they run in parallel), so only on of them is returned. - */ -unsigned int timer_read(tim_t dev) +void timer_start(tim_t tim) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - return TIMER_0_DEV->TC_CHANNEL[0].TC_CV; -#endif -#if TIMER_1_EN - case TIMER_1: - return TIMER_1_DEV->TC_CHANNEL[0].TC_CV; -#endif -#if TIMER_2_EN - case TIMER_2: - return TIMER_2_DEV->TC_CHANNEL[0].TC_CV; -#endif - case TIMER_UNDEFINED: - default: - return 0; - } + dev(tim)->TC_CHANNEL[1].TC_CCR = (TC_CCR_CLKEN | TC_CCR_SWTRG); } -/* - * For stopping the counting of channels 1 + 2, channel 0 is disabled. - */ -void timer_stop(tim_t dev) +void timer_stop(tim_t tim) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - TIMER_0_DEV->TC_CHANNEL[2].TC_CCR = TC_CCR_CLKDIS; - break; -#endif -#if TIMER_1_EN - case TIMER_1: - TIMER_1_DEV->TC_CHANNEL[2].TC_CCR = TC_CCR_CLKDIS; - break; -#endif -#if TIMER_2_EN - case TIMER_2: - TIMER_2_DEV->TC_CHANNEL[2].TC_CCR = TC_CCR_CLKDIS; - break; -#endif - case TIMER_UNDEFINED: - break; - } + dev(tim)->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKDIS; } -void timer_start(tim_t dev) +void timer_irq_enable(tim_t tim) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - TIMER_0_DEV->TC_CHANNEL[2].TC_CCR = TC_CCR_CLKEN; - break; -#endif -#if TIMER_1_EN - case TIMER_1: - TIMER_1_DEV->TC_CHANNEL[2].TC_CCR = TC_CCR_CLKEN; - break; -#endif -#if TIMER_2_EN - case TIMER_2: - TIMER_2_DEV->TC_CHANNEL[2].TC_CCR = TC_CCR_CLKEN; - break; -#endif - case TIMER_UNDEFINED: - break; - } + NVIC_EnableIRQ(timer_config[tim].id_ch0); } -void timer_irq_enable(tim_t dev) +void timer_irq_disable(tim_t tim) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - NVIC_EnableIRQ(TC0_IRQn); - NVIC_EnableIRQ(TC1_IRQn); - break; -#endif -#if TIMER_1_EN - case TIMER_1: - NVIC_EnableIRQ(TC3_IRQn); - NVIC_EnableIRQ(TC4_IRQn); - break; -#endif -#if TIMER_2_EN - case TIMER_2: - NVIC_EnableIRQ(TC6_IRQn); - NVIC_EnableIRQ(TC7_IRQn); - break; -#endif - case TIMER_UNDEFINED: - break; - } + NVIC_DisableIRQ(timer_config[tim].id_ch0); } -void timer_irq_disable(tim_t dev) +static inline void isr_handler(tim_t tim) { - switch (dev) { -#if TIMER_0_EN - case TIMER_0: - NVIC_DisableIRQ(TC0_IRQn); - NVIC_DisableIRQ(TC1_IRQn); - break; -#endif -#if TIMER_1_EN - case TIMER_1: - NVIC_DisableIRQ(TC3_IRQn); - NVIC_DisableIRQ(TC4_IRQn); - break; -#endif -#if TIMER_2_EN - case TIMER_2: - NVIC_DisableIRQ(TC6_IRQn); - NVIC_DisableIRQ(TC7_IRQn); - break; -#endif - case TIMER_UNDEFINED: - break; - } -} + uint32_t status = dev(tim)->TC_CHANNEL[0].TC_SR; -#if TIMER_0_EN -void TIMER_0_ISR1(void) -{ - uint32_t status = TIMER_0_DEV->TC_CHANNEL[0].TC_SR; - if (status & TC_SR_CPAS) { - TIMER_0_DEV->TC_CHANNEL[0].TC_IDR = TC_IDR_CPAS; - timer_config[TIMER_0].cb(0); - } - else if (status & TC_SR_CPBS) { - TIMER_0_DEV->TC_CHANNEL[0].TC_IDR = TC_IDR_CPBS; - timer_config[TIMER_0].cb(1); - } - else if (status & TC_SR_CPCS) { - TIMER_0_DEV->TC_CHANNEL[0].TC_IDR = TC_IDR_CPCS; - timer_config[TIMER_0].cb(2); + for (int i = 0; i < TIMER_CHANNELS; i++) { + if (status & (TC_SR_CPAS << i)) { + dev(tim)->TC_CHANNEL[0].TC_IDR = (TC_IDR_CPAS << i); + isr_ctx[tim].cb(i); + } } + if (sched_context_switch_request) { thread_yield(); } } -void TIMER_0_ISR2(void) +#ifdef TIMER_0_ISR +void TIMER_0_ISR(void) { - uint32_t status = TIMER_0_DEV->TC_CHANNEL[1].TC_SR; - if (status & TC_SR_CPAS) { - TIMER_0_DEV->TC_CHANNEL[1].TC_IDR = TC_IDR_CPAS; - timer_config[TIMER_0].cb(3); - } - else if (status & TC_SR_CPBS) { - TIMER_0_DEV->TC_CHANNEL[1].TC_IDR = TC_IDR_CPBS; - timer_config[TIMER_0].cb(4); - } - else if (status & TC_SR_CPCS) { - TIMER_0_DEV->TC_CHANNEL[1].TC_IDR = TC_IDR_CPCS; - timer_config[TIMER_0].cb(5); - } - if (sched_context_switch_request) { - thread_yield(); - } + isr_handler(0); } -#endif /* TIMER_0_EN */ +#endif - -#if TIMER_1_EN -void TIMER_1_ISR1(void) +#ifdef TIMER_1_ISR +void TIMER_1_ISR(void) { - uint32_t status = TIMER_1_DEV->TC_CHANNEL[0].TC_SR; - if (status & TC_SR_CPAS) { - TIMER_1_DEV->TC_CHANNEL[0].TC_IDR = TC_IDR_CPAS; - timer_config[TIMER_1].cb(0); - } - if (status & TC_SR_CPBS) { - TIMER_1_DEV->TC_CHANNEL[0].TC_IDR = TC_IDR_CPBS; - timer_config[TIMER_1].cb(1); - } - if (status & TC_SR_CPCS) { - TIMER_1_DEV->TC_CHANNEL[0].TC_IDR = TC_IDR_CPCS; - timer_config[TIMER_1].cb(2); - } - if (sched_context_switch_request) { - thread_yield(); - } + isr_handler(1); } +#endif -void TIMER_1_ISR2(void) +#ifdef TIMER_2_ISR +void TIMER_2_ISR(void) { - uint32_t status = TIMER_1_DEV->TC_CHANNEL[1].TC_SR; - if (status & TC_SR_CPAS) { - TIMER_1_DEV->TC_CHANNEL[1].TC_IDR = TC_IDR_CPAS; - timer_config[TIMER_1].cb(3); - } - if (status & TC_SR_CPBS) { - TIMER_1_DEV->TC_CHANNEL[1].TC_IDR = TC_IDR_CPBS; - timer_config[TIMER_1].cb(4); - } - if (status & TC_SR_CPCS) { - TIMER_1_DEV->TC_CHANNEL[1].TC_IDR = TC_IDR_CPCS; - timer_config[TIMER_1].cb(5); - } - if (sched_context_switch_request) { - thread_yield(); - } + isr_handler(2); } -#endif /* TIMER_1_EN */ - - -#if TIMER_2_EN -void TIMER_2_ISR1(void) -{ - uint32_t status = TIMER_2_DEV->TC_CHANNEL[0].TC_SR; - if (status & TC_SR_CPAS) { - TIMER_2_DEV->TC_CHANNEL[0].TC_IDR = TC_IDR_CPAS; - timer_config[TIMER_2].cb(0); - } - else if (status & TC_SR_CPBS) { - TIMER_2_DEV->TC_CHANNEL[0].TC_IDR = TC_IDR_CPBS; - timer_config[TIMER_2].cb(1); - } - else if (status & TC_SR_CPCS) { - TIMER_2_DEV->TC_CHANNEL[0].TC_IDR = TC_IDR_CPCS; - timer_config[TIMER_2].cb(2); - } - if (sched_context_switch_request) { - thread_yield(); - } -} - -void TIMER_2_ISR2(void) -{ - uint32_t status = TIMER_2_DEV->TC_CHANNEL[1].TC_SR; - if (status & TC_SR_CPAS) { - TIMER_2_DEV->TC_CHANNEL[1].TC_IDR = TC_IDR_CPAS; - timer_config[TIMER_2].cb(3); - } - else if (status & TC_SR_CPBS) { - TIMER_2_DEV->TC_CHANNEL[1].TC_IDR = TC_IDR_CPBS; - timer_config[TIMER_2].cb(4); - } - else if (status & TC_SR_CPCS) { - TIMER_2_DEV->TC_CHANNEL[1].TC_IDR = TC_IDR_CPCS; - timer_config[TIMER_2].cb(5); - } - if (sched_context_switch_request) { - thread_yield(); - } -} -#endif /* TIMER_2_EN */ +#endif