mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/msp430: improve periph_timer
- add support for multiple timers - add support for selecting clock source in the board's `periph_conf.h` - add support for the prescaler - implement `periph_timer_query_freqs` - add a second timer to all MSP430 boards - the first timer is fast ticking, high-power - the second is slow ticking, low-power
This commit is contained in:
parent
aa045d540f
commit
7044699388
@ -48,11 +48,24 @@ static const msp430_clock_params_t clock_params = {
|
||||
* @name Timer configuration
|
||||
* @{
|
||||
*/
|
||||
#define TIMER_NUMOF (1U)
|
||||
#define TIMER_BASE (&TIMER_A)
|
||||
#define TIMER_CHAN (3)
|
||||
#define TIMER_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER_ISR_CCX (TIMERA1_VECTOR)
|
||||
static const timer_conf_t timer_conf[] = {
|
||||
{
|
||||
.timer = &TIMER_A,
|
||||
.irq_flags = &TIMER_A_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK,
|
||||
},
|
||||
{
|
||||
.timer = &TIMER_B,
|
||||
.irq_flags = &TIMER_B_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK,
|
||||
}
|
||||
};
|
||||
#define TIMER_NUMOF ARRAY_SIZE(timer_conf)
|
||||
|
||||
#define TIMER0_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER0_ISR_CCX (TIMERA1_VECTOR)
|
||||
#define TIMER1_ISR_CC0 (TIMERB0_VECTOR)
|
||||
#define TIMER1_ISR_CCX (TIMERB1_VECTOR)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -47,11 +47,24 @@ static const msp430_clock_params_t clock_params = {
|
||||
* @name Timer configuration
|
||||
* @{
|
||||
*/
|
||||
#define TIMER_NUMOF (1U)
|
||||
#define TIMER_BASE (&TIMER_A)
|
||||
#define TIMER_CHAN (3)
|
||||
#define TIMER_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER_ISR_CCX (TIMERA1_VECTOR)
|
||||
static const timer_conf_t timer_conf[] = {
|
||||
{
|
||||
.timer = &TIMER_A,
|
||||
.irq_flags = &TIMER_A_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK,
|
||||
},
|
||||
{
|
||||
.timer = &TIMER_B,
|
||||
.irq_flags = &TIMER_B_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK,
|
||||
}
|
||||
};
|
||||
#define TIMER_NUMOF ARRAY_SIZE(timer_conf)
|
||||
|
||||
#define TIMER0_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER0_ISR_CCX (TIMERA1_VECTOR)
|
||||
#define TIMER1_ISR_CC0 (TIMERB0_VECTOR)
|
||||
#define TIMER1_ISR_CCX (TIMERB1_VECTOR)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -47,11 +47,24 @@ static const msp430_clock_params_t clock_params = {
|
||||
* @name Timer configuration
|
||||
* @{
|
||||
*/
|
||||
#define TIMER_NUMOF (1U)
|
||||
#define TIMER_BASE (&TIMER_A)
|
||||
#define TIMER_CHAN (3)
|
||||
#define TIMER_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER_ISR_CCX (TIMERA1_VECTOR)
|
||||
static const timer_conf_t timer_conf[] = {
|
||||
{
|
||||
.timer = &TIMER_A,
|
||||
.irq_flags = &TIMER_A_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK,
|
||||
},
|
||||
{
|
||||
.timer = &TIMER_B,
|
||||
.irq_flags = &TIMER_B_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK,
|
||||
}
|
||||
};
|
||||
#define TIMER_NUMOF ARRAY_SIZE(timer_conf)
|
||||
|
||||
#define TIMER0_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER0_ISR_CCX (TIMERA1_VECTOR)
|
||||
#define TIMER1_ISR_CC0 (TIMERB0_VECTOR)
|
||||
#define TIMER1_ISR_CCX (TIMERB1_VECTOR)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -46,11 +46,24 @@ static const msp430_clock_params_t clock_params = {
|
||||
* @name Timer configuration
|
||||
* @{
|
||||
*/
|
||||
#define TIMER_NUMOF (1U)
|
||||
#define TIMER_BASE (&TIMER_A)
|
||||
#define TIMER_CHAN (3)
|
||||
#define TIMER_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER_ISR_CCX (TIMERA1_VECTOR)
|
||||
static const timer_conf_t timer_conf[] = {
|
||||
{
|
||||
.timer = &TIMER_A,
|
||||
.irq_flags = &TIMER_A_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK,
|
||||
},
|
||||
{
|
||||
.timer = &TIMER_B,
|
||||
.irq_flags = &TIMER_B_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK,
|
||||
}
|
||||
};
|
||||
#define TIMER_NUMOF ARRAY_SIZE(timer_conf)
|
||||
|
||||
#define TIMER0_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER0_ISR_CCX (TIMERA1_VECTOR)
|
||||
#define TIMER1_ISR_CC0 (TIMERB0_VECTOR)
|
||||
#define TIMER1_ISR_CCX (TIMERB1_VECTOR)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -47,11 +47,24 @@ static const msp430_clock_params_t clock_params = {
|
||||
* @name Timer configuration
|
||||
* @{
|
||||
*/
|
||||
#define TIMER_NUMOF (1U)
|
||||
#define TIMER_BASE (&TIMER_A)
|
||||
#define TIMER_CHAN (3)
|
||||
#define TIMER_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER_ISR_CCX (TIMERA1_VECTOR)
|
||||
static const timer_conf_t timer_conf[] = {
|
||||
{
|
||||
.timer = &TIMER_A,
|
||||
.irq_flags = &TIMER_A_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK,
|
||||
},
|
||||
{
|
||||
.timer = &TIMER_B,
|
||||
.irq_flags = &TIMER_B_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK,
|
||||
}
|
||||
};
|
||||
#define TIMER_NUMOF ARRAY_SIZE(timer_conf)
|
||||
|
||||
#define TIMER0_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER0_ISR_CCX (TIMERA1_VECTOR)
|
||||
#define TIMER1_ISR_CC0 (TIMERB0_VECTOR)
|
||||
#define TIMER1_ISR_CCX (TIMERB1_VECTOR)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -47,11 +47,24 @@ static const msp430_clock_params_t clock_params = {
|
||||
* @name Timer configuration
|
||||
* @{
|
||||
*/
|
||||
#define TIMER_NUMOF (1U)
|
||||
#define TIMER_BASE (&TIMER_A)
|
||||
#define TIMER_CHAN (3)
|
||||
#define TIMER_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER_ISR_CCX (TIMERA1_VECTOR)
|
||||
static const timer_conf_t timer_conf[] = {
|
||||
{
|
||||
.timer = &TIMER_A,
|
||||
.irq_flags = &TIMER_A_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK,
|
||||
},
|
||||
{
|
||||
.timer = &TIMER_B,
|
||||
.irq_flags = &TIMER_B_IRQFLAGS,
|
||||
.clock_source = TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK,
|
||||
}
|
||||
};
|
||||
#define TIMER_NUMOF ARRAY_SIZE(timer_conf)
|
||||
|
||||
#define TIMER0_ISR_CC0 (TIMERA0_VECTOR)
|
||||
#define TIMER0_ISR_CCX (TIMERA1_VECTOR)
|
||||
#define TIMER1_ISR_CC0 (TIMERB0_VECTOR)
|
||||
#define TIMER1_ISR_CCX (TIMERB1_VECTOR)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -18,6 +18,7 @@ config CPU_ARCH_MSP430
|
||||
select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE
|
||||
select HAS_PERIPH_FLASHPAGE_PAGEWISE
|
||||
select HAS_PERIPH_PM
|
||||
select HAS_PERIPH_TIMER_QUERY_FREQS
|
||||
select MODULE_MALLOC_THREAD_SAFE if TEST_KCONFIG
|
||||
|
||||
config HAS_CPU_MSP430
|
||||
|
@ -18,3 +18,4 @@ FEATURES_PROVIDED += periph_flashpage
|
||||
FEATURES_PROVIDED += periph_flashpage_in_address_space
|
||||
FEATURES_PROVIDED += periph_flashpage_pagewise
|
||||
FEATURES_PROVIDED += periph_pm
|
||||
FEATURES_PROVIDED += periph_timer_query_freqs
|
||||
|
@ -57,13 +57,16 @@ extern "C" {
|
||||
* @name Timer Input Divider Values
|
||||
*
|
||||
* @details The vendor header macros are again non-obvious in their naming, so
|
||||
* provide better alies names.
|
||||
* provide better alias names.
|
||||
* @{
|
||||
*/
|
||||
#define TXID_DIV_1 ID_0 /**< Input Divider: Divide by 1 */
|
||||
#define TXID_DIV_2 ID_1 /**< Input Divider: Divide by 2 */
|
||||
#define TXID_DIV_4 ID_2 /**< Input Divider: Divide by 4 */
|
||||
#define TXID_DIV_8 ID_3 /**< Input Divider: Divide by 4 */
|
||||
#define TXID_DIV_8 ID_3 /**< Input Divider: Divide by 8 */
|
||||
#define TXID_DIV_Msk ID_3 /**< Mask to get the TXID field */
|
||||
#define TXID_DIV_Pos 6U /**< Position of the TXID field */
|
||||
#define TXID_DIV_MAX 3 /**< Maximum configuration value in the TXID field */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -108,18 +111,11 @@ typedef struct {
|
||||
REG8 SEL; /**< alternative function select */
|
||||
} msp430_port_p3_p6_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Timer interrupt status registers
|
||||
*/
|
||||
typedef struct {
|
||||
REG16 TBIV; /**< TIMER_A interrupt status */
|
||||
REG16 reserved[7];/**< reserved */
|
||||
REG16 TAIV; /**< TIMER_B interrupt status */
|
||||
} msp430_timer_ivec_t;
|
||||
|
||||
/**
|
||||
* @brief Timer module registers
|
||||
* @brief Timer peripheral registers
|
||||
*
|
||||
* @note The TIMER_A timer only has 3 CC channels instead of the 8 channels
|
||||
* the TIMER_B has, the memory layout is the same nonetheless.
|
||||
*/
|
||||
typedef struct {
|
||||
REG16 CTL; /**< timer control */
|
||||
@ -160,16 +156,29 @@ extern msp430_port_p3_p6_t PORT_5;
|
||||
*/
|
||||
extern msp430_port_p3_p6_t PORT_6;
|
||||
|
||||
/**
|
||||
* @brief Register map of the timer interrupt control registers
|
||||
*/
|
||||
extern msp430_timer_ivec_t TIMER_IVEC;
|
||||
|
||||
/**
|
||||
* @brief Register map of the timer A control registers
|
||||
*/
|
||||
extern msp430_timer_t TIMER_A;
|
||||
|
||||
/**
|
||||
* @brief IRQ flags for TIMER_A
|
||||
*
|
||||
* Called TAIV in the data sheet / vendor files. This shallow alias
|
||||
* makes the name more readable and does impedance matching for the type
|
||||
* (`volatile uint16_t` vs `volatile short`).
|
||||
*/
|
||||
extern REG16 TIMER_A_IRQFLAGS;
|
||||
|
||||
/**
|
||||
* @brief IRQ flags for TIMER_B
|
||||
*
|
||||
* Called TBIV in the data sheet / vendor files. This shallow alias
|
||||
* makes the name more readable and does impedance matching for the type
|
||||
* (`volatile uint16_t` vs `volatile short`).
|
||||
*/
|
||||
extern REG16 TIMER_B_IRQFLAGS;
|
||||
|
||||
/**
|
||||
* @brief Register map of the timer B control registers
|
||||
*/
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bitarithm.h"
|
||||
#include "compiler_hints.h"
|
||||
#include "cpu.h"
|
||||
#include "msp430_regs.h"
|
||||
|
||||
@ -52,6 +53,17 @@ typedef uint16_t gpio_t;
|
||||
*/
|
||||
#define SPI_HWCS(x) (SPI_CS_UNDEF)
|
||||
|
||||
/**
|
||||
* @brief The MSP430 timer peripheral can have up to 8 channels
|
||||
*
|
||||
* @note The actual number of channels should be queried per timer, as
|
||||
* timers have either 7 or 3 capture/compare channels; typically both
|
||||
* variants are present in the same MCU. This is the highest number
|
||||
* of channels supported, e.g. useful for "worst case" static memory
|
||||
* allocation.
|
||||
*/
|
||||
#define TIMER_CHANNEL_NUMOF 7
|
||||
|
||||
/**
|
||||
* @name Override flank selection values
|
||||
* @{
|
||||
@ -289,6 +301,31 @@ typedef struct {
|
||||
bool has_xt2;
|
||||
} msp430_clock_params_t;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of possible clock sources for a timer
|
||||
*/
|
||||
typedef enum {
|
||||
TIMER_CLOCK_SOURCE_TXCLK = TXSSEL_TXCLK, /**< External TxCLK as clock source */
|
||||
TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK = TXSSEL_ACLK, /**< Auxiliary clock as clock source */
|
||||
TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK = TXSSEL_SMCLK, /**< Sub-system master clock as clock source */
|
||||
TIMER_CLOCK_SOURCE_INCLK = TXSSEL_INCLK, /**< External INCLK as clock source */
|
||||
} msp430_timer_clock_source_t;
|
||||
|
||||
/**
|
||||
* @brief Timer configuration on an MSP430 timer
|
||||
*/
|
||||
typedef struct {
|
||||
msp430_timer_t *timer; /**< Hardware timer to use */
|
||||
/**
|
||||
* @brief "Timer interrupt vector" register
|
||||
*
|
||||
* Use `&TIMER_A_IRQFLAGS` for `TIMER_A` or
|
||||
* `&TIMER_B_IRQFLAGS` for `TIMER_B`.
|
||||
*/
|
||||
REG16 *irq_flags;
|
||||
msp430_timer_clock_source_t clock_source; /**< Clock source to use */
|
||||
} timer_conf_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the basic clock system to provide the main clock,
|
||||
* the subsystem clock, and the auxiliary clock.
|
||||
|
@ -24,16 +24,15 @@ SECTIONS
|
||||
/* provide address for register maps by taking the address of the first
|
||||
* register (as provided by the vendor files) */
|
||||
|
||||
PROVIDE(PORT_1 = P1IN);
|
||||
PROVIDE(PORT_2 = P2IN);
|
||||
PROVIDE(PORT_3 = P3IN);
|
||||
PROVIDE(PORT_3 = P3IN);
|
||||
PROVIDE(PORT_4 = P4IN);
|
||||
PROVIDE(PORT_5 = P5IN);
|
||||
PROVIDE(PORT_6 = P6IN);
|
||||
PROVIDE(PORT_1 = P1IN);
|
||||
PROVIDE(PORT_2 = P2IN);
|
||||
PROVIDE(PORT_3 = P3IN);
|
||||
PROVIDE(PORT_3 = P3IN);
|
||||
PROVIDE(PORT_4 = P4IN);
|
||||
PROVIDE(PORT_5 = P5IN);
|
||||
PROVIDE(PORT_6 = P6IN);
|
||||
|
||||
/* no typo: TBIV indeed comes before TAIV in memory, see msp430_timer_ivec_t */
|
||||
PROVIDE(TIMER_IVEC = TBIV);
|
||||
|
||||
PROVIDE(TIMER_A = TACTL);
|
||||
PROVIDE(TIMER_B = TBCTL);
|
||||
PROVIDE(TIMER_A = TACTL);
|
||||
PROVIDE(TIMER_B = TBCTL);
|
||||
PROVIDE(TIMER_A_IRQFLAGS = TAIV);
|
||||
PROVIDE(TIMER_B_IRQFLAGS = TBIV);
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin
|
||||
* 2023 Otto-von-Guericke-Universität Magdeburg
|
||||
*
|
||||
* 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
|
||||
@ -21,107 +22,248 @@
|
||||
* through the board's `periph_conf.h`
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "compiler_hints.h"
|
||||
#include "cpu.h"
|
||||
#include "periph_cpu.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph/timer.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph_cpu.h"
|
||||
|
||||
/**
|
||||
* @brief Save reference to the timer callback
|
||||
* @brief Interrupt context for each configured timer
|
||||
*/
|
||||
static timer_cb_t isr_cb;
|
||||
static timer_isr_ctx_t isr_ctx[TIMER_NUMOF];
|
||||
|
||||
/**
|
||||
* @brief Save argument for the ISR callback
|
||||
*/
|
||||
static void *isr_arg;
|
||||
/* Hack to count the number of ISR vectors provided by the board */
|
||||
enum {
|
||||
#ifdef TIMER0_ISR_CC0
|
||||
TIMER_ISR_COUNT_HELPER_0,
|
||||
#endif
|
||||
#ifdef TIMER1_ISR_CC0
|
||||
TIMER_ISR_COUNT_HELPER_1,
|
||||
#endif
|
||||
TIMER_ISR_NUMOF
|
||||
};
|
||||
|
||||
static_assert((TXID_DIV_MAX << TXID_DIV_Pos) == TXID_DIV_Msk, "TXID_DIV_MAX or TXID_DIV_Pos is incorrect.");
|
||||
static_assert(TIMER_ISR_NUMOF == TIMER_NUMOF,
|
||||
"For each provided timer a corresponding IRQ number needs to be provided by the board.");
|
||||
|
||||
static uint32_t abs_diff(uint32_t a, uint32_t b)
|
||||
{
|
||||
if (a >= b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
return b - a;
|
||||
}
|
||||
|
||||
static uint16_t prescale(msp430_timer_clock_source_t clock_source, uint32_t freq)
|
||||
{
|
||||
uint32_t clock_freq;
|
||||
assume((clock_source == TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK) ||
|
||||
(clock_source == TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK));
|
||||
switch (clock_source) {
|
||||
default:
|
||||
case TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK:
|
||||
clock_freq = msp430_auxiliary_clock_freq();
|
||||
break;
|
||||
case TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK:
|
||||
clock_freq = msp430_submain_clock_freq();
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned prescaler = 0;
|
||||
uint32_t best_diff = UINT32_MAX;
|
||||
|
||||
for (unsigned i = 0; i <= TXID_DIV_MAX; i++) {
|
||||
uint32_t prescaled_freq = clock_freq >> i;
|
||||
uint32_t off = abs_diff(freq, prescaled_freq);
|
||||
if (off < best_diff) {
|
||||
best_diff = off;
|
||||
prescaler = i;
|
||||
}
|
||||
}
|
||||
|
||||
return clock_source | (prescaler << TXID_DIV_Pos);
|
||||
}
|
||||
|
||||
int timer_init(tim_t dev, uint32_t freq, timer_cb_t cb, void *arg)
|
||||
{
|
||||
/* using fixed TIMER_BASE for now */
|
||||
if (dev != 0) {
|
||||
return -1;
|
||||
}
|
||||
/* TODO: configure time-base depending on freq value */
|
||||
if (freq != 1000000ul) {
|
||||
return -1;
|
||||
}
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
msp430_timer_t *msptimer = timer_conf[dev].timer;
|
||||
|
||||
/* reset the timer A configuration */
|
||||
TIMER_BASE->CTL = TACLR;
|
||||
msptimer->CTL = TACLR;
|
||||
/* save callback */
|
||||
isr_cb = cb;
|
||||
isr_arg = arg;
|
||||
/* configure timer to use the SMCLK with prescaler of 8 */
|
||||
TIMER_BASE->CTL = (TXSSEL_SMCLK | TXID_DIV_8);
|
||||
isr_ctx[dev].cb = cb;
|
||||
isr_ctx[dev].arg = arg;
|
||||
/* compute prescaler */
|
||||
uint16_t ctl = prescale(timer_conf[dev].clock_source, freq);
|
||||
msptimer->CTL = ctl;
|
||||
/* configure CC channels */
|
||||
for (int i = 0; i < TIMER_CHAN; i++) {
|
||||
TIMER_BASE->CCTL[i] = 0;
|
||||
for (unsigned i = 0; i < timer_query_channel_numof(dev); i++) {
|
||||
msptimer->CCTL[i] = 0;
|
||||
}
|
||||
/* start the timer in continuous mode */
|
||||
TIMER_BASE->CTL |= TXMC_CONT;
|
||||
msptimer->CTL = ctl | TXMC_CONT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timer_set_absolute(tim_t dev, int channel, unsigned int value)
|
||||
{
|
||||
if (dev != 0 || channel >= TIMER_CHAN) {
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
msp430_timer_t *msptimer = timer_conf[dev].timer;
|
||||
|
||||
if ((unsigned)channel >= timer_query_channel_numof(dev)) {
|
||||
return -1;
|
||||
}
|
||||
TIMER_BASE->CCR[channel] = value;
|
||||
TIMER_BASE->CCTL[channel] &= ~(CCIFG);
|
||||
TIMER_BASE->CCTL[channel] |= CCIE;
|
||||
|
||||
msptimer->CCR[channel] = value;
|
||||
msptimer->CCTL[channel] &= ~(CCIFG);
|
||||
msptimer->CCTL[channel] |= CCIE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timer_clear(tim_t dev, int channel)
|
||||
{
|
||||
if (dev != 0 || channel >= TIMER_CHAN) {
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
msp430_timer_t *msptimer = timer_conf[dev].timer;
|
||||
|
||||
if ((unsigned)channel >= timer_query_channel_numof(dev)) {
|
||||
return -1;
|
||||
}
|
||||
TIMER_BASE->CCTL[channel] &= ~(CCIE);
|
||||
msptimer->CCTL[channel] &= ~(CCIE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int timer_read(tim_t dev)
|
||||
{
|
||||
(void)dev;
|
||||
return (unsigned int)TIMER_BASE->R;
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
msp430_timer_t *msptimer = timer_conf[dev].timer;
|
||||
return msptimer->R;
|
||||
}
|
||||
|
||||
void timer_start(tim_t dev)
|
||||
{
|
||||
(void)dev;
|
||||
TIMER_BASE->CTL |= TXMC_CONT;
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
msp430_timer_t *msptimer = timer_conf[dev].timer;
|
||||
msptimer->CTL |= TXMC_CONT;
|
||||
}
|
||||
|
||||
void timer_stop(tim_t dev)
|
||||
{
|
||||
(void)dev;
|
||||
TIMER_BASE->CTL &= ~(TXMC_MASK);
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
msp430_timer_t *msptimer = timer_conf[dev].timer;
|
||||
msptimer->CTL &= ~(TXMC_MASK);
|
||||
}
|
||||
|
||||
ISR(TIMER_ISR_CC0, isr_timer_a_cc0)
|
||||
__attribute__((pure))
|
||||
uword_t timer_query_freqs_numof(tim_t dev)
|
||||
{
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
/* Smallest div value is 0, so number is max + 1 */
|
||||
return TXID_DIV_MAX + 1;
|
||||
}
|
||||
|
||||
__attribute__((pure))
|
||||
uword_t timer_query_channel_numof(tim_t dev)
|
||||
{
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
if (timer_conf[dev].timer == &TIMER_A) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 7;
|
||||
}
|
||||
|
||||
uint32_t timer_query_freqs(tim_t dev, uword_t index)
|
||||
{
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
const msp430_timer_clock_source_t clock_source = timer_conf[dev].clock_source;
|
||||
assume((clock_source == TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK) ||
|
||||
(clock_source == TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK));
|
||||
|
||||
uint32_t clock_freq;
|
||||
switch (clock_source) {
|
||||
default:
|
||||
case TIMER_CLOCK_SOURCE_AUXILIARY_CLOCK:
|
||||
clock_freq = msp430_auxiliary_clock_freq();
|
||||
break;
|
||||
case TIMER_CLOCK_SOURCE_SUBMAIN_CLOCK:
|
||||
clock_freq = msp430_submain_clock_freq();
|
||||
break;
|
||||
}
|
||||
|
||||
return clock_freq >> index;
|
||||
}
|
||||
|
||||
static void timer_isr(tim_t dev, unsigned chan)
|
||||
{
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
msp430_timer_t *msptimer = timer_conf[dev].timer;
|
||||
/* disable IRQ */
|
||||
msptimer->CCTL[chan] &= ~(CCIE);
|
||||
isr_ctx[dev].cb(isr_ctx[dev].arg, chan);
|
||||
|
||||
}
|
||||
static void timer_isr_cc0(tim_t dev)
|
||||
{
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
timer_isr(dev, 0);
|
||||
}
|
||||
|
||||
static void timer_isr_ccx(tim_t dev)
|
||||
{
|
||||
assume((unsigned)dev < TIMER_NUMOF);
|
||||
unsigned chan = *timer_conf[dev].irq_flags >> 1U;
|
||||
if (chan >= timer_query_channel_numof(dev)) {
|
||||
/* timer overflown */
|
||||
}
|
||||
else {
|
||||
/* CC matched */
|
||||
timer_isr(dev, chan);
|
||||
}
|
||||
}
|
||||
|
||||
ISR(TIMER0_ISR_CC0, isr_timer0_cc0)
|
||||
{
|
||||
__enter_isr();
|
||||
|
||||
TIMER_BASE->CCTL[0] &= ~(CCIE);
|
||||
isr_cb(isr_arg, 0);
|
||||
timer_isr_cc0(0);
|
||||
|
||||
__exit_isr();
|
||||
}
|
||||
|
||||
ISR(TIMER_ISR_CCX, isr_timer_a_ccx)
|
||||
ISR(TIMER0_ISR_CCX, isr_timer0_ccx)
|
||||
{
|
||||
__enter_isr();
|
||||
|
||||
int chan = (int)(TIMER_IVEC.TAIV >> 1);
|
||||
TIMER_BASE->CCTL[chan] &= ~(CCIE);
|
||||
isr_cb(isr_arg, chan);
|
||||
timer_isr_ccx(0);
|
||||
|
||||
__exit_isr();
|
||||
}
|
||||
|
||||
#ifdef TIMER1_ISR_CC0
|
||||
ISR(TIMER1_ISR_CC0, isr_timer1_cc0)
|
||||
{
|
||||
__enter_isr();
|
||||
|
||||
timer_isr_cc0(1);
|
||||
|
||||
__exit_isr();
|
||||
}
|
||||
|
||||
ISR(TIMER1_ISR_CCX, isr_timer1_ccx)
|
||||
{
|
||||
__enter_isr();
|
||||
|
||||
timer_isr_ccx(1);
|
||||
|
||||
__exit_isr();
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user