mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 14:33:52 +01:00
cpu, cc2538: cleanup periph/timer
This commit is contained in:
parent
768459d6c1
commit
12868d2416
@ -162,6 +162,11 @@ typedef struct {
|
||||
cc2538_reg_t RESERVED4[15]; /**< Reserved */
|
||||
} cc2538_gptimer_t;
|
||||
|
||||
/**
|
||||
* @brief Base address of general-purpose timers (GPT)
|
||||
*/
|
||||
#define GPTIMER_BASE (0x40030000)
|
||||
|
||||
#define GPTIMER0 ( (cc2538_gptimer_t*)0x40030000 ) /**< GPTIMER0 Instance */
|
||||
#define GPTIMER1 ( (cc2538_gptimer_t*)0x40031000 ) /**< GPTIMER1 Instance */
|
||||
#define GPTIMER2 ( (cc2538_gptimer_t*)0x40032000 ) /**< GPTIMER2 Instance */
|
||||
|
||||
@ -233,12 +233,14 @@ typedef struct {
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Timer configuration data
|
||||
* @brief Timer configuration
|
||||
*
|
||||
* General purpose timers (GPT[0-3]) are configured consecutively and in order
|
||||
* (without gaps) starting from GPT0, i.e. if multiple timers are enabled.
|
||||
*/
|
||||
typedef struct {
|
||||
cc2538_gptimer_t *dev; /**< timer device */
|
||||
uint_fast8_t channels; /**< number of channels */
|
||||
uint_fast8_t cfg; /**< timer config word */
|
||||
uint_fast8_t chn; /**< number of channels */
|
||||
uint_fast8_t cfg; /**< timer config word */
|
||||
} timer_conf_t;
|
||||
|
||||
/**
|
||||
|
||||
@ -30,10 +30,10 @@
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define LOAD_VALUE 0xffff
|
||||
#define LOAD_VALUE (0xffff)
|
||||
|
||||
#define TIMER_A_IRQ_MASK 0x000000ff
|
||||
#define TIMER_B_IRQ_MASK 0x0000ff00
|
||||
#define TIMER_A_IRQ_MASK (0x000000ff)
|
||||
#define TIMER_B_IRQ_MASK (0x0000ff00)
|
||||
|
||||
#define BIT(n) ( 1UL << (n) )
|
||||
|
||||
@ -49,60 +49,84 @@
|
||||
#define TBMIM BIT(11)
|
||||
#define TAMIM BIT(4)
|
||||
|
||||
/* Convert a gptimer instance pointer to a GPTimer number */
|
||||
#define GPTIMER_GET_NUM(gptimer) ( ((uintptr_t)(gptimer) >> 12) & 0x3 )
|
||||
typedef struct {
|
||||
uint16_t mask;
|
||||
uint16_t flag;
|
||||
} _isr_cfg_t;
|
||||
|
||||
#define match_bit(chan) ( (chan)? TBMIM : TAMIM )
|
||||
static const _isr_cfg_t chn_isr_cfg[] = {
|
||||
{ .mask = TIMER_A_IRQ_MASK, .flag = TAMIM },
|
||||
{ .mask = TIMER_B_IRQ_MASK, .flag = TBMIM }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Timer state memory
|
||||
*/
|
||||
static timer_isr_ctx_t config[GPTIMER_NUMOF];
|
||||
|
||||
static const int IRQn_lut[GPTIMER_NUMOF] = {
|
||||
static const int irqn_cfg[] = {
|
||||
GPTIMER_0A_IRQn,
|
||||
GPTIMER_1A_IRQn,
|
||||
GPTIMER_2A_IRQn,
|
||||
GPTIMER_3A_IRQn
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Timer state memory
|
||||
*/
|
||||
static timer_isr_ctx_t isr_ctx[TIMER_NUMOF];
|
||||
|
||||
/* enable timer interrupts */
|
||||
static inline void _irq_enable(tim_t dev);
|
||||
static inline void _irq_enable(tim_t tim)
|
||||
{
|
||||
DEBUG("%s(%u)\n", __FUNCTION__, tim);
|
||||
|
||||
if (tim < TIMER_NUMOF) {
|
||||
IRQn_Type irqn = irqn_cfg[tim];
|
||||
|
||||
NVIC_SetPriority(irqn, TIMER_IRQ_PRIO);
|
||||
NVIC_EnableIRQ(irqn);
|
||||
|
||||
if (timer_config[tim].chn == 2) {
|
||||
irqn++;
|
||||
NVIC_SetPriority(irqn, TIMER_IRQ_PRIO);
|
||||
NVIC_EnableIRQ(irqn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline cc2538_gptimer_t *dev(tim_t tim)
|
||||
{
|
||||
assert(tim < TIMER_NUMOF);
|
||||
|
||||
return ((cc2538_gptimer_t *)(GPTIMER_BASE | (((uint32_t)tim) << 12)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup the given timer
|
||||
*
|
||||
*/
|
||||
int timer_init(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg)
|
||||
int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
|
||||
{
|
||||
cc2538_gptimer_t *gptimer = timer_config[dev].dev;
|
||||
unsigned int gptimer_num;
|
||||
uint32_t chan_mode;
|
||||
DEBUG("%s(%u, %lu, %p, %p)\n", __FUNCTION__, tim, freq, cb, arg);
|
||||
|
||||
DEBUG("%s(%u, %lu, %p, %p)\n", __FUNCTION__, dev, freq, cb, arg);
|
||||
|
||||
if (dev >= TIMER_NUMOF) {
|
||||
if (tim >= TIMER_NUMOF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
gptimer_num = GPTIMER_GET_NUM(gptimer);
|
||||
|
||||
/* Save the callback function: */
|
||||
assert(gptimer_num < GPTIMER_NUMOF);
|
||||
config[gptimer_num].cb = cb;
|
||||
config[gptimer_num].arg = arg;
|
||||
assert(tim < TIMER_NUMOF);
|
||||
isr_ctx[tim].cb = cb;
|
||||
isr_ctx[tim].arg = arg;
|
||||
|
||||
/* Enable the clock for this timer: */
|
||||
SYS_CTRL_RCGCGPT |= (1 << gptimer_num);
|
||||
SYS_CTRL_RCGCGPT |= (1 << tim);
|
||||
|
||||
/* Disable this timer before configuring it: */
|
||||
gptimer->cc2538_gptimer_ctl.CTL = 0;
|
||||
dev(tim)->cc2538_gptimer_ctl.CTL = 0;
|
||||
|
||||
if (timer_config[dev].cfg == GPTMCFG_32_BIT_TIMER) {
|
||||
uint32_t prescaler = 0;
|
||||
uint32_t chan_mode = TnCMIE | GPTIMER_PERIODIC_MODE;
|
||||
if (timer_config[tim].cfg == GPTMCFG_32_BIT_TIMER) {
|
||||
/* Count up in periodic mode */
|
||||
chan_mode = TnCMIE | TnCDIR | GPTIMER_PERIODIC_MODE;
|
||||
chan_mode |= TnCDIR ;
|
||||
|
||||
if (timer_config[dev].channels > 1) {
|
||||
if (timer_config[tim].chn > 1) {
|
||||
DEBUG("Invalid timer_config. Multiple channels are available only in 16-bit mode.");
|
||||
return -1;
|
||||
}
|
||||
@ -111,89 +135,70 @@ int timer_init(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg)
|
||||
DEBUG("In 32-bit mode, the GPTimer frequency must equal the system clock frequency (%u).", sys_clock_freq());
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
/* Count down in periodic mode */
|
||||
chan_mode = TnCMIE | GPTIMER_PERIODIC_MODE;
|
||||
}
|
||||
else if (timer_config[tim].cfg == GPTMCFG_16_BIT_TIMER) {
|
||||
prescaler = sys_clock_freq();
|
||||
prescaler += freq / 2;
|
||||
prescaler /= freq;
|
||||
if (prescaler > 0) prescaler--;
|
||||
if (prescaler > 255) prescaler = 255;
|
||||
|
||||
dev(tim)->TAPR = prescaler;
|
||||
dev(tim)->TAILR = LOAD_VALUE;
|
||||
}
|
||||
else {
|
||||
DEBUG("timer_init: invalid timer config must be 16 or 32Bit mode!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gptimer->CFG = timer_config[dev].cfg;
|
||||
gptimer->cc2538_gptimer_tamr.TAMR = chan_mode;
|
||||
dev(tim)->CFG = timer_config[tim].cfg;
|
||||
dev(tim)->cc2538_gptimer_ctl.CTL = TAEN;
|
||||
dev(tim)->cc2538_gptimer_tamr.TAMR = chan_mode;
|
||||
|
||||
switch (timer_config[dev].channels) {
|
||||
case 1:
|
||||
/* Enable the timer: */
|
||||
gptimer->cc2538_gptimer_ctl.CTL = TAEN;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
gptimer->cc2538_gptimer_tbmr.TBMR = chan_mode;
|
||||
|
||||
gptimer->TAILR = LOAD_VALUE;
|
||||
gptimer->TBILR = LOAD_VALUE;
|
||||
|
||||
uint32_t prescaler = sys_clock_freq();
|
||||
prescaler += freq / 2;
|
||||
prescaler /= freq;
|
||||
if (prescaler > 0) prescaler--;
|
||||
if (prescaler > 255) prescaler = 255;
|
||||
|
||||
gptimer->TAPR = prescaler;
|
||||
gptimer->TBPR = prescaler;
|
||||
|
||||
/* Enable the timer: */
|
||||
gptimer->cc2538_gptimer_ctl.CTL = TBEN | TAEN;
|
||||
break;
|
||||
if (timer_config[tim].chn > 1) {
|
||||
dev(tim)->cc2538_gptimer_tbmr.TBMR = chan_mode;
|
||||
dev(tim)->TBPR = prescaler;
|
||||
dev(tim)->TBILR = LOAD_VALUE;
|
||||
/* Enable the timer: */
|
||||
dev(tim)->cc2538_gptimer_ctl.CTL = TBEN | TAEN;
|
||||
}
|
||||
|
||||
/* Enable interrupts for given timer: */
|
||||
_irq_enable(dev);
|
||||
_irq_enable(tim);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timer_set_absolute(tim_t dev, int channel, unsigned int value)
|
||||
int timer_set_absolute(tim_t tim, int channel, unsigned int value)
|
||||
{
|
||||
DEBUG("%s(%u, %u, %u)\n", __FUNCTION__, dev, channel, value);
|
||||
DEBUG("%s(%u, %u, %u)\n", __FUNCTION__, tim, channel, value);
|
||||
|
||||
/* get timer base register address */
|
||||
cc2538_gptimer_t *gptimer = timer_config[dev].dev;
|
||||
|
||||
if ( (dev >= TIMER_NUMOF) || (channel >= timer_config[dev].channels) ) {
|
||||
if ((tim >= TIMER_NUMOF) || (channel >= timer_config[tim].chn) ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (channel) {
|
||||
case 0:
|
||||
/* clear any pending match interrupts */
|
||||
gptimer->ICR = TAMIM;
|
||||
|
||||
gptimer->TAMATCHR = (gptimer->CFG == GPTMCFG_32_BIT_TIMER)? value : (LOAD_VALUE - value);
|
||||
gptimer->cc2538_gptimer_imr.IMR |= TAMIM; /**< Enable the Timer A Match Interrupt */
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* clear any pending match interrupts */
|
||||
gptimer->ICR = TBMIM;
|
||||
|
||||
gptimer->TBMATCHR = (gptimer->CFG == GPTMCFG_32_BIT_TIMER)? value : (LOAD_VALUE - value);
|
||||
gptimer->cc2538_gptimer_imr.IMR |= TBMIM; /**< Enable the Timer B Match Interrupt */
|
||||
break;
|
||||
/* clear any pending match interrupts */
|
||||
dev(tim)->ICR = chn_isr_cfg[channel].flag;
|
||||
if (channel == 0) {
|
||||
dev(tim)->TAMATCHR = (timer_config[tim].cfg == GPTMCFG_32_BIT_TIMER) ?
|
||||
value : (LOAD_VALUE - value);
|
||||
}
|
||||
|
||||
/* set timeout value */
|
||||
else {
|
||||
dev(tim)->TBMATCHR = (LOAD_VALUE - value);
|
||||
}
|
||||
dev(tim)->cc2538_gptimer_imr.IMR |= chn_isr_cfg[channel].flag;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int timer_clear(tim_t dev, int channel)
|
||||
int timer_clear(tim_t tim, int channel)
|
||||
{
|
||||
DEBUG("%s(%u, %u)\n", __FUNCTION__, dev, channel);
|
||||
DEBUG("%s(%u, %u)\n", __FUNCTION__, tim, channel);
|
||||
|
||||
if ( (dev >= TIMER_NUMOF) || (channel >= timer_config[dev].channels) ) {
|
||||
if ( (tim >= TIMER_NUMOF) || (channel >= timer_config[tim].chn) ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
timer_config[dev].dev->cc2538_gptimer_imr.IMR &= ~match_bit(channel);
|
||||
/* clear interupt flags */
|
||||
dev(tim)->cc2538_gptimer_imr.IMR &= ~(chn_isr_cfg[channel].flag);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -202,121 +207,81 @@ int timer_clear(tim_t dev, int channel)
|
||||
* 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)
|
||||
unsigned int timer_read(tim_t tim)
|
||||
{
|
||||
if (dev >= TIMER_NUMOF) {
|
||||
DEBUG("%s(%u)\n", __FUNCTION__, tim);
|
||||
|
||||
if (tim >= TIMER_NUMOF) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cc2538_gptimer_t* gptimer = timer_config[dev].dev;
|
||||
if (gptimer->CFG == GPTMCFG_32_BIT_TIMER) {
|
||||
return gptimer->TAV;
|
||||
if (timer_config[tim].cfg == GPTMCFG_32_BIT_TIMER) {
|
||||
return dev(tim)->TAV;
|
||||
}
|
||||
else {
|
||||
return LOAD_VALUE - (gptimer->TAV & 0xffff);
|
||||
return LOAD_VALUE - (dev(tim)->TAV & 0xffff);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For stopping the counting of all channels.
|
||||
*/
|
||||
void timer_stop(tim_t dev)
|
||||
void timer_stop(tim_t tim)
|
||||
{
|
||||
DEBUG("%s(%u)\n", __FUNCTION__, dev);
|
||||
DEBUG("%s(%u)\n", __FUNCTION__, tim);
|
||||
|
||||
if (dev < TIMER_NUMOF) {
|
||||
timer_config[dev].dev->cc2538_gptimer_ctl.CTL = 0;
|
||||
if (tim < TIMER_NUMOF) {
|
||||
dev(tim)->cc2538_gptimer_ctl.CTL = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void timer_start(tim_t dev)
|
||||
void timer_start(tim_t tim)
|
||||
{
|
||||
DEBUG("%s(%u)\n", __FUNCTION__, dev);
|
||||
DEBUG("%s(%u)\n", __FUNCTION__, tim);
|
||||
|
||||
if (dev < TIMER_NUMOF) {
|
||||
switch (timer_config[dev].channels) {
|
||||
case 1:
|
||||
timer_config[dev].dev->cc2538_gptimer_ctl.CTL = TAEN;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
timer_config[dev].dev->cc2538_gptimer_ctl.CTL = TBEN | TAEN;
|
||||
break;
|
||||
if (tim < TIMER_NUMOF) {
|
||||
if (timer_config[tim].chn == 1) {
|
||||
dev(tim)->cc2538_gptimer_ctl.CTL = TAEN;
|
||||
}
|
||||
else if (timer_config[tim].chn == 2) {
|
||||
dev(tim)->cc2538_gptimer_ctl.CTL = TBEN | TAEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _irq_enable(tim_t dev)
|
||||
/**
|
||||
* @brief timer interrupt handler
|
||||
*
|
||||
* @param[in] num GPT instance number
|
||||
* @param[in] chn channel number (0=A, 1=B)
|
||||
*/
|
||||
static void irq_handler(tim_t tim, int channel)
|
||||
{
|
||||
DEBUG("%s(%u)\n", __FUNCTION__, dev);
|
||||
DEBUG("%s(%u,%d)\n", __FUNCTION__, tim, channel);
|
||||
assert(tim < TIMER_NUMOF);
|
||||
assert(channel < timer_config[tim].chn);
|
||||
|
||||
if (dev < TIMER_NUMOF) {
|
||||
IRQn_Type irqn = IRQn_lut[GPTIMER_GET_NUM(timer_config[dev].dev)];
|
||||
uint32_t mis;
|
||||
/* Latch the active interrupt flags */
|
||||
mis = dev(tim)->MIS & chn_isr_cfg[channel].mask;
|
||||
/* Clear the latched interrupt flags */
|
||||
dev(tim)->ICR = mis;
|
||||
|
||||
NVIC_SetPriority(irqn, TIMER_IRQ_PRIO);
|
||||
NVIC_EnableIRQ(irqn);
|
||||
|
||||
if (timer_config[dev].channels == 2) {
|
||||
irqn++;
|
||||
NVIC_SetPriority(irqn, TIMER_IRQ_PRIO);
|
||||
NVIC_EnableIRQ(irqn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static cc2538_gptimer_t* GPTIMER = GPTIMER0;
|
||||
|
||||
static void irq_handler_a(int n) {
|
||||
uint32_t mis;
|
||||
|
||||
/* Latch the active interrupt flags */
|
||||
mis = GPTIMER[n].MIS & TIMER_A_IRQ_MASK;
|
||||
|
||||
/* Clear the latched interrupt flags */
|
||||
GPTIMER[n].ICR = mis;
|
||||
|
||||
if (mis & TAMIM) {
|
||||
/* This is a Timer A Match Interrupt */
|
||||
|
||||
/* Disable further match interrupts for this timer/channel */
|
||||
GPTIMER[n].cc2538_gptimer_imr.IMR &= ~TAMIM;
|
||||
|
||||
/* Invoke the callback function */
|
||||
assert(config[n].cb != NULL);
|
||||
config[n].cb(config[n].arg, 0);
|
||||
}
|
||||
if (mis & chn_isr_cfg[channel].flag) {
|
||||
/* Disable further match interrupts for this timer/channel */
|
||||
dev(tim)->cc2538_gptimer_imr.IMR &= ~chn_isr_cfg[channel].flag;
|
||||
/* Invoke the callback function */
|
||||
isr_ctx[tim].cb(isr_ctx[tim].arg, channel);
|
||||
}
|
||||
|
||||
cortexm_isr_end();
|
||||
}
|
||||
|
||||
static void irq_handler_b(int n) {
|
||||
uint32_t mis;
|
||||
|
||||
/* Latch the active interrupt flags */
|
||||
mis = GPTIMER[n].MIS & TIMER_B_IRQ_MASK;
|
||||
|
||||
/* Clear the latched interrupt flags */
|
||||
GPTIMER[n].ICR = mis;
|
||||
|
||||
if (mis & TBMIM) {
|
||||
/* This is a Timer B Match Interrupt */
|
||||
|
||||
/* Disable further match interrupts for this timer/channel */
|
||||
GPTIMER[n].cc2538_gptimer_imr.IMR &= ~TBMIM;
|
||||
|
||||
/* Invoke the callback function */
|
||||
assert(config[n].cb != NULL);
|
||||
config[n].cb(config[n].arg, 1);
|
||||
}
|
||||
|
||||
cortexm_isr_end();
|
||||
}
|
||||
|
||||
void isr_timer0_chan0(void) {irq_handler_a(0);}
|
||||
void isr_timer0_chan1(void) {irq_handler_b(0);}
|
||||
void isr_timer1_chan0(void) {irq_handler_a(1);}
|
||||
void isr_timer1_chan1(void) {irq_handler_b(1);}
|
||||
void isr_timer2_chan0(void) {irq_handler_a(2);}
|
||||
void isr_timer2_chan1(void) {irq_handler_b(2);}
|
||||
void isr_timer3_chan0(void) {irq_handler_a(3);}
|
||||
void isr_timer3_chan1(void) {irq_handler_b(3);}
|
||||
void isr_timer0_chan0(void) {irq_handler(0, 0);}
|
||||
void isr_timer0_chan1(void) {irq_handler(0, 1);}
|
||||
void isr_timer1_chan0(void) {irq_handler(1, 0);}
|
||||
void isr_timer1_chan1(void) {irq_handler(1, 1);}
|
||||
void isr_timer2_chan0(void) {irq_handler(2, 0);}
|
||||
void isr_timer2_chan1(void) {irq_handler(2, 1);}
|
||||
void isr_timer3_chan0(void) {irq_handler(3, 0);}
|
||||
void isr_timer3_chan1(void) {irq_handler(3, 1);}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user