cpu/sam0_common: implement timer_set_periodic()

This commit is contained in:
Benjamin Valentin 2020-04-18 23:56:26 +02:00
parent 41a961be22
commit 8486e8c6d1
2 changed files with 103 additions and 3 deletions

View File

@ -5,6 +5,9 @@ endif
# All SAM0 based CPUs provide PM
USEMODULE += pm_layered
# the timer implements timer_set_periodic()
FEATURES_PROVIDED += periph_timer_periodic
# include sam0 common periph drivers
USEMODULE += sam0_common_periph

View File

@ -37,6 +37,23 @@
*/
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)
{
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;
}
/* 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
*/
@ -187,7 +230,52 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value)
break;
default:
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;
}
@ -255,13 +343,22 @@ static inline void timer_isr(tim_t tim)
tc->INTFLAG.reg = status;
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) {
config[tim].cb(config[tim].arg, 0);
}
}
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) {
config[tim].cb(config[tim].arg, 1);
}