1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-26 15:03:53 +01:00

cpu/stm32: implement periph_timer_periodic

Seems like the Interrupt flag for a Capture/Compare channel gets set when

- the CC-value is reached
- the timer resets before the CC value is reached.

We only want the first event and ignore the second one. Unfortunately I did
not find a way to disable the second event type, so it is filtered in software.

That is we need to

 - ignore the CC-interrupts when the COUNT register register is reset
 - ignore the CC-interrupts > TOP value/ARR (auto-reload register)
This commit is contained in:
Benjamin Valentin 2020-06-29 22:15:48 +02:00
parent 110a626b97
commit a0972c9e0c
3 changed files with 123 additions and 6 deletions

View File

@ -13,6 +13,7 @@ config CPU_STM32
select HAS_PERIPH_GPIO
select HAS_PERIPH_GPIO_IRQ
select HAS_PUF_SRAM
select HAS_PERIPH_TIMER_PERIODIC
select HAS_PERIPH_UART_MODECFG
select HAS_PERIPH_UART_NONBLOCKING
select HAS_PERIPH_WDT

View File

@ -5,6 +5,7 @@ FEATURES_PROVIDED += cpu_stm32$(CPU_FAM)
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
FEATURES_PROVIDED += puf_sram
FEATURES_PROVIDED += periph_timer_periodic
FEATURES_PROVIDED += periph_uart_modecfg
FEATURES_PROVIDED += periph_uart_nonblocking
FEATURES_PROVIDED += periph_wdt

View File

@ -36,6 +36,57 @@ static inline TIM_TypeDef *dev(tim_t tim)
return timer_config[tim].dev;
}
#ifdef MODULE_PERIPH_TIMER_PERIODIC
/**
* @brief Helper macro to get channel bit in timer/channel bitmap
*/
#define CHAN_BIT(tim, chan) (1 << chan) << (TIMER_CHANNEL_NUMOF * (tim & 1))
/**
* @brief Bitmap for compare channel disable after match
*/
static uint8_t _oneshot[(TIMER_NUMOF+1)/2];
/**
* @brief Clear interrupt enable after the interrupt has fired
*/
static inline void set_oneshot(tim_t tim, int chan)
{
_oneshot[tim >> 1] |= CHAN_BIT(tim, chan);
}
/**
* @brief Enable interrupt with every wrap-around of the timer
*/
static inline void clear_oneshot(tim_t tim, int chan)
{
_oneshot[tim >> 1] &= ~CHAN_BIT(tim, chan);
}
static inline bool is_oneshot(tim_t tim, int chan)
{
return _oneshot[tim >> 1] & CHAN_BIT(tim, chan);
}
#else /* !MODULE_PERIPH_TIMER_PERIODIC */
static inline void set_oneshot(tim_t tim, int chan)
{
(void)tim;
(void)chan;
}
static inline bool is_oneshot(tim_t tim, int chan)
{
(void)tim;
(void)chan;
return true;
}
#endif /* MODULE_PERIPH_TIMER_PERIODIC */
int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
{
/* check if device is valid */
@ -74,13 +125,53 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value)
return -1;
}
set_oneshot(tim, channel);
TIM_CHAN(tim, channel) = (value & timer_config[tim].max);
dev(tim)->SR &= ~(TIM_SR_CC1IF << channel);
#ifdef MODULE_PERIPH_TIMER_PERIODIC
if (dev(tim)->ARR == TIM_CHAN(tim, channel)) {
dev(tim)->ARR = timer_config[tim].max;
}
#endif
dev(tim)->DIER |= (TIM_DIER_CC1IE << channel);
return 0;
}
#ifdef MODULE_PERIPH_TIMER_PERIODIC
int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags)
{
if (channel >= (int)TIMER_CHANNEL_NUMOF) {
return -1;
}
clear_oneshot(tim, channel);
if (flags & TIM_FLAG_RESET_ON_SET) {
/* setting COUNT gives us an interrupt on all channels */
unsigned state = irq_disable();
dev(tim)->CNT = 0;
/* wait for the interrupt & clear it */
while(dev(tim)->SR == 0) {}
dev(tim)->SR = 0;
irq_restore(state);
}
TIM_CHAN(tim, channel) = value;
dev(tim)->DIER |= (TIM_DIER_CC1IE << channel);
if (flags & TIM_FLAG_RESET_ON_MATCH) {
dev(tim)->ARR = value;
}
return 0;
}
#endif /* MODULE_PERIPH_TIMER_PERIODIC */
int timer_clear(tim_t tim, int channel)
{
if (channel >= (int)TIMER_CHANNEL_NUMOF) {
@ -88,6 +179,13 @@ int timer_clear(tim_t tim, int channel)
}
dev(tim)->DIER &= ~(TIM_DIER_CC1IE << channel);
#ifdef MODULE_PERIPH_TIMER_PERIODIC
if (dev(tim)->ARR == TIM_CHAN(tim, channel)) {
dev(tim)->ARR = timer_config[tim].max;
}
#endif
return 0;
}
@ -108,13 +206,30 @@ void timer_stop(tim_t tim)
static inline void irq_handler(tim_t tim)
{
uint32_t status = (dev(tim)->SR & dev(tim)->DIER);
uint32_t top = dev(tim)->ARR;
uint32_t status = dev(tim)->SR & dev(tim)->DIER;
dev(tim)->SR = 0;
for (unsigned int i = 0; i < TIMER_CHANNEL_NUMOF; i++) {
if (status & (TIM_SR_CC1IF << i)) {
dev(tim)->DIER &= ~(TIM_DIER_CC1IE << i);
isr_ctx[tim].cb(isr_ctx[tim].arg, i);
for (unsigned int i = 0; status; i++) {
uint32_t msk = TIM_SR_CC1IF << i;
/* check if interrupt flag is set */
if ((status & msk) == 0) {
continue;
}
status &= ~msk;
/* interrupt flag gets set for all channels > ARR */
if (TIM_CHAN(tim, i) > top) {
continue;
}
/* disable Interrupt */
if (is_oneshot(tim, i)) {
dev(tim)->DIER &= ~msk;
}
isr_ctx[tim].cb(isr_ctx[tim].arg, i);
}
cortexm_isr_end();
}