Merge pull request #13902 from benpicco/periph_timer_periodic
periph/timer: add timer_set_periodic()
This commit is contained in:
commit
49aef1b678
@ -1050,6 +1050,10 @@ ifneq (,$(filter periph_gpio_irq,$(USEMODULE)))
|
|||||||
FEATURES_REQUIRED += periph_gpio
|
FEATURES_REQUIRED += periph_gpio
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter periph_timer_periodic,$(USEMODULE)))
|
||||||
|
FEATURES_REQUIRED += periph_timer
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter devfs_hwrng,$(USEMODULE)))
|
ifneq (,$(filter devfs_hwrng,$(USEMODULE)))
|
||||||
FEATURES_REQUIRED += periph_hwrng
|
FEATURES_REQUIRED += periph_hwrng
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -5,6 +5,7 @@ FEATURES_PROVIDED += periph_cpuid
|
|||||||
FEATURES_PROVIDED += periph_eeprom
|
FEATURES_PROVIDED += periph_eeprom
|
||||||
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
||||||
FEATURES_PROVIDED += periph_pm
|
FEATURES_PROVIDED += periph_pm
|
||||||
|
FEATURES_PROVIDED += periph_timer_periodic
|
||||||
FEATURES_PROVIDED += periph_wdt
|
FEATURES_PROVIDED += periph_wdt
|
||||||
FEATURES_PROVIDED += puf_sram
|
FEATURES_PROVIDED += puf_sram
|
||||||
|
|
||||||
|
|||||||
@ -74,6 +74,23 @@ static ctx_t ctx[] = {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static unsigned _oneshot;
|
||||||
|
|
||||||
|
static inline void set_oneshot(tim_t tim, int chan)
|
||||||
|
{
|
||||||
|
_oneshot |= (1 << chan) << (TIMER_CHANNELS * tim);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void clear_oneshot(tim_t tim, int chan)
|
||||||
|
{
|
||||||
|
_oneshot &= ~((1 << chan) << (TIMER_CHANNELS * tim));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_oneshot(tim_t tim, int chan)
|
||||||
|
{
|
||||||
|
return _oneshot & ((1 << chan) << (TIMER_CHANNELS * tim));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Setup the given timer
|
* @brief Setup the given timer
|
||||||
*/
|
*/
|
||||||
@ -109,7 +126,7 @@ int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pre == PRESCALE_NUMOF) {
|
if (pre == PRESCALE_NUMOF) {
|
||||||
DEBUG("timer.c: prescaling failed!\n");
|
DEBUG("timer.c: prescaling from %lu Hz failed!\n", CLOCK_CORECLOCK);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,9 +155,12 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned state = irq_disable();
|
unsigned state = irq_disable();
|
||||||
|
|
||||||
ctx[tim].dev->OCR[channel] = (uint16_t)value;
|
ctx[tim].dev->OCR[channel] = (uint16_t)value;
|
||||||
*ctx[tim].flag &= ~(1 << (channel + OCF1A));
|
*ctx[tim].flag &= ~(1 << (channel + OCF1A));
|
||||||
*ctx[tim].mask |= (1 << (channel + OCIE1A));
|
*ctx[tim].mask |= (1 << (channel + OCIE1A));
|
||||||
|
set_oneshot(tim, channel);
|
||||||
|
|
||||||
irq_restore(state);
|
irq_restore(state);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -154,9 +174,11 @@ int timer_set(tim_t tim, int channel, unsigned int timeout)
|
|||||||
|
|
||||||
unsigned state = irq_disable();
|
unsigned state = irq_disable();
|
||||||
unsigned absolute = ctx[tim].dev->CNT + timeout;
|
unsigned absolute = ctx[tim].dev->CNT + timeout;
|
||||||
|
|
||||||
ctx[tim].dev->OCR[channel] = absolute;
|
ctx[tim].dev->OCR[channel] = absolute;
|
||||||
uint8_t mask = 1 << (channel + OCIE1A);
|
*ctx[tim].mask |= (1 << (channel + OCIE1A));
|
||||||
*ctx[tim].mask |= mask;
|
set_oneshot(tim, channel);
|
||||||
|
|
||||||
if ((absolute - ctx[tim].dev->CNT) > timeout) {
|
if ((absolute - ctx[tim].dev->CNT) > timeout) {
|
||||||
/* Timer already expired. Trigger the interrupt now and loop until it
|
/* Timer already expired. Trigger the interrupt now and loop until it
|
||||||
* is triggered.
|
* is triggered.
|
||||||
@ -164,13 +186,53 @@ int timer_set(tim_t tim, int channel, unsigned int timeout)
|
|||||||
while (!(*ctx[tim].flag & (1 << (OCF1A + channel)))) {
|
while (!(*ctx[tim].flag & (1 << (OCF1A + channel)))) {
|
||||||
ctx[tim].dev->OCR[channel] = ctx[tim].dev->CNT;
|
ctx[tim].dev->OCR[channel] = ctx[tim].dev->CNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
irq_restore(state);
|
irq_restore(state);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if (channel >= TIMER_CHANNELS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & TIM_FLAG_RESET_ON_SET) {
|
||||||
|
ctx[tim].dev->CNT = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned state = irq_disable();
|
||||||
|
|
||||||
|
ctx[tim].dev->OCR[channel] = (uint16_t)value;
|
||||||
|
|
||||||
|
*ctx[tim].flag &= ~(1 << (channel + OCF1A));
|
||||||
|
*ctx[tim].mask |= (1 << (channel + OCIE1A));
|
||||||
|
|
||||||
|
clear_oneshot(tim, channel);
|
||||||
|
|
||||||
|
/* only OCR0 can be use to set TOP */
|
||||||
|
if (channel == 0) {
|
||||||
|
if (flags & TIM_FLAG_RESET_ON_MATCH) {
|
||||||
|
/* enable CTC mode */
|
||||||
|
ctx[tim].dev->CRB |= (1 << 3);
|
||||||
|
} else {
|
||||||
|
/* disable CTC mode */
|
||||||
|
ctx[tim].dev->CRB &= (1 << 3);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert((flags & TIM_FLAG_RESET_ON_MATCH) == 0);
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_restore(state);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
int timer_clear(tim_t tim, int channel)
|
int timer_clear(tim_t tim, int channel)
|
||||||
{
|
{
|
||||||
if (channel >= TIMER_CHANNELS) {
|
if (channel >= TIMER_CHANNELS) {
|
||||||
@ -216,7 +278,9 @@ static inline void _isr(tim_t tim, int chan)
|
|||||||
|
|
||||||
atmega_enter_isr();
|
atmega_enter_isr();
|
||||||
|
|
||||||
*ctx[tim].mask &= ~(1 << (chan + OCIE1A));
|
if (is_oneshot(tim, chan)) {
|
||||||
|
*ctx[tim].mask &= ~(1 << (chan + OCIE1A));
|
||||||
|
}
|
||||||
ctx[tim].cb(ctx[tim].arg, chan);
|
ctx[tim].cb(ctx[tim].arg, chan);
|
||||||
|
|
||||||
#if defined(DEBUG_TIMER_PORT)
|
#if defined(DEBUG_TIMER_PORT)
|
||||||
|
|||||||
@ -2,5 +2,6 @@
|
|||||||
FEATURES_PROVIDED += backup_ram
|
FEATURES_PROVIDED += backup_ram
|
||||||
FEATURES_PROVIDED += periph_dac
|
FEATURES_PROVIDED += periph_dac
|
||||||
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
||||||
|
FEATURES_PROVIDED += periph_timer_periodic
|
||||||
|
|
||||||
-include $(RIOTCPU)/arm7_common/Makefile.features
|
-include $(RIOTCPU)/arm7_common/Makefile.features
|
||||||
|
|||||||
@ -126,7 +126,7 @@ typedef struct {
|
|||||||
/**
|
/**
|
||||||
* @brief Number of available timer channels
|
* @brief Number of available timer channels
|
||||||
*/
|
*/
|
||||||
#define TIMER_CHAN_NUMOF (4U)
|
#define TIMER_CHANNELS (4U)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Declare needed generic SPI functions
|
* @brief Declare needed generic SPI functions
|
||||||
|
|||||||
@ -38,6 +38,23 @@
|
|||||||
*/
|
*/
|
||||||
static timer_isr_ctx_t isr_ctx[TIMER_NUMOF];
|
static timer_isr_ctx_t isr_ctx[TIMER_NUMOF];
|
||||||
|
|
||||||
|
static uint32_t _oneshot;
|
||||||
|
|
||||||
|
static inline void set_oneshot(tim_t tim, int chan)
|
||||||
|
{
|
||||||
|
_oneshot |= (1 << chan) << (TIMER_CHANNELS * tim);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void clear_oneshot(tim_t tim, int chan)
|
||||||
|
{
|
||||||
|
_oneshot &= ~((1 << chan) << (TIMER_CHANNELS * tim));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_oneshot(tim_t tim, int chan)
|
||||||
|
{
|
||||||
|
return _oneshot & ((1 << chan) << (TIMER_CHANNELS * tim));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Forward declarations for interrupt functions
|
* @brief Forward declarations for interrupt functions
|
||||||
* @{
|
* @{
|
||||||
@ -139,19 +156,53 @@ int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
|
|||||||
|
|
||||||
int timer_set_absolute(tim_t tim, int channel, unsigned int value)
|
int timer_set_absolute(tim_t tim, int channel, unsigned int value)
|
||||||
{
|
{
|
||||||
if (((unsigned) tim >= TIMER_NUMOF) || ((unsigned) channel >= TIMER_CHAN_NUMOF)) {
|
if (((unsigned) tim >= TIMER_NUMOF) || ((unsigned) channel >= TIMER_CHANNELS)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
lpc23xx_timer_t *dev = get_dev(tim);
|
lpc23xx_timer_t *dev = get_dev(tim);
|
||||||
|
|
||||||
|
set_oneshot(tim, channel);
|
||||||
|
|
||||||
dev->MR[channel] = value;
|
dev->MR[channel] = value;
|
||||||
|
/* Match Interrupt */
|
||||||
dev->MCR |= (1 << (channel * 3));
|
dev->MCR |= (1 << (channel * 3));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags)
|
||||||
|
{
|
||||||
|
if (((unsigned) tim >= TIMER_NUMOF) || ((unsigned) channel >= TIMER_CHANNELS)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lpc23xx_timer_t *dev = get_dev(tim);
|
||||||
|
|
||||||
|
if (flags & TIM_FLAG_RESET_ON_SET) {
|
||||||
|
/* reset the timer */
|
||||||
|
dev->TCR = 2;
|
||||||
|
/* start the timer */
|
||||||
|
/* cppcheck-suppress redundantAssignment
|
||||||
|
* (reason: TCR is volatile control register.
|
||||||
|
Bit 2 will put the timer into Reset
|
||||||
|
Bit 1 will control if the timer is running) */
|
||||||
|
dev->TCR = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_oneshot(tim, channel);
|
||||||
|
|
||||||
|
uint8_t cfg = (flags & TIM_FLAG_RESET_ON_MATCH)
|
||||||
|
? 0x3 /* Match Interrupt & Reset on Match */
|
||||||
|
: 0x1; /* Match Interrupt */
|
||||||
|
|
||||||
|
dev->MR[channel] = value;
|
||||||
|
dev->MCR |= (cfg << (channel * 3));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int timer_clear(tim_t tim, int channel)
|
int timer_clear(tim_t tim, int channel)
|
||||||
{
|
{
|
||||||
if (((unsigned) tim >= TIMER_NUMOF) || ((unsigned) channel >= TIMER_CHAN_NUMOF)) {
|
if (((unsigned) tim >= TIMER_NUMOF) || ((unsigned) channel >= TIMER_CHANNELS)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
get_dev(tim)->MCR &= ~(1 << (channel * 3));
|
get_dev(tim)->MCR &= ~(1 << (channel * 3));
|
||||||
@ -173,15 +224,28 @@ void timer_stop(tim_t tim)
|
|||||||
get_dev(tim)->TCR = 0;
|
get_dev(tim)->TCR = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void chan_handler(lpc23xx_timer_t *dev, unsigned tim_num, unsigned chan_num)
|
||||||
|
{
|
||||||
|
const uint32_t mask = (1 << chan_num);
|
||||||
|
|
||||||
|
if ((dev->IR & mask) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->IR |= mask;
|
||||||
|
if (is_oneshot(tim_num, chan_num)) {
|
||||||
|
dev->MCR &= ~(1 << (chan_num * 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
isr_ctx[tim_num].cb(isr_ctx[tim_num].arg, chan_num);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void isr_handler(lpc23xx_timer_t *dev, int tim_num)
|
static inline void isr_handler(lpc23xx_timer_t *dev, int tim_num)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < TIMER_CHAN_NUMOF; i++) {
|
for (unsigned i = 0; i < TIMER_CHANNELS; ++i) {
|
||||||
if (dev->IR & (1 << i)) {
|
chan_handler(dev, tim_num, i);
|
||||||
dev->IR |= (1 << i);
|
|
||||||
dev->MCR &= ~(1 << (i * 3));
|
|
||||||
isr_ctx[tim_num].cb(isr_ctx[tim_num].arg, i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we must not forget to acknowledge the handling of the interrupt */
|
/* we must not forget to acknowledge the handling of the interrupt */
|
||||||
VICVectAddr = 0;
|
VICVectAddr = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,9 @@ endif
|
|||||||
# All SAM0 based CPUs provide PM
|
# All SAM0 based CPUs provide PM
|
||||||
USEMODULE += pm_layered
|
USEMODULE += pm_layered
|
||||||
|
|
||||||
|
# the timer implements timer_set_periodic()
|
||||||
|
FEATURES_PROVIDED += periph_timer_periodic
|
||||||
|
|
||||||
# include sam0 common periph drivers
|
# include sam0 common periph drivers
|
||||||
USEMODULE += sam0_common_periph
|
USEMODULE += sam0_common_periph
|
||||||
|
|
||||||
|
|||||||
@ -343,6 +343,11 @@ typedef struct {
|
|||||||
uint16_t flags; /**< flags for CTRA, e.g. TC_CTRLA_MODE_COUNT32 */
|
uint16_t flags; /**< flags for CTRA, e.g. TC_CTRLA_MODE_COUNT32 */
|
||||||
} tc32_conf_t;
|
} tc32_conf_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of available timer channels
|
||||||
|
*/
|
||||||
|
#define TIMER_CHANNELS (2)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set up alternate function (PMUX setting) for a PORT pin
|
* @brief Set up alternate function (PMUX setting) for a PORT pin
|
||||||
*
|
*
|
||||||
|
|||||||
@ -37,6 +37,23 @@
|
|||||||
*/
|
*/
|
||||||
static timer_isr_ctx_t config[TIMER_NUMOF];
|
static timer_isr_ctx_t config[TIMER_NUMOF];
|
||||||
|
|
||||||
|
static uint32_t _oneshot;
|
||||||
|
|
||||||
|
static inline void set_oneshot(tim_t tim, int chan)
|
||||||
|
{
|
||||||
|
_oneshot |= (1 << chan) << (TIMER_CHANNELS * tim);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void clear_oneshot(tim_t tim, int chan)
|
||||||
|
{
|
||||||
|
_oneshot &= ~((1 << chan) << (TIMER_CHANNELS * tim));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_oneshot(tim_t tim, int chan)
|
||||||
|
{
|
||||||
|
return _oneshot & ((1 << chan) << (TIMER_CHANNELS * tim));
|
||||||
|
}
|
||||||
|
|
||||||
static inline TcCount32 *dev(tim_t tim)
|
static inline TcCount32 *dev(tim_t tim)
|
||||||
{
|
{
|
||||||
return &timer_config[tim].dev->COUNT32;
|
return &timer_config[tim].dev->COUNT32;
|
||||||
@ -89,6 +106,32 @@ static uint8_t _get_prescaler(unsigned long freq_out, unsigned long freq_in)
|
|||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TOP value is CC0 */
|
||||||
|
static inline void _set_mfrq(tim_t tim)
|
||||||
|
{
|
||||||
|
timer_stop(tim);
|
||||||
|
wait_synchronization(tim);
|
||||||
|
#ifdef TC_WAVE_WAVEGEN_MFRQ
|
||||||
|
dev(tim)->WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;
|
||||||
|
#else
|
||||||
|
dev(tim)->CTRLA.bit.WAVEGEN = TC_CTRLA_WAVEGEN_MFRQ_Val;
|
||||||
|
#endif
|
||||||
|
timer_start(tim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TOP value is MAX timer value */
|
||||||
|
static inline void _set_nfrq(tim_t tim)
|
||||||
|
{
|
||||||
|
timer_stop(tim);
|
||||||
|
wait_synchronization(tim);
|
||||||
|
#ifdef TC_WAVE_WAVEGEN_NFRQ
|
||||||
|
dev(tim)->WAVE.reg = TC_WAVE_WAVEGEN_NFRQ;
|
||||||
|
#else
|
||||||
|
dev(tim)->CTRLA.bit.WAVEGEN = TC_CTRLA_WAVEGEN_NFRQ_Val;
|
||||||
|
#endif
|
||||||
|
timer_start(tim);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Setup the given timer
|
* @brief Setup the given timer
|
||||||
*/
|
*/
|
||||||
@ -187,7 +230,52 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value)
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_oneshot(tim, channel);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags)
|
||||||
|
{
|
||||||
|
DEBUG("Setting timer %i channel %i to %i (repeating)\n", tim, channel, value);
|
||||||
|
|
||||||
|
/* set timeout value */
|
||||||
|
switch (channel) {
|
||||||
|
case 0:
|
||||||
|
dev(tim)->INTFLAG.reg = TC_INTFLAG_MC0;
|
||||||
|
|
||||||
|
if (flags & TIM_FLAG_RESET_ON_MATCH) {
|
||||||
|
_set_mfrq(tim);
|
||||||
|
} else {
|
||||||
|
_set_nfrq(tim);
|
||||||
|
}
|
||||||
|
|
||||||
|
_set_cc(tim, 0, value);
|
||||||
|
dev(tim)->INTENSET.reg = TC_INTENSET_MC0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
/* only CC0 can be used to set TOP */
|
||||||
|
if (flags & TIM_FLAG_RESET_ON_MATCH) {
|
||||||
|
assert(0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev(tim)->INTFLAG.reg = TC_INTFLAG_MC1;
|
||||||
|
_set_cc(tim, 1, value);
|
||||||
|
dev(tim)->INTENSET.reg = TC_INTENSET_MC1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & TIM_FLAG_RESET_ON_SET) {
|
||||||
|
dev(tim)->COUNT.reg = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_oneshot(tim, channel);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -255,13 +343,22 @@ static inline void timer_isr(tim_t tim)
|
|||||||
tc->INTFLAG.reg = status;
|
tc->INTFLAG.reg = status;
|
||||||
|
|
||||||
if ((status & TC_INTFLAG_MC0) && tc->INTENSET.bit.MC0) {
|
if ((status & TC_INTFLAG_MC0) && tc->INTENSET.bit.MC0) {
|
||||||
tc->INTENCLR.reg = TC_INTENCLR_MC0;
|
|
||||||
|
if (is_oneshot(tim, 0)) {
|
||||||
|
tc->INTENCLR.reg = TC_INTENCLR_MC0;
|
||||||
|
}
|
||||||
|
|
||||||
if (config[tim].cb) {
|
if (config[tim].cb) {
|
||||||
config[tim].cb(config[tim].arg, 0);
|
config[tim].cb(config[tim].arg, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((status & TC_INTFLAG_MC1) && tc->INTENSET.bit.MC1) {
|
if ((status & TC_INTFLAG_MC1) && tc->INTENSET.bit.MC1) {
|
||||||
tc->INTENCLR.reg = TC_INTENCLR_MC1;
|
|
||||||
|
if (is_oneshot(tim, 1)) {
|
||||||
|
tc->INTENCLR.reg = TC_INTENCLR_MC1;
|
||||||
|
}
|
||||||
|
|
||||||
if (config[tim].cb) {
|
if (config[tim].cb) {
|
||||||
config[tim].cb(config[tim].arg, 1);
|
config[tim].cb(config[tim].arg, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
#define PERIPH_TIMER_H
|
#define PERIPH_TIMER_H
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "periph_cpu.h"
|
#include "periph_cpu.h"
|
||||||
/** @todo remove dev_enums.h include once all platforms are ported to the updated periph interface */
|
/** @todo remove dev_enums.h include once all platforms are ported to the updated periph interface */
|
||||||
@ -69,6 +70,26 @@ extern "C" {
|
|||||||
typedef unsigned int tim_t;
|
typedef unsigned int tim_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the timer when the set() function is called
|
||||||
|
*
|
||||||
|
* When set, calling the timer_set_periodic() function resets the timer count value.
|
||||||
|
*/
|
||||||
|
#ifndef TIM_FLAG_RESET_ON_SET
|
||||||
|
#define TIM_FLAG_RESET_ON_SET (0x01)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the timer on match
|
||||||
|
*
|
||||||
|
* When set, a match on this channel will reset the timer count value.
|
||||||
|
* When set on multiple channels, only the channel with the lowest match value
|
||||||
|
* will be reached.
|
||||||
|
*/
|
||||||
|
#ifndef TIM_FLAG_RESET_ON_MATCH
|
||||||
|
#define TIM_FLAG_RESET_ON_MATCH (0x02)
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Signature of event callback functions triggered from interrupts
|
* @brief Signature of event callback functions triggered from interrupts
|
||||||
*
|
*
|
||||||
@ -138,6 +159,21 @@ int timer_set(tim_t dev, int channel, unsigned int timeout);
|
|||||||
*/
|
*/
|
||||||
int timer_set_absolute(tim_t dev, int channel, unsigned int value);
|
int timer_set_absolute(tim_t dev, int channel, unsigned int value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set an absolute timeout value for the given channel of the given timer
|
||||||
|
* The timeout will be called periodically for each iteration
|
||||||
|
*
|
||||||
|
* @param[in] dev the timer device to set
|
||||||
|
* @param[in] channel the channel to set
|
||||||
|
* @param[in] value the absolute compare value when the callback will be
|
||||||
|
* triggered
|
||||||
|
* @param[in] flags options
|
||||||
|
*
|
||||||
|
* @return 0 on success
|
||||||
|
* @return -1 on error
|
||||||
|
*/
|
||||||
|
int timer_set_periodic(tim_t dev, int channel, unsigned int value, uint8_t flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clear the given channel of the given timer device
|
* @brief Clear the given channel of the given timer device
|
||||||
*
|
*
|
||||||
|
|||||||
5
tests/periph_timer_periodic/Makefile
Normal file
5
tests/periph_timer_periodic/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
include ../Makefile.tests_common
|
||||||
|
|
||||||
|
FEATURES_REQUIRED = periph_timer_periodic
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.include
|
||||||
107
tests/periph_timer_periodic/main.c
Normal file
107
tests/periph_timer_periodic/main.c
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Beuth Hochschule für Technik 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
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup tests
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Periodic timer test application
|
||||||
|
*
|
||||||
|
* @author Benjamin Valentin <benpicco@beuth-hochschule.de>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "board.h"
|
||||||
|
#include "test_utils/expect.h"
|
||||||
|
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "periph/timer.h"
|
||||||
|
#include "xtimer.h"
|
||||||
|
|
||||||
|
/* We use the timer used for xtimer with the frequency used by xtimer here
|
||||||
|
* to make sure we have a known valid timer configuration.
|
||||||
|
*
|
||||||
|
* DO NOT USE any low-level timer functions demonstrated here when xtimer
|
||||||
|
* is used with that timer!
|
||||||
|
* Configure a separate timer, XTIMER_DEV is usually 'owned' by xtimer, but
|
||||||
|
* as xtimer is not used in this test, we can use it and the fact that every board
|
||||||
|
* provides a configuration for it.
|
||||||
|
*/
|
||||||
|
#define TIMER_CYCL XTIMER_DEV
|
||||||
|
#define CYCLE_MS 100UL
|
||||||
|
#define CYCLES_MAX 10
|
||||||
|
|
||||||
|
static unsigned count[TIMER_CHANNELS];
|
||||||
|
|
||||||
|
static void cb(void *arg, int chan)
|
||||||
|
{
|
||||||
|
unsigned c = count[chan]++;
|
||||||
|
|
||||||
|
printf("[%d] tick\n", chan);
|
||||||
|
|
||||||
|
if (c > CYCLES_MAX) {
|
||||||
|
timer_stop(TIMER_CYCL);
|
||||||
|
mutex_unlock(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* _print_ok(int chan, bool *succeeded)
|
||||||
|
{
|
||||||
|
if (chan == 0 && count[chan] > 0) {
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chan > 0 && count[chan] == 0) {
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
*succeeded = false;
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
mutex_t lock = MUTEX_INIT_LOCKED;
|
||||||
|
const unsigned long timer_hz = XTIMER_HZ;
|
||||||
|
const unsigned steps = (CYCLE_MS * timer_hz) / 1000;
|
||||||
|
|
||||||
|
printf("\nRunning Timer %d at %lu Hz.\n", TIMER_CYCL, timer_hz);
|
||||||
|
printf("One counter cycle is %u ticks or %lu ms\n", steps, CYCLE_MS);
|
||||||
|
puts("Will print 'tick' every cycle.\n");
|
||||||
|
|
||||||
|
expect(timer_init(TIMER_CYCL, timer_hz, cb, &lock) == 0);
|
||||||
|
|
||||||
|
puts("TEST START");
|
||||||
|
|
||||||
|
/* Only the first channel should trigger and reset the counter */
|
||||||
|
/* If subsequent channels trigger this is an error. */
|
||||||
|
timer_set_periodic(TIMER_CYCL, 1, 2 * steps, TIM_FLAG_RESET_ON_SET);
|
||||||
|
timer_set_periodic(TIMER_CYCL, 0, steps, TIM_FLAG_RESET_ON_MATCH);
|
||||||
|
|
||||||
|
mutex_lock(&lock);
|
||||||
|
|
||||||
|
puts("\nCycles:");
|
||||||
|
|
||||||
|
bool succeeded = true;
|
||||||
|
for (unsigned i = 0; i < TIMER_CHANNELS; ++i) {
|
||||||
|
printf("channel %u = %02u\t[%s]\n", i, count[i], _print_ok(i, &succeeded));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (succeeded) {
|
||||||
|
puts("TEST SUCCEEDED");
|
||||||
|
} else {
|
||||||
|
puts("TEST FAILED");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
25
tests/periph_timer_periodic/tests/01-run.py
Executable file
25
tests/periph_timer_periodic/tests/01-run.py
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Copyright (C) 2020 Benjamin Valentin <benpicco@beuth-hochschule.de>
|
||||||
|
#
|
||||||
|
# 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 details.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from testrunner import run
|
||||||
|
|
||||||
|
|
||||||
|
def testfunc(child):
|
||||||
|
child.expect_exact('TEST START')
|
||||||
|
start = time.time()
|
||||||
|
child.expect_exact('TEST SUCCEEDED')
|
||||||
|
end = time.time()
|
||||||
|
# test should run 10 cycles with 100ms each
|
||||||
|
assert (end - start) > 1
|
||||||
|
assert (end - start) < 1.5
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(run(testfunc))
|
||||||
Loading…
x
Reference in New Issue
Block a user