-
Notifications
You must be signed in to change notification settings - Fork 8.1k
drivers: counter: introduce counter capture api #89127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
#include <zephyr/drivers/reset.h> | ||
#include <zephyr/irq.h> | ||
#include <zephyr/sys/atomic.h> | ||
#include <zephyr/drivers/pinctrl.h> | ||
|
||
#include <stm32_ll_tim.h> | ||
#include <stm32_ll_rcc.h> | ||
|
@@ -53,6 +54,14 @@ static uint32_t(*const get_timer_compare[TIMER_MAX_CH])(TIM_TypeDef *) = { | |
LL_TIM_OC_GetCompareCH3, LL_TIM_OC_GetCompareCH4, | ||
}; | ||
#endif | ||
|
||
#ifdef CONFIG_COUNTER_CAPTURE | ||
static uint32_t (*const get_timer_capture[TIMER_MAX_CH])(const TIM_TypeDef *) = { | ||
LL_TIM_IC_GetCaptureCH1, LL_TIM_IC_GetCaptureCH2, | ||
LL_TIM_IC_GetCaptureCH3, LL_TIM_IC_GetCaptureCH4, | ||
}; | ||
#endif /* CONFIG_COUNTER_CAPTURE */ | ||
|
||
/** Channel to interrupt enable function mapping. */ | ||
static void(*const enable_it[TIMER_MAX_CH])(TIM_TypeDef *) = { | ||
LL_TIM_EnableIT_CC1, LL_TIM_EnableIT_CC2, | ||
|
@@ -96,6 +105,9 @@ struct counter_stm32_data { | |
|
||
struct counter_stm32_ch_data { | ||
counter_alarm_callback_t callback; | ||
#ifdef CONFIG_COUNTER_CAPTURE | ||
counter_capture_cb_t capture; | ||
#endif /* CONFIG_COUNTER_CAPTURE */ | ||
void *user_data; | ||
}; | ||
|
||
|
@@ -110,6 +122,9 @@ struct counter_stm32_config { | |
uint32_t irqn; | ||
/* Reset controller device configuration */ | ||
const struct reset_dt_spec reset; | ||
#ifdef CONFIG_COUNTER_CAPTURE | ||
const struct pinctrl_dev_config *pcfg; | ||
#endif /* CONFIG_COUNTER_CAPTURE */ | ||
|
||
LOG_INSTANCE_PTR_DECLARE(log); | ||
}; | ||
|
@@ -412,6 +427,14 @@ static int counter_stm32_init_timer(const struct device *dev) | |
/* config/enable IRQ */ | ||
cfg->irq_config_func(dev); | ||
|
||
#ifdef CONFIG_COUNTER_CAPTURE | ||
r = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); | ||
if (r < 0) { | ||
LOG_ERR("%s: Counter Capture pinctrl setup failed (%d)", dev->name, r); | ||
return r; | ||
} | ||
#endif /* CONFIG_COUNTER_CAPTURE */ | ||
|
||
/* initialize timer */ | ||
LL_TIM_StructInit(&init); | ||
|
||
|
@@ -465,29 +488,121 @@ static void counter_stm32_top_irq_handle(const struct device *dev) | |
cb(dev, data->top_user_data); | ||
} | ||
|
||
static void counter_stm32_alarm_irq_handle(const struct device *dev, uint32_t id) | ||
static void counter_stm32_irq_handle(const struct device *dev, uint32_t id) | ||
{ | ||
const struct counter_stm32_config *config = dev->config; | ||
struct counter_stm32_data *data = dev->data; | ||
TIM_TypeDef *timer = config->timer; | ||
|
||
struct counter_stm32_ch_data *chdata; | ||
counter_alarm_callback_t cb; | ||
|
||
atomic_and(&data->cc_int_pending, ~BIT(id)); | ||
disable_it[id](timer); | ||
|
||
chdata = &config->ch_data[id]; | ||
cb = chdata->callback; | ||
chdata->callback = NULL; | ||
|
||
if (cb) { | ||
uint32_t cc_val = get_timer_compare[id](timer); | ||
#ifdef CONFIG_COUNTER_CAPTURE | ||
etienne-lms marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/* With Counter Capture, we need to check which mode it was configured for */ | ||
if (LL_TIM_IC_GetActiveInput(timer, LL_TIM_CHANNEL_CH1 << (4 * id)) == | ||
LL_TIM_ACTIVEINPUT_DIRECTTI) { | ||
counter_capture_cb_t cb = chdata->capture; | ||
|
||
/* TODO: check overcapture flag, then print error? | ||
* CC1OF is also set if at least two consecutive captures | ||
* occurred whereas the flag was not cleared | ||
*/ | ||
|
||
if (cb) { | ||
uint32_t cc_val = get_timer_capture[id](timer); | ||
uint32_t pol = LL_TIM_IC_GetPolarity(timer, LL_TIM_CHANNEL_CH1 << (4 * id)); | ||
uint32_t flags = 0; | ||
|
||
/* translate stm32 flags to zephyr flags */ | ||
if (pol == LL_TIM_IC_POLARITY_RISING) { | ||
flags = COUNTER_CAPTURE_RISING_EDGE; | ||
} else if (pol == LL_TIM_IC_POLARITY_FALLING) { | ||
flags = COUNTER_CAPTURE_FALLING_EDGE; | ||
} else if (pol == LL_TIM_IC_POLARITY_BOTHEDGE) { | ||
flags = COUNTER_CAPTURE_BOTH_EDGES; | ||
} | ||
|
||
cb(dev, id, flags, cc_val, chdata->user_data); | ||
} | ||
} else { | ||
etienne-lms marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#endif | ||
counter_alarm_callback_t cb; | ||
|
||
/* Alarm is One-Shot, so disable the Interrupt */ | ||
disable_it[id](timer); | ||
|
||
cb = chdata->callback; | ||
chdata->callback = NULL; | ||
|
||
cb(dev, id, cc_val, chdata->user_data); | ||
if (cb) { | ||
uint32_t cc_val = get_timer_compare[id](timer); | ||
|
||
cb(dev, id, cc_val, chdata->user_data); | ||
} | ||
#ifdef CONFIG_COUNTER_CAPTURE | ||
} | ||
#endif | ||
} | ||
|
||
#ifdef CONFIG_COUNTER_CAPTURE | ||
etienne-lms marked this conversation as resolved.
Show resolved
Hide resolved
|
||
static int counter_stm32_capture_enable(const struct device *dev, uint8_t chan) | ||
{ | ||
const struct counter_stm32_config *config = dev->config; | ||
TIM_TypeDef *timer = config->timer; | ||
|
||
LL_TIM_CC_EnableChannel(timer, LL_TIM_CHANNEL_CH1 << (4 * chan)); | ||
|
||
return 0; | ||
} | ||
|
||
static int counter_stm32_capture_disable(const struct device *dev, uint8_t chan) | ||
{ | ||
const struct counter_stm32_config *config = dev->config; | ||
TIM_TypeDef *timer = config->timer; | ||
|
||
LL_TIM_CC_DisableChannel(timer, LL_TIM_CHANNEL_CH1 << (4 * chan)); | ||
|
||
return 0; | ||
} | ||
|
||
static int counter_stm32_capture_callback_set(const struct device *dev, uint8_t chan, | ||
uint32_t flags, counter_capture_cb_t cb, | ||
void *user_data) | ||
{ | ||
const struct counter_stm32_config *config = dev->config; | ||
struct counter_stm32_ch_data *chdata = &config->ch_data[chan]; | ||
TIM_TypeDef *timer = config->timer; | ||
uint32_t config_flags = LL_TIM_ACTIVEINPUT_DIRECTTI; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be worth the call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it shouldn't be disabling the interrupt in the case where it's being used as a counter capture, the alarm is a one-shot alarm just disables it in that case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking of preventing the interrupt to trigger in between |
||
|
||
chdata->capture = cb; | ||
chdata->user_data = user_data; | ||
|
||
if ((flags & COUNTER_CAPTURE_BOTH_EDGES) == COUNTER_CAPTURE_BOTH_EDGES) { | ||
config_flags |= LL_TIM_IC_POLARITY_BOTHEDGE; | ||
} else if (flags & COUNTER_CAPTURE_FALLING_EDGE) { | ||
config_flags |= LL_TIM_IC_POLARITY_FALLING; | ||
} else if (flags & COUNTER_CAPTURE_RISING_EDGE) { | ||
config_flags |= LL_TIM_IC_POLARITY_RISING; | ||
} | ||
/* TODO: add 'vendor' config flags for filter, prescaler div */ | ||
config_flags |= LL_TIM_ICPSC_DIV1 | LL_TIM_IC_FILTER_FDIV1; | ||
|
||
/* Config is only writable when it is off */ | ||
LL_TIM_CC_DisableChannel(timer, LL_TIM_CHANNEL_CH1 << (4 * chan)); | ||
|
||
LL_TIM_IC_Config(timer, LL_TIM_CHANNEL_CH1 << (4 * chan), config_flags); | ||
|
||
LL_TIM_CC_EnableChannel(timer, LL_TIM_CHANNEL_CH1 << (4 * chan)); | ||
/* enable interrupt */ | ||
enable_it[chan](timer); | ||
|
||
return 0; | ||
} | ||
#endif | ||
|
||
static DEVICE_API(counter, counter_stm32_driver_api) = { | ||
.start = counter_stm32_start, | ||
.stop = counter_stm32_stop, | ||
|
@@ -501,6 +616,11 @@ static DEVICE_API(counter, counter_stm32_driver_api) = { | |
.get_guard_period = counter_stm32_get_guard_period, | ||
.set_guard_period = counter_stm32_set_guard_period, | ||
.get_freq = counter_stm32_get_freq, | ||
#ifdef CONFIG_COUNTER_CAPTURE | ||
.capture_enable = counter_stm32_capture_enable, | ||
.capture_disable = counter_stm32_capture_disable, | ||
.capture_callback_set = counter_stm32_capture_callback_set, | ||
#endif | ||
}; | ||
|
||
#define TIM_IRQ_HANDLE_CC(timx, cc) \ | ||
|
@@ -511,7 +631,7 @@ static DEVICE_API(counter, counter_stm32_driver_api) = { | |
if (hw_irq) { \ | ||
LL_TIM_ClearFlag_CC##cc(timer); \ | ||
} \ | ||
counter_stm32_alarm_irq_handle(dev, cc - 1U); \ | ||
counter_stm32_irq_handle(dev, cc - 1U); \ | ||
} \ | ||
} while (0) | ||
|
||
|
@@ -562,6 +682,8 @@ void counter_stm32_irq_handler(const struct device *dev) | |
BUILD_ASSERT(NUM_CH(TIM(idx)) <= TIMER_MAX_CH, \ | ||
"TIMER too many channels"); \ | ||
\ | ||
IF_ENABLED(CONFIG_COUNTER_CAPTURE, (PINCTRL_DT_INST_DEFINE(idx);)) \ | ||
\ | ||
static struct counter_stm32_data counter##idx##_data; \ | ||
static struct counter_stm32_ch_data counter##idx##_ch_data[TIMER_MAX_CH]; \ | ||
\ | ||
|
@@ -593,6 +715,8 @@ void counter_stm32_irq_handler(const struct device *dev) | |
.irq_config_func = counter_##idx##_stm32_irq_config, \ | ||
.irqn = DT_IRQN(TIMER(idx)), \ | ||
.reset = RESET_DT_SPEC_GET(TIMER(idx)), \ | ||
IF_ENABLED(CONFIG_COUNTER_CAPTURE, \ | ||
(.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx),)) \ | ||
}; \ | ||
\ | ||
DEVICE_DT_INST_DEFINE(idx, \ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** Channel to timestamp capture function mapping. */
Could you add an empty line before
#ifdef ...
above and after#endif
below?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done