Skip to content

Commit

Permalink
Merge pull request #14391 from benpicco/cpu/stm32-timer_periodic
Browse files Browse the repository at this point in the history
cpu/stm32: implement periph_timer_periodic
  • Loading branch information
maribu committed Aug 10, 2020
2 parents 0064397 + 29f83a6 commit 7d9aed7
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 10 deletions.
1 change: 1 addition & 0 deletions cpu/stm32/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions cpu/stm32/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
127 changes: 121 additions & 6 deletions cpu/stm32/periph/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -74,12 +125,52 @@ 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)
{
Expand All @@ -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;
}

Expand All @@ -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; status; i++) {
uint32_t msk = TIM_SR_CC1IF << i;

/* check if interrupt flag is set */
if ((status & msk) == 0) {
continue;
}
status &= ~msk;

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);
/* 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();
}
Expand Down
2 changes: 2 additions & 0 deletions tests/driver_soft_uart/Makefile.ci
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ BOARD_INSUFFICIENT_MEMORY := \
arduino-nano \
arduino-uno \
atmega328p \
nucleo-f031k6 \
stm32f030f4-demo \
#
2 changes: 1 addition & 1 deletion tests/periph_timer_periodic/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
* provides a configuration for it.
*/
#define TIMER_CYCL XTIMER_DEV
#define CYCLE_MS 100UL
#define CYCLE_MS 25UL
#define CYCLES_MAX 10

static unsigned count[TIMER_CHANNEL_NUMOF];
Expand Down
6 changes: 3 additions & 3 deletions tests/periph_timer_periodic/tests/01-run.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ def testfunc(child):
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
# test should run 10 cycles with 25ms each
assert (end - start) > 0.25
assert (end - start) < 0.35


if __name__ == "__main__":
Expand Down

0 comments on commit 7d9aed7

Please sign in to comment.