Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #7335 from smlng/cpu/cc2650/periph_timer
cpu, cc26x0: fixing periph timer
  • Loading branch information
smlng committed Aug 17, 2017
2 parents fcac33e + 864e9f5 commit 2bab65c
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 71 deletions.
19 changes: 12 additions & 7 deletions boards/cc2650stk/include/periph_conf.h
Expand Up @@ -39,18 +39,23 @@ extern "C" {
*/
static const timer_conf_t timer_config[] = {
{
.dev = GPT0,
.num = 0
.cfg = GPT_CFG_16T,
.chn = 2,
},
{
.dev = GPT1,
.num = 1
.cfg = GPT_CFG_32T,
.chn = 1,
},
{
.cfg = GPT_CFG_16T,
.chn = 2,
},
{
.cfg = GPT_CFG_32T,
.chn = 1,
}
};

#define TIMER_0_ISR isr_timer0_chan0
#define TIMER_1_ISR isr_timer1_chan0

#define TIMER_NUMOF (sizeof(timer_config) / sizeof(timer_config[0]))
/** @} */

Expand Down
10 changes: 6 additions & 4 deletions cpu/cc26x0/include/periph_cpu.h
Expand Up @@ -60,12 +60,14 @@ typedef enum {
#endif /* ndef DOXYGEN */

/**
* @brief Timer configuration options
* @brief Configuration of low-level general purpose timers
*
* @note Timers *must* be configured consecutively and in order (without gaps)
* starting from GPT0, specifically if multiple timers are enabled.
*/
typedef struct {
gpt_reg_t *dev; /**< the GPT base address */
uint8_t num; /**< number of the timer */
uint8_t irqn; /**< interrupt number */
uint8_t cfg; /**< timer config [16,32 Bit] */
uint8_t chn; /**< number of channels [1,2] */
} timer_conf_t;

#ifdef __cplusplus
Expand Down
230 changes: 170 additions & 60 deletions cpu/cc26x0/periph/timer.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2016 Leon George
* Copyright (C) 2017 HAW Hamburg
*
* 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
Expand All @@ -15,146 +16,255 @@
* @brief Low-level timer driver implementation for the CC26x0
*
* @author Leon M. George <leon@georgemail.eu>
*
* @author Sebastian Meiling <s@mlng.net>
* @}
*/

#include <stdlib.h>
#include <stdio.h>

#include "assert.h"
#include "board.h"
#include "cpu.h"
#include "periph_conf.h"
#include "periph/timer.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

#define LOAD_VALUE (0xffff)

#define TIMER_A_IRQ_MASK (0x000000ff)
#define TIMER_B_IRQ_MASK (0x0000ff00)

#define TIMER_IRQ_PRIO (1U)

typedef struct {
uint16_t mask;
uint16_t flag;
} _isr_cfg_t;

static _isr_cfg_t chn_isr_cfg[] = {
{ .mask = TIMER_A_IRQ_MASK, .flag = GPT_IMR_TAMIM },
{ .mask = TIMER_B_IRQ_MASK, .flag = GPT_IMR_TBMIM }
};

/**
* @brief Allocate memory for the interrupt context
* @name function prototypes
* @{
*/
static void irq_handler(tim_t tim, int channel);
/** @} */

/**
* @brief Allocate memory for timer interrupt context(s)
*/
static timer_isr_ctx_t ctx[TIMER_NUMOF];
static timer_isr_ctx_t isr_ctx[TIMER_NUMOF];

/**
* @brief Get the GPT register base for a timer
* @brief Enable global interrupts for timer channel(s)
*
* @param[in] tim index of the timer
*/
static void _irq_enable(tim_t tim)
{
assert(tim < TIMER_NUMOF);

/* enable global timer interrupt for channel A */
IRQn_Type irqn = GPTIMER_0A_IRQN + (2 * tim);
NVIC_SetPriority(irqn, TIMER_IRQ_PRIO);
NVIC_EnableIRQ(irqn);
/* and channel B, if enabled */
if(timer_config[tim].chn == 2) {
irqn++;
NVIC_SetPriority(irqn, TIMER_IRQ_PRIO);
NVIC_EnableIRQ(irqn);
}
}

/**
* @brief Get the GPT register base for a timer
*
* @param[in] tim index of the timer
*
* @return base address
*/
static inline gpt_reg_t *dev(tim_t tim)
{
return timer_config[tim].dev;
assert(tim < TIMER_NUMOF);

return ((gpt_reg_t *)(GPT0_BASE | (((uint32_t)tim) << 12)));
}

int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
{
DEBUG("timer_init(%u, %lu)\n", tim, freq);
/* make sure given timer is valid */
if (tim >= TIMER_NUMOF) {
return -1;
}

/* enable the times clock */
PRCM->GPTCLKGR |= (1 << timer_config[tim].num);
/* enable the timer clock */
PRCM->GPTCLKGR |= (1 << tim);
PRCM->CLKLOADCTL = CLKLOADCTL_LOAD;
while (!(PRCM->CLKLOADCTL & CLKLOADCTL_LOADDONE)) {}

/* disable (and reset) timer */
dev(tim)->CTL = 0;

/* save context */
ctx[tim].cb = cb;
ctx[tim].arg = arg;
isr_ctx[tim].cb = cb;
isr_ctx[tim].arg = arg;

/* configure timer to 16-bit, periodic, up-counting */
dev(tim)->CFG = GPT_CFG_16T;
dev(tim)->TAMR = (GPT_TXMR_TXMR_PERIODIC | GPT_TXMR_TXCDIR_UP | GPT_TXMR_TXMIE);

/* set the timer speed */
dev(tim)->TAPR = (RCOSC48M_FREQ / freq) - 1;
/* enable global timer interrupt and start the timer */
NVIC_EnableIRQ(GPTIMER_0A_IRQN + (2 * timer_config[tim].num));
uint32_t chan_mode = (GPT_TXMR_TXMR_PERIODIC | GPT_TXMR_TXMIE);
uint32_t prescaler = 0;
if (timer_config[tim].cfg == GPT_CFG_32T) {
if (timer_config[tim].chn > 1) {
DEBUG("timer_init: in 32Bit mode single channel only!\n");
return -1;
}
if (freq != RCOSC48M_FREQ) {
DEBUG("timer_init: in 32Bit mode freq must equal system clock!\n");
return -1;
}
chan_mode |= GPT_TXMR_TXCDIR_UP;
}
else if (timer_config[tim].cfg == GPT_CFG_16T) {
/* prescaler only available in 16Bit mode */
prescaler = RCOSC48M_FREQ;
prescaler += freq / 2;
prescaler /= freq;
if (prescaler > 0) {
prescaler--;
}
if (prescaler > 255) {
prescaler = 255;
}
dev(tim)->TAILR = LOAD_VALUE;
dev(tim)->TAPR = prescaler;
}
else {
DEBUG("timer_init: invalid timer config must be 16 or 32Bit mode!\n");
return -1;
}
/* configure channels and start timer */
dev(tim)->CFG = timer_config[tim].cfg;
dev(tim)->CTL = GPT_CTL_TAEN;
dev(tim)->TAMR = chan_mode;

if (timer_config[tim].chn == 2) {
/* set the timer speed */
dev(tim)->TBPR = prescaler;
dev(tim)->TBMR = chan_mode;
dev(tim)->TBILR = LOAD_VALUE;
dev(tim)->CTL = GPT_CTL_TAEN | GPT_CTL_TBEN;
}
/* enable timer IRQs */
_irq_enable(tim);

return 0;
}

int timer_set_absolute(tim_t tim, int channel, unsigned int value)
{
if (channel != 0) {
DEBUG("timer_set_absolute(%u, %u, %u)\n", tim, channel, value);

if ((tim >= TIMER_NUMOF) || (channel >= timer_config[tim].chn)) {
return -1;
}

dev(tim)->TAMATCHR = value;
dev(tim)->IMR |= GPT_IMR_TAMIM;
dev(tim)->ICLR = chn_isr_cfg[channel].flag;
if (channel == 0) {
dev(tim)->TAMATCHR = (timer_config[tim].cfg == GPT_CFG_32T) ?
value : (LOAD_VALUE - value);
}
else {
dev(tim)->TBMATCHR = (timer_config[tim].cfg == GPT_CFG_32T) ?
value : (LOAD_VALUE - value);
}
dev(tim)->IMR |= chn_isr_cfg[channel].flag;

return 0;
return 1;
}

int timer_clear(tim_t tim, int channel)
{
if (channel != 0) {
DEBUG("timer_clear(%u, %d)\n", tim, channel);
if ((tim >= TIMER_NUMOF) || (channel >= timer_config[tim].chn)) {
return -1;
}

dev(tim)->IMR &= ~(GPT_IMR_TAMIM);
/* clear interupt flags */
dev(tim)->IMR &= ~(chn_isr_cfg[channel].flag);

return 0;
}

unsigned int timer_read(tim_t tim)
{
return dev(tim)->TAV & 0xFFFF;
DEBUG("timer_read(%u)\n", tim);
if (tim >= TIMER_NUMOF) {
return 0;
}
if (timer_config[tim].cfg == GPT_CFG_32T) {
return dev(tim)->TAV;
}
return LOAD_VALUE - (dev(tim)->TAV & 0xFFFF);
}

void timer_stop(tim_t tim)
{
dev(tim)->CTL = 0;
DEBUG("timer_stop(%u)\n", tim);
if (tim < TIMER_NUMOF) {
dev(tim)->CTL = 0;
}
}

void timer_start(tim_t tim)
{
dev(tim)->CTL = GPT_CTL_TAEN;
DEBUG("timer_start(%u)\n", tim);

if (tim < TIMER_NUMOF) {
if (timer_config[tim].chn == 1) {
dev(tim)->CTL = GPT_CTL_TAEN;
}
else if (timer_config[tim].chn == 2) {
dev(tim)->CTL = GPT_CTL_TAEN | GPT_CTL_TBEN;
}
}
}

/**
* @brief handle interrupt for a timer
* @brief Timer interrupt handler
*
* @param[in] tim index of the timer
* @param[in] chn channel number (0=A, 1=B)
*/
static inline void isr_handler(tim_t tim)
static void irq_handler(tim_t tim, int channel)
{
uint32_t mis = dev(tim)->MIS;
assert(tim < TIMER_NUMOF);
assert(channel < timer_config[tim].chn);

uint32_t mis;
/* Latch the active interrupt flags */
mis = dev(tim)->MIS & chn_isr_cfg[channel].mask;
/* Clear the latched interrupt flags */
dev(tim)->ICLR = mis;

if (mis & GPT_IMR_TAMIM) {
dev(tim)->IMR &= ~GPT_IMR_TAMIM;
ctx[tim].cb(ctx[tim].arg, 0);
if (mis & chn_isr_cfg[channel].flag) {
/* Disable further match interrupts for this timer/channel */
dev(tim)->IMR &= ~chn_isr_cfg[channel].flag;
/* Invoke the callback function */
isr_ctx[tim].cb(isr_ctx[tim].arg, channel);
}

cortexm_isr_end();
}

#ifdef TIMER_0_ISR
void TIMER_0_ISR(void)
{
isr_handler(0);
}
#endif

#ifdef TIMER_1_ISR
void TIMER_1_ISR(void)
{
isr_handler(1);
}
#endif

#ifdef TIMER_2_ISR
void TIMER_2_ISR(void)
{
isr_handler(2);
}
#endif

#ifdef TIMER_3_ISR
void TIMER_3_ISR(void)
{
isr_handler(3);
}
#endif
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);}

0 comments on commit 2bab65c

Please sign in to comment.