diff --git a/drivers/pwm/pwm_stm32.c b/drivers/pwm/pwm_stm32.c index 85e7911dff7e4..70f4b19d032fc 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); } diff --git a/dts/bindings/pwm/st,stm32-pwm.yaml b/dts/bindings/pwm/st,stm32-pwm.yaml index 5ad0300a53226..c5210485d6b9f 100644 --- a/dts/bindings/pwm/st,stm32-pwm.yaml +++ b/dts/bindings/pwm/st,stm32-pwm.yaml @@ -26,9 +26,15 @@ properties: "#pwm-cells": const: 3 + description: | + Number of items to expect in a PWM + - channel of the timer used for PWM + - period to set in ns + - flags : combination of standard flags like PWM_POLARITY_NORMAL + or specific flags like PWM_STM32_COMPLEMENTARY. As an example for channel2: + <&pwm1 2 100 (PWM_POLARITY_NORMAL | PWM_STM32_COMPLEMENTARY)>; pwm-cells: - channel -# period in terms of nanoseconds - period - flags diff --git a/include/drivers/pwm.h b/include/drivers/pwm.h index 82d144ed11c14..6400bda4603d2 100644 --- a/include/drivers/pwm.h +++ b/include/drivers/pwm.h @@ -64,8 +64,12 @@ extern "C" { /** * @brief Provides a type to hold PWM configuration flags. + * + * The lower 8 bits are used for standard flags. + * The upper 8 bits are reserved for SoC specific flags. */ -typedef uint8_t pwm_flags_t; + +typedef uint16_t pwm_flags_t; /** * @typedef pwm_pin_set_t diff --git a/include/dt-bindings/pwm/pwm.h b/include/dt-bindings/pwm/pwm.h index 92f3157a5ed07..d879654cff99b 100644 --- a/include/dt-bindings/pwm/pwm.h +++ b/include/dt-bindings/pwm/pwm.h @@ -11,6 +11,7 @@ * The `PWM_POLARITY_*` flags are used with pwm_pin_set_cycles(), * pwm_pin_set_usec(), pwm_pin_set_nsec() or pwm_pin_configure_capture() to * specify the polarity of a PWM pin. + * The flags are on the lower 8bits of the pwm_flags_t * @{ */ /** PWM pin normal polarity (active-high pulse). */ diff --git a/include/dt-bindings/pwm/stm32_pwm.h b/include/dt-bindings/pwm/stm32_pwm.h new file mode 100644 index 0000000000000..e342253f2f553 --- /dev/null +++ b/include/dt-bindings/pwm/stm32_pwm.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_PWM_STM32_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_PWM_STM32_H_ + +/** + * @name custom PWM complementary flags for output pins + * This flag can be used with any of the `pwm_pin_set_*` API calls to indicate + * that the PWM signal has to be routed to the complementary output channel. + * This feature is only available on certain SoC families, refer to the + * binding's documentation for more details. + * The custom flags are on the upper 8bits of the pwm_flags_t + * @{ + */ +/** PWM complementary output pin is enabled */ +#define PWM_STM32_COMPLEMENTARY (1U << 8) + +/** @cond INTERNAL_HIDDEN */ +#define PWM_STM32_COMPLEMENTARY_MASK 0x100 +/** @endcond */ +/** @} */ + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_PWM_STM32_H_ */ diff --git a/samples/basic/blinky_pwm/boards/nucleo_l4r5zi.overlay b/samples/basic/blinky_pwm/boards/nucleo_l4r5zi.overlay new file mode 100644 index 0000000000000..7e40a6480089b --- /dev/null +++ b/samples/basic/blinky_pwm/boards/nucleo_l4r5zi.overlay @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2022 STMicroelectronics + */ + +#include +#include + +/ { + pwmleds { + compatible = "pwm-leds"; + + red_pwm_led: red_pwm_led { + pwms = <&pwm1 2 4 (PWM_POLARITY_NORMAL | PWM_STM32_COMPLEMENTARY)>; + }; + }; + + aliases { + pwm-led0 = &red_pwm_led; + }; +}; + +&timers1 { + status = "okay"; + + pwm1: pwm { + status = "okay"; + pinctrl-0 = <&tim1_ch2n_pb14>; + pinctrl-names = "default"; + }; +};