Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 58 additions & 26 deletions drivers/pwm/pwm_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <init.h>

#include <drivers/clock_control/stm32_clock_control.h>
#include <dt-bindings/pwm/stm32_pwm.h>

#include <logging/log.h>
LOG_MODULE_REGISTER(pwm_stm32, CONFIG_PWM_LOG_LEVEL);
Expand All @@ -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. */
Expand All @@ -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,
Expand All @@ -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) = {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
}
Expand Down
8 changes: 7 additions & 1 deletion dts/bindings/pwm/st,stm32-pwm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 5 additions & 1 deletion include/drivers/pwm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/dt-bindings/pwm/pwm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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). */
Expand Down
26 changes: 26 additions & 0 deletions include/dt-bindings/pwm/stm32_pwm.h
Original file line number Diff line number Diff line change
@@ -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_ */
32 changes: 32 additions & 0 deletions samples/basic/blinky_pwm/boards/nucleo_l4r5zi.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2022 STMicroelectronics
*/

#include <dt-bindings/pwm/pwm.h>
#include <dt-bindings/pwm/stm32_pwm.h>

/ {
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";
};
};