Skip to content

Commit ae28ce4

Browse files
drivers: counter: stm32: introduce counter capture api
This introduces apis for input capture on the STM32. This is something agnostic enough that is seen on other vendors such as Atmel's SAM line and others. A use case for this would be time stamping a rising and/or falling edge where precision and lack of jitter is important. The only way to currently do this in zephyr is to set up a GPIO interrupt and then read the counter or kernel time... but this can have limitations of software such as the processor may have a lot going on and can be subject to interrupt latencies. Having a hardware 'latch' done of the rising and/or falling edge with respect to a timer can eliminate these imprecisions. How this is to be used, is that an application is to call `capture_callback_set` with the configuration of the channel number, flags (such as falling, rising, or both edges), and then a callback function. Then the `capture_enable` function is to be called to enable this. The callback has the timestamp of the edge in ticks and the edge that it captured (rising or falling). Signed-off-by: Ryan McClelland <ryanmcclelland@meta.com>
1 parent 5f689b1 commit ae28ce4

File tree

5 files changed

+255
-10
lines changed

5 files changed

+255
-10
lines changed

drivers/counter/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ menuconfig COUNTER
1010

1111
if COUNTER
1212

13+
config COUNTER_CAPTURE
14+
bool "Enable counter capture support"
15+
help
16+
Enable support for counter capture. This is a feature that allows
17+
hardware timestamping of external events as well as a software
18+
interrupt on the event. This feature is not supported by all
19+
counter drivers.
20+
1321
config COUNTER_INIT_PRIORITY
1422
int "Counter init priority"
1523
default 60

drivers/counter/Kconfig.stm32_timer

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ config COUNTER_TIMER_STM32
77
depends on DT_HAS_ST_STM32_COUNTER_ENABLED
88
select USE_STM32_LL_TIM
99
select RESET
10+
select PINCTRL if COUNTER_CAPTURE
1011
help
1112
Enable the counter driver for STM32 family of processors.

drivers/counter/counter_ll_stm32_timer.c

Lines changed: 128 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <zephyr/drivers/reset.h>
1212
#include <zephyr/irq.h>
1313
#include <zephyr/sys/atomic.h>
14+
#include <zephyr/drivers/pinctrl.h>
1415

1516
#include <stm32_ll_tim.h>
1617
#include <stm32_ll_rcc.h>
@@ -53,6 +54,12 @@ static uint32_t(*const get_timer_compare[TIMER_MAX_CH])(TIM_TypeDef *) = {
5354
LL_TIM_OC_GetCompareCH3, LL_TIM_OC_GetCompareCH4,
5455
};
5556
#endif
57+
#ifdef CONFIG_COUNTER_CAPTURE
58+
static uint32_t (*const get_timer_capture[TIMER_MAX_CH])(const TIM_TypeDef *) = {
59+
LL_TIM_IC_GetCaptureCH1, LL_TIM_IC_GetCaptureCH2,
60+
LL_TIM_IC_GetCaptureCH3, LL_TIM_IC_GetCaptureCH4,
61+
};
62+
#endif /* CONFIG_COUNTER_CAPTURE */
5663
/** Channel to interrupt enable function mapping. */
5764
static void(*const enable_it[TIMER_MAX_CH])(TIM_TypeDef *) = {
5865
LL_TIM_EnableIT_CC1, LL_TIM_EnableIT_CC2,
@@ -96,6 +103,9 @@ struct counter_stm32_data {
96103

97104
struct counter_stm32_ch_data {
98105
counter_alarm_callback_t callback;
106+
#ifdef CONFIG_COUNTER_CAPTURE
107+
counter_capture_cb_t capture;
108+
#endif /* CONFIG_COUNTER_CAPTURE */
99109
void *user_data;
100110
};
101111

@@ -109,6 +119,9 @@ struct counter_stm32_config {
109119
uint32_t irqn;
110120
/* Reset controller device configuration */
111121
const struct reset_dt_spec reset;
122+
#ifdef CONFIG_COUNTER_CAPTURE
123+
const struct pinctrl_dev_config *pcfg;
124+
#endif /* CONFIG_COUNTER_CAPTURE */
112125

113126
LOG_INSTANCE_PTR_DECLARE(log);
114127
};
@@ -497,6 +510,14 @@ static int counter_stm32_init_timer(const struct device *dev)
497510
/* config/enable IRQ */
498511
cfg->irq_config_func(dev);
499512

513+
#ifdef CONFIG_COUNTER_CAPTURE
514+
r = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
515+
if (r < 0) {
516+
LOG_ERR("%s: Counter Capture pinctrl setup failed (%d)", dev->name, r);
517+
return r;
518+
}
519+
#endif /* CONFIG_COUNTER_CAPTURE */
520+
500521
/* initialize timer */
501522
LL_TIM_StructInit(&init);
502523

@@ -570,28 +591,117 @@ static void counter_stm32_top_irq_handle(const struct device *dev)
570591
cb(dev, data->top_user_data);
571592
}
572593

573-
static void counter_stm32_alarm_irq_handle(const struct device *dev, uint32_t id)
594+
static void counter_stm32_irq_handle(const struct device *dev, uint32_t id)
574595
{
575596
const struct counter_stm32_config *config = dev->config;
576597
struct counter_stm32_data *data = dev->data;
577598
TIM_TypeDef *timer = config->timer;
578599

579600
struct counter_stm32_ch_data *chdata;
580-
counter_alarm_callback_t cb;
581601

582602
atomic_and(&data->cc_int_pending, ~BIT(id));
583-
disable_it[id](timer);
584603

585604
chdata = &config->ch_data[id];
586-
cb = chdata->callback;
587-
chdata->callback = NULL;
605+
#ifdef CONFIG_COUNTER_CAPTURE
606+
/* With Counter Capture, we need to check which mode it was configured for */
607+
if (LL_TIM_IC_GetActiveInput(timer, LL_TIM_CHANNEL_CH1 << (4*id)) == LL_TIM_ACTIVEINPUT_DIRECTTI) {
608+
counter_capture_cb_t cb;
609+
610+
cb = chdata->capture;
611+
612+
/* TODO: check overcapture flag, then print error?
613+
* CC1OF is also set if at least two consecutive captures
614+
* occurred whereas the flag was not cleared
615+
*/
616+
617+
if (cb) {
618+
uint32_t cc_val = get_timer_capture[id](timer);
619+
uint32_t pol = LL_TIM_IC_GetPolarity(timer, LL_TIM_CHANNEL_CH1 << (4*id));
620+
uint32_t flags = 0;
621+
622+
/* translate stm32 flags to zephyr flags */
623+
if (pol == LL_TIM_IC_POLARITY_RISING) {
624+
flags = COUNTER_CAPTURE_RISING_EDGE;
625+
} else if (pol == LL_TIM_IC_POLARITY_FALLING) {
626+
flags = COUNTER_CAPTURE_FALLING_EDGE;
627+
} else if (pol == LL_TIM_IC_POLARITY_BOTHEDGE) {
628+
flags = COUNTER_CAPTURE_BOTH_EDGES;
629+
}
630+
631+
cb(dev, id, flags, cc_val, chdata->user_data);
632+
}
633+
} else {
634+
#endif
635+
counter_alarm_callback_t cb;
636+
/* Alarm is One-Shot, so disable the Interrupt */
637+
disable_it[id](timer);
638+
639+
cb = chdata->callback;
640+
chdata->callback = NULL;
641+
642+
if (cb) {
643+
uint32_t cc_val = get_timer_compare[id](timer);
644+
645+
cb(dev, id, cc_val, chdata->user_data);
646+
}
647+
#ifdef CONFIG_COUNTER_CAPTURE
648+
}
649+
#endif
650+
}
651+
#ifdef CONFIG_COUNTER_CAPTURE
652+
static int counter_stm32_capture_enable(const struct device *dev, uint8_t chan)
653+
{
654+
const struct counter_stm32_config *config = dev->config;
655+
TIM_TypeDef *timer = config->timer;
588656

589-
if (cb) {
590-
uint32_t cc_val = get_timer_compare[id](timer);
657+
LL_TIM_CC_EnableChannel(timer, LL_TIM_CHANNEL_CH1 << (4*chan));
591658

592-
cb(dev, id, cc_val, chdata->user_data);
659+
return 0;
660+
}
661+
662+
static int counter_stm32_capture_disable(const struct device *dev, uint8_t chan)
663+
{
664+
const struct counter_stm32_config *config = dev->config;
665+
TIM_TypeDef *timer = config->timer;
666+
667+
LL_TIM_CC_DisableChannel(timer, LL_TIM_CHANNEL_CH1 << (4*chan));
668+
669+
return 0;
670+
}
671+
672+
static int counter_stm32_capture_callback_set(const struct device *dev, uint8_t chan,
673+
uint32_t flags, counter_capture_cb_t cb,
674+
void *user_data)
675+
{
676+
const struct counter_stm32_config *config = dev->config;
677+
struct counter_stm32_ch_data *chdata = &config->ch_data[chan];
678+
TIM_TypeDef *timer = config->timer;
679+
uint32_t config_flags = LL_TIM_ACTIVEINPUT_DIRECTTI;
680+
chdata->capture = cb;
681+
chdata->user_data = user_data;
682+
683+
if ((flags & COUNTER_CAPTURE_BOTH_EDGES) == COUNTER_CAPTURE_BOTH_EDGES) {
684+
config_flags |= LL_TIM_IC_POLARITY_BOTHEDGE;
685+
} else if (flags & COUNTER_CAPTURE_FALLING_EDGE) {
686+
config_flags |= LL_TIM_IC_POLARITY_FALLING;
687+
} else if (flags & COUNTER_CAPTURE_RISING_EDGE) {
688+
config_flags |= LL_TIM_IC_POLARITY_RISING;
593689
}
690+
/* TODO: add 'vendor' config flags for filter, prescaler div */
691+
config_flags |= (LL_TIM_ICPSC_DIV1 | LL_TIM_IC_FILTER_FDIV1);
692+
693+
/* Config is only writable when it is off */
694+
LL_TIM_CC_DisableChannel(timer, LL_TIM_CHANNEL_CH1 << (4*chan));
695+
696+
LL_TIM_IC_Config(timer, LL_TIM_CHANNEL_CH1 << (4*chan), config_flags);
697+
698+
LL_TIM_CC_EnableChannel(timer, LL_TIM_CHANNEL_CH1 << (4*chan));
699+
/* enable interrupt */
700+
enable_it[chan](timer);
701+
702+
return 0;
594703
}
704+
#endif
595705

596706
static DEVICE_API(counter, counter_stm32_driver_api) = {
597707
.start = counter_stm32_start,
@@ -607,6 +717,11 @@ static DEVICE_API(counter, counter_stm32_driver_api) = {
607717
.get_freq = counter_stm32_get_freq,
608718
.reset = counter_stm32_reset_timer,
609719
.set_value = counter_stm32_set_value,
720+
#ifdef CONFIG_COUNTER_CAPTURE
721+
.capture_enable = counter_stm32_capture_enable,
722+
.capture_disable = counter_stm32_capture_disable,
723+
.capture_callback_set = counter_stm32_capture_callback_set,
724+
#endif
610725
};
611726

612727
#define TIM_IRQ_HANDLE_CC(timx, cc) \
@@ -617,7 +732,7 @@ static DEVICE_API(counter, counter_stm32_driver_api) = {
617732
if (hw_irq) { \
618733
LL_TIM_ClearFlag_CC##cc(timer); \
619734
} \
620-
counter_stm32_alarm_irq_handle(dev, cc - 1U); \
735+
counter_stm32_irq_handle(dev, cc - 1U); \
621736
} \
622737
} while (0)
623738

@@ -660,6 +775,8 @@ void counter_stm32_irq_handler(const struct device *dev)
660775
BUILD_ASSERT(NUM_CH(TIM(idx)) <= TIMER_MAX_CH, \
661776
"TIMER too many channels"); \
662777
\
778+
IF_ENABLED(CONFIG_COUNTER_CAPTURE, \
779+
(PINCTRL_DT_INST_DEFINE(idx);)) \
663780
static struct counter_stm32_data counter##idx##_data; \
664781
static struct counter_stm32_ch_data counter##idx##_ch_data[TIMER_MAX_CH]; \
665782
\
@@ -691,6 +808,8 @@ void counter_stm32_irq_handler(const struct device *dev)
691808
.irq_config_func = counter_##idx##_stm32_irq_config, \
692809
.irqn = DT_IRQN(TIMER(idx)), \
693810
.reset = RESET_DT_SPEC_GET(TIMER(idx)), \
811+
IF_ENABLED(CONFIG_COUNTER_CAPTURE, \
812+
(.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx),)) \
694813
}; \
695814
\
696815
DEVICE_DT_INST_DEFINE(idx, \

dts/bindings/counter/st,stm32-counter.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ description: STM32 counters
55

66
compatible: "st,stm32-counter"
77

8-
include: base.yaml
8+
include: [base.yaml, pinctrl-device.yaml]

include/zephyr/drivers/counter.h

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,35 @@ extern "C" {
112112

113113
/**@} */
114114

115+
/**
116+
* @anchor COUNTER_CAPTURE_FLAGS
117+
* @name Counter capture flags
118+
*
119+
* @brief Used by @ref counter_capture_callback_set.
120+
* @{
121+
*/
122+
123+
/**
124+
* @brief Capture rising edge of an external input signal
125+
*/
126+
#define COUNTER_CAPTURE_RISING_EDGE BIT(0)
127+
128+
/**
129+
* @brief Capture falling edge of an external input signal
130+
*/
131+
#define COUNTER_CAPTURE_FALLING_EDGE BIT(1)
132+
133+
/**
134+
* @brief Capture both falling and rising edge of an external input signal
135+
*/
136+
#define COUNTER_CAPTURE_BOTH_EDGES (COUNTER_CAPTURE_FALLING_EDGE | COUNTER_CAPTURE_RISING_EDGE)
137+
138+
/**@} */
139+
140+
typedef void (*counter_capture_cb_t)(const struct device *dev, uint8_t chan,
141+
uint32_t flags, uint64_t ticks,
142+
void *user_data);
143+
115144
/** @brief Alarm callback
116145
*
117146
* @param dev Pointer to the device structure for the driver instance.
@@ -235,6 +264,15 @@ typedef int (*counter_api_set_guard_period)(const struct device *dev,
235264
uint32_t ticks,
236265
uint32_t flags);
237266
typedef uint32_t (*counter_api_get_freq)(const struct device *dev);
267+
typedef int (*counter_api_capture_callback_set)(const struct device *dev,
268+
uint8_t chan,
269+
uint32_t flags,
270+
counter_capture_cb_t cb,
271+
void *user_data);
272+
typedef int (*counter_api_capture_enable)(const struct device *dev,
273+
uint8_t chan);
274+
typedef int (*counter_api_capture_disable)(const struct device *dev,
275+
uint8_t chan);
238276

239277
__subsystem struct counter_driver_api {
240278
counter_api_start start;
@@ -252,6 +290,11 @@ __subsystem struct counter_driver_api {
252290
counter_api_get_guard_period get_guard_period;
253291
counter_api_set_guard_period set_guard_period;
254292
counter_api_get_freq get_freq;
293+
#ifdef CONFIG_COUNTER_CAPTURE
294+
counter_api_capture_callback_set capture_callback_set;
295+
counter_api_capture_enable capture_enable;
296+
counter_api_capture_disable capture_disable;
297+
#endif
255298
};
256299

257300
/**
@@ -308,6 +351,80 @@ static inline uint32_t z_impl_counter_get_frequency(const struct device *dev)
308351

309352
return api->get_freq ? api->get_freq(dev) : config->freq;
310353
}
354+
#ifdef CONFIG_COUNTER_CAPTURE
355+
__syscall int counter_capture_callback_set(const struct device *dev,
356+
uint8_t chan,
357+
uint32_t flags,
358+
counter_capture_cb_t cb,
359+
void *user_data);
360+
361+
static inline int z_impl_counter_capture_callback_set(const struct device *dev,
362+
uint8_t chan,
363+
uint32_t flags,
364+
counter_capture_cb_t cb,
365+
void *user_data)
366+
{
367+
const struct counter_driver_api *api =
368+
(struct counter_driver_api *)dev->api;
369+
370+
if (!api->capture_callback_set) {
371+
return -ENOTSUP;
372+
}
373+
374+
return api->capture_callback_set(dev, chan, flags, cb, user_data);
375+
}
376+
377+
/**
378+
* @brief Enable capture on a channel.
379+
*
380+
* @param dev Pointer to the device structure for the driver instance.
381+
* @param chan Channel ID.
382+
*
383+
* @retval 0 If successful.
384+
* @retval Negative error code on failure
385+
*/
386+
__syscall int counter_capture_enable(const struct device *dev,
387+
uint8_t chan);
388+
389+
static inline int z_impl_counter_capture_enable(const struct device *dev,
390+
uint8_t chan)
391+
{
392+
const struct counter_driver_api *api =
393+
(struct counter_driver_api *)dev->api;
394+
395+
if (!api->capture_enable) {
396+
return -ENOTSUP;
397+
}
398+
399+
return api->capture_enable(dev, chan);
400+
}
401+
402+
/**
403+
* @brief Disable capture on a channel.
404+
*
405+
* @param dev Pointer to the device structure for the driver instance.
406+
* @param chan Channel ID.
407+
*
408+
* @retval 0 If successful.
409+
* @retval Negative error code on failure
410+
*/
411+
__syscall int counter_capture_disable(const struct device *dev,
412+
uint8_t chan);
413+
414+
static inline int z_impl_counter_capture_disable(const struct device *dev,
415+
uint8_t chan)
416+
{
417+
const struct counter_driver_api *api =
418+
(struct counter_driver_api *)dev->api;
419+
420+
if (!api->capture_disable) {
421+
return -ENOTSUP;
422+
}
423+
424+
return api->capture_disable(dev, chan);
425+
}
426+
#endif
427+
311428

312429
/**
313430
* @brief Function to convert microseconds to ticks.

0 commit comments

Comments
 (0)