drivers: pwm: stm32: support counter modes

Adds support for different timer counter modes.

Signed-off-by: Georgij Cernysiov <geo.cgv@gmail.com>
This commit is contained in:
Georgij Cernysiov 2022-04-11 11:50:22 +02:00 committed by Marti Bolivar
commit 3650664875

View file

@ -60,6 +60,7 @@ struct pwm_stm32_data {
struct pwm_stm32_config { struct pwm_stm32_config {
TIM_TypeDef *timer; TIM_TypeDef *timer;
uint32_t prescaler; uint32_t prescaler;
uint32_t countermode;
struct stm32_pclken pclken; struct stm32_pclken pclken;
const struct pinctrl_dev_config *pcfg; const struct pinctrl_dev_config *pcfg;
#ifdef CONFIG_PWM_CAPTURE #ifdef CONFIG_PWM_CAPTURE
@ -85,7 +86,6 @@ static const uint32_t ch2ll[TIMER_MAX_CH] = {
#endif #endif
}; };
/** Some stm32 mcus have complementary channels : 3 or 4 */ /** Some stm32 mcus have complementary channels : 3 or 4 */
static const uint32_t ch2ll_n[] = { static const uint32_t ch2ll_n[] = {
#if defined(LL_TIM_CHANNEL_CH1N) #if defined(LL_TIM_CHANNEL_CH1N)
@ -126,6 +126,20 @@ static uint32_t get_polarity(pwm_flags_t flags)
return LL_TIM_OCPOLARITY_LOW; return LL_TIM_OCPOLARITY_LOW;
} }
/**
* @brief Check if LL counter mode is center-aligned.
*
* @param ll_countermode LL counter mode.
*
* @return `true` when center-aligned, otherwise `false`.
*/
static inline bool is_center_aligned(const uint32_t ll_countermode)
{
return ((ll_countermode == LL_TIM_COUNTERMODE_CENTER_DOWN) ||
(ll_countermode == LL_TIM_COUNTERMODE_CENTER_UP) ||
(ll_countermode == LL_TIM_COUNTERMODE_CENTER_UP_DOWN));
}
/** /**
* Obtain timer clock speed. * Obtain timer clock speed.
* *
@ -231,6 +245,21 @@ static int pwm_stm32_pin_set(const struct device *dev, uint32_t pwm,
return -EINVAL; return -EINVAL;
} }
if (cfg->countermode == LL_TIM_COUNTERMODE_UP) {
/* remove 1 period cycle, accounts for 1 extra low cycle */
period_cycles -= 1U;
} else if (cfg->countermode == LL_TIM_COUNTERMODE_DOWN) {
/* remove 1 pulse cycle, accounts for 1 extra high cycle */
pulse_cycles -= 1U;
/* remove 1 period cycle, accounts for 1 extra low cycle */
period_cycles -= 1U;
} else if (is_center_aligned(cfg->countermode)) {
pulse_cycles /= 2U;
period_cycles /= 2U;
} else {
return -ENOTSUP;
}
/* /*
* Non 32-bit timers count from 0 up to the value in the ARR register * Non 32-bit timers count from 0 up to the value in the ARR register
* (16-bit). Thus period_cycles cannot be greater than UINT16_MAX + 1. * (16-bit). Thus period_cycles cannot be greater than UINT16_MAX + 1.
@ -312,13 +341,13 @@ static int pwm_stm32_pin_set(const struct device *dev, uint32_t pwm,
LL_TIM_EnableARRPreload(cfg->timer); LL_TIM_EnableARRPreload(cfg->timer);
/* in LL_TIM_OC_EnablePreload, the channel is always the non-complementary */ /* in LL_TIM_OC_EnablePreload, the channel is always the non-complementary */
LL_TIM_OC_EnablePreload(cfg->timer, channel); LL_TIM_OC_EnablePreload(cfg->timer, channel);
LL_TIM_SetAutoReload(cfg->timer, period_cycles - 1u); LL_TIM_SetAutoReload(cfg->timer, period_cycles);
LL_TIM_GenerateEvent_UPDATE(cfg->timer); LL_TIM_GenerateEvent_UPDATE(cfg->timer);
} else { } else {
/* in LL_TIM_OC_SetPolarity, the channel could be the complementary one */ /* in LL_TIM_OC_SetPolarity, the channel could be the complementary one */
LL_TIM_OC_SetPolarity(cfg->timer, current_channel, get_polarity(flags)); LL_TIM_OC_SetPolarity(cfg->timer, current_channel, get_polarity(flags));
set_timer_compare[pwm - 1u](cfg->timer, pulse_cycles); set_timer_compare[pwm - 1u](cfg->timer, pulse_cycles);
LL_TIM_SetAutoReload(cfg->timer, period_cycles - 1u); LL_TIM_SetAutoReload(cfg->timer, period_cycles);
} }
return 0; return 0;
@ -629,7 +658,7 @@ static int pwm_stm32_init(const struct device *dev)
LL_TIM_StructInit(&init); LL_TIM_StructInit(&init);
init.Prescaler = cfg->prescaler; init.Prescaler = cfg->prescaler;
init.CounterMode = LL_TIM_COUNTERMODE_UP; init.CounterMode = cfg->countermode;
init.Autoreload = 0u; init.Autoreload = 0u;
init.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; init.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
@ -695,6 +724,7 @@ replaced by 'st,prescaler' property in parent node, aka timers"
/* if exist, otherwise use parent (timers) property */ \ /* if exist, otherwise use parent (timers) property */ \
.prescaler = DT_INST_PROP_OR(index, st_prescaler, \ .prescaler = DT_INST_PROP_OR(index, st_prescaler, \
(DT_PROP(DT_INST_PARENT(index), st_prescaler))), \ (DT_PROP(DT_INST_PARENT(index), st_prescaler))), \
.countermode = DT_PROP(DT_INST_PARENT(index), st_countermode), \
.pclken = DT_INST_CLK(index, timer), \ .pclken = DT_INST_CLK(index, timer), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
CAPTURE_INIT(index) \ CAPTURE_INIT(index) \