From 90d1644a10ac6eb80ee2e8685489f658436a501b Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Tue, 25 Jan 2022 10:49:28 +0100 Subject: [PATCH] drivers: pwm: stm32 can initialize the PWM complementary output If the dts defines the PWM complementary output, then the OCN must be init in place of the OC state and polarity. This is an exclusive setting for this pin. The channel in LL_TIM_OC_SetPolarity can be the complementary one. Signed-off-by: Francois Ramu --- drivers/pwm/pwm_stm32.c | 84 ++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/drivers/pwm/pwm_stm32.c b/drivers/pwm/pwm_stm32.c index 85e7911dff7..70f4b19d032 100644 --- a/drivers/pwm/pwm_stm32.c +++ b/drivers/pwm/pwm_stm32.c @@ -19,6 +19,7 @@ #include #include +#include #include LOG_MODULE_REGISTER(pwm_stm32, CONFIG_PWM_LOG_LEVEL); @@ -40,6 +41,10 @@ struct pwm_stm32_capture_data { bool capture_pulse; bool continuous; }; + +/* first capture is always nonsense, second is nonsense when polarity changed */ +#define SKIPPED_PWM_CAPTURES 2u + #endif /*CONFIG_PWM_CAPTURE*/ /** PWM data. */ @@ -66,34 +71,15 @@ struct pwm_stm32_config { #endif /* CONFIG_PWM_CAPTURE */ }; -/** Series F3, F7, G0, G4, H7, L4, MP1 and WB have up to 6 channels, others up - * to 4. - */ -#if defined(CONFIG_SOC_SERIES_STM32F3X) || \ - defined(CONFIG_SOC_SERIES_STM32F7X) || \ - defined(CONFIG_SOC_SERIES_STM32G0X) || \ - defined(CONFIG_SOC_SERIES_STM32G4X) || \ - defined(CONFIG_SOC_SERIES_STM32H7X) || \ - defined(CONFIG_SOC_SERIES_STM32L4X) || \ - defined(CONFIG_SOC_SERIES_STM32MP1X) || \ - defined(CONFIG_SOC_SERIES_STM32WBX) +/** Maximum number of timer channels : some stm32 soc have 6 else only 4 */ +#if defined(LL_TIM_CHANNEL_CH6) #define TIMER_HAS_6CH 1 -#else -#define TIMER_HAS_6CH 0 -#endif - -/** Maximum number of timer channels. */ -#if TIMER_HAS_6CH #define TIMER_MAX_CH 6u #else +#define TIMER_HAS_6CH 0 #define TIMER_MAX_CH 4u #endif -/* first capture is always nonsense, second is nonsense when polarity changed */ -#ifdef CONFIG_PWM_CAPTURE -#define SKIPPED_PWM_CAPTURES 2u -#endif /* CONFIG_PWM_CAPTURE */ - /** Channel to LL mapping. */ static const uint32_t ch2ll[TIMER_MAX_CH] = { LL_TIM_CHANNEL_CH1, LL_TIM_CHANNEL_CH2, @@ -103,6 +89,21 @@ static const uint32_t ch2ll[TIMER_MAX_CH] = { #endif }; + +/** Some stm32 mcus have complementary channels : 3 or 4 */ +static const uint32_t ch2ll_n[] = { +#if defined(LL_TIM_CHANNEL_CH1N) + LL_TIM_CHANNEL_CH1N, + LL_TIM_CHANNEL_CH2N, + LL_TIM_CHANNEL_CH3N, +#if defined(LL_TIM_CHANNEL_CH4N) +/** stm32g4x and stm32u5x have 4 complementary channels */ + LL_TIM_CHANNEL_CH4N, +#endif /* LL_TIM_CHANNEL_CH4N */ +#endif /* LL_TIM_CHANNEL_CH1N */ +}; +/** Maximum number of complemented timer channels is ARRAY_SIZE(ch2ll_n)*/ + /** Channel to compare set function mapping. */ static void (*const set_timer_compare[TIMER_MAX_CH])(TIM_TypeDef *, uint32_t) = { @@ -227,6 +228,7 @@ static int pwm_stm32_pin_set(const struct device *dev, uint32_t pwm, const struct pwm_stm32_config *cfg = dev->config; uint32_t channel; + uint32_t current_channel; /* complementary output if used */ if (pwm < 1u || pwm > TIMER_MAX_CH) { LOG_ERR("Invalid channel (%d)", pwm); @@ -259,20 +261,47 @@ static int pwm_stm32_pin_set(const struct device *dev, uint32_t pwm, channel = ch2ll[pwm - 1u]; + /* in LL_TIM_CC_DisableChannel and LL_TIM_CC_IsEnabledChannel, + * the channel param could be the complementary one + */ + if ((flags & PWM_STM32_COMPLEMENTARY_MASK) == PWM_STM32_COMPLEMENTARY) { + if (pwm > ARRAY_SIZE(ch2ll_n)) { + /* setting a flag on a channel that has not this capability */ + LOG_ERR("Channel %d has NO complementary output", pwm); + return -EINVAL; + } + current_channel = ch2ll_n[pwm - 1u]; + } else { + current_channel = channel; + } + if (period_cycles == 0u) { - LL_TIM_CC_DisableChannel(cfg->timer, channel); + LL_TIM_CC_DisableChannel(cfg->timer, current_channel); return 0; } - if (!LL_TIM_CC_IsEnabledChannel(cfg->timer, channel)) { + if (!LL_TIM_CC_IsEnabledChannel(cfg->timer, current_channel)) { LL_TIM_OC_InitTypeDef oc_init; LL_TIM_OC_StructInit(&oc_init); oc_init.OCMode = LL_TIM_OCMODE_PWM1; + +#if defined(LL_TIM_CHANNEL_CH1N) + /* the flags holds the PWM_STM32_COMPLEMENTARY information */ + if ((flags & PWM_STM32_COMPLEMENTARY_MASK) == PWM_STM32_COMPLEMENTARY) { + oc_init.OCNState = LL_TIM_OCSTATE_ENABLE; + oc_init.OCNPolarity = get_polarity(flags); + } else { + oc_init.OCState = LL_TIM_OCSTATE_ENABLE; + oc_init.OCPolarity = get_polarity(flags); + } +#else /* LL_TIM_CHANNEL_CH1N */ + oc_init.OCState = LL_TIM_OCSTATE_ENABLE; - oc_init.CompareValue = pulse_cycles; oc_init.OCPolarity = get_polarity(flags); +#endif /* LL_TIM_CHANNEL_CH1N */ + oc_init.CompareValue = pulse_cycles; #ifdef CONFIG_PWM_CAPTURE if (IS_TIM_SLAVE_INSTANCE(cfg->timer)) { @@ -283,17 +312,20 @@ static int pwm_stm32_pin_set(const struct device *dev, uint32_t pwm, } #endif /* CONFIG_PWM_CAPTURE */ + /* in LL_TIM_OC_Init, the channel is always the non-complementary */ if (LL_TIM_OC_Init(cfg->timer, channel, &oc_init) != SUCCESS) { LOG_ERR("Could not initialize timer channel output"); return -EIO; } LL_TIM_EnableARRPreload(cfg->timer); + /* in LL_TIM_OC_EnablePreload, the channel is always the non-complementary */ LL_TIM_OC_EnablePreload(cfg->timer, channel); LL_TIM_SetAutoReload(cfg->timer, period_cycles - 1u); LL_TIM_GenerateEvent_UPDATE(cfg->timer); } else { - LL_TIM_OC_SetPolarity(cfg->timer, channel, get_polarity(flags)); + /* in LL_TIM_OC_SetPolarity, the channel could be the complementary one */ + LL_TIM_OC_SetPolarity(cfg->timer, current_channel, get_polarity(flags)); set_timer_compare[pwm - 1u](cfg->timer, pulse_cycles); LL_TIM_SetAutoReload(cfg->timer, period_cycles - 1u); }