drivers: pwm: add support for inverted PWM signals

Add support for requesting an inverted PWM pulse (active-low) when
setting up the period and pulse width of a PWM pin. This is useful
when driving external, active-low circuitry (e.g. an LED) with a PWM
signal.

All in-tree PWM drivers is updated to match the new API signature, but
no driver support for inverted PWM signals is added yet.

All in-tree PWM consumers are updated to pass a flags value of 0
(0 meaning default, which is normal PWM polarity).

Fixes #21384.

Signed-off-by: Henrik Brix Andersen <hebad@vestas.com>
This commit is contained in:
Henrik Brix Andersen 2019-11-13 14:46:37 +01:00 committed by Carles Cufí
commit db611e6781
24 changed files with 152 additions and 38 deletions

View file

@ -135,11 +135,13 @@ static int __set_one_port(struct device *dev, u32_t pwm,
* @param pwm Which PWM pin to set
* @param period_cycles Period in clock cycles of the pwm.
* @param pulse_cycles PWM width in clock cycles
* @param flags Flags for pin configuration (polarity).
*
* @return 0
*/
static int pwm_dw_pin_set_cycles(struct device *dev,
u32_t pwm, u32_t period_cycles, u32_t pulse_cycles)
u32_t pwm, u32_t period_cycles,
u32_t pulse_cycles, pwm_flags_t flags)
{
const struct pwm_dw_config * const cfg =
(struct pwm_dw_config *)dev->config->config_info;
@ -151,6 +153,11 @@ static int pwm_dw_pin_set_cycles(struct device *dev,
return -EIO;
}
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
if (period_cycles == 0U || pulse_cycles > period_cycles) {
return -EINVAL;
}

View file

@ -8,11 +8,12 @@
#include <drivers/pwm.h>
static inline int z_vrfy_pwm_pin_set_cycles(struct device *dev, u32_t pwm,
u32_t period, u32_t pulse)
u32_t period, u32_t pulse,
pwm_flags_t flags)
{
Z_OOPS(Z_SYSCALL_DRIVER_PWM(dev, pin_set));
return z_impl_pwm_pin_set_cycles((struct device *)dev, pwm, period,
pulse);
pulse, flags);
}
#include <syscalls/pwm_pin_set_cycles_mrsh.c>

View file

@ -52,7 +52,8 @@ static int imx_pwm_get_cycles_per_sec(struct device *dev, u32_t pwm,
}
static int imx_pwm_pin_set(struct device *dev, u32_t pwm,
u32_t period_cycles, u32_t pulse_cycles)
u32_t period_cycles, u32_t pulse_cycles,
pwm_flags_t flags)
{
PWM_Type *base = DEV_BASE(dev);
const struct imx_pwm_config *config = DEV_CFG(dev);
@ -69,6 +70,11 @@ static int imx_pwm_pin_set(struct device *dev, u32_t pwm,
return -EINVAL;
}
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
LOG_DBG("enabled=%d, pulse_cycles=%d, period_cycles=%d,"
" duty_cycle=%d\n", enabled, pulse_cycles, period_cycles,
(pulse_cycles * 100U / period_cycles));

View file

@ -311,7 +311,7 @@ static int pwm_led_esp32_timer_set(int speed_mode, int timer,
/* period_cycles is not used, set frequency on menuconfig instead. */
static int pwm_led_esp32_pin_set_cycles(struct device *dev,
u32_t pwm, u32_t period_cycles,
u32_t pulse_cycles)
u32_t pulse_cycles, pwm_flags_t flags)
{
int speed_mode;
int channel;
@ -322,6 +322,11 @@ static int pwm_led_esp32_pin_set_cycles(struct device *dev,
ARG_UNUSED(period_cycles);
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
channel = pwm_led_esp32_get_gpio_config(pwm, config->ch_cfg);
if (channel < 0) {
return -EINVAL;

View file

@ -305,7 +305,8 @@ done:
}
static int pwm_xec_pin_set(struct device *dev, u32_t pwm,
u32_t period_cycles, u32_t pulse_cycles)
u32_t period_cycles, u32_t pulse_cycles,
pwm_flags_t flags)
{
PWM_Type *pwm_regs = PWM_XEC_REG_BASE(dev);
u32_t target_freq;
@ -319,6 +320,11 @@ static int pwm_xec_pin_set(struct device *dev, u32_t pwm,
return -EINVAL;
}
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
on = pulse_cycles;
off = period_cycles - pulse_cycles;

View file

@ -30,7 +30,8 @@ struct pwm_mcux_data {
};
static int mcux_pwm_pin_set(struct device *dev, u32_t pwm,
u32_t period_cycles, u32_t pulse_cycles)
u32_t period_cycles, u32_t pulse_cycles,
pwm_flags_t flags)
{
const struct pwm_mcux_config *config = dev->config->config_info;
struct pwm_mcux_data *data = dev->driver_data;
@ -41,6 +42,11 @@ static int mcux_pwm_pin_set(struct device *dev, u32_t pwm,
return -EINVAL;
}
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
if ((period_cycles == 0) || (pulse_cycles > period_cycles)) {
LOG_ERR("Invalid combination: period_cycles=%u, "
"pulse_cycles=%u", period_cycles, pulse_cycles);

View file

@ -34,7 +34,8 @@ struct mcux_ftm_data {
};
static int mcux_ftm_pin_set(struct device *dev, u32_t pwm,
u32_t period_cycles, u32_t pulse_cycles)
u32_t period_cycles, u32_t pulse_cycles,
pwm_flags_t flags)
{
const struct mcux_ftm_config *config = dev->config->config_info;
struct mcux_ftm_data *data = dev->driver_data;
@ -51,6 +52,11 @@ static int mcux_ftm_pin_set(struct device *dev, u32_t pwm,
return -ENOTSUP;
}
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
duty_cycle = pulse_cycles * 100U / period_cycles;
data->channel[pwm].dutyCyclePercent = duty_cycle;

View file

@ -87,7 +87,8 @@ static u8_t pwm_channel_map(struct pwm_data *data, u8_t map_size,
}
static int pwm_nrf5_sw_pin_set(struct device *dev, u32_t pwm,
u32_t period_cycles, u32_t pulse_cycles)
u32_t period_cycles, u32_t pulse_cycles,
pwm_flags_t flags)
{
struct pwm_config *config;
NRF_TIMER_Type *timer;
@ -101,6 +102,11 @@ static int pwm_nrf5_sw_pin_set(struct device *dev, u32_t pwm,
timer = config->timer;
data = dev->driver_data;
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
/* check if requested period is allowed while other channels are
* active.
*/

View file

@ -123,7 +123,8 @@ static bool any_other_channel_is_active(u8_t channel,
}
static int pwm_nrfx_pin_set(struct device *dev, u32_t pwm,
u32_t period_cycles, u32_t pulse_cycles)
u32_t period_cycles, u32_t pulse_cycles,
pwm_flags_t flags)
{
/* We assume here that period_cycles will always be 16MHz
* peripheral clock. Since pwm_nrfx_get_cycles_per_sec() function might
@ -134,6 +135,11 @@ static int pwm_nrfx_pin_set(struct device *dev, u32_t pwm,
struct pwm_nrfx_data *data = dev->driver_data;
u8_t channel;
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
/* Check if PWM pin is one of the predefiend DTS config pins.
* Return its array index (channel number),
* or NRF_PWM_CHANNEL_COUNT if not initialized through DTS.

View file

@ -61,7 +61,8 @@ static inline int has_i2c_master(struct device *dev)
* value to pulse_count
*/
static int pwm_pca9685_pin_set_cycles(struct device *dev, u32_t pwm,
u32_t period_count, u32_t pulse_count)
u32_t period_count, u32_t pulse_count,
pwm_flags_t flags)
{
const struct pwm_pca9685_config * const config =
dev->config->config_info;
@ -76,6 +77,11 @@ static int pwm_pca9685_pin_set_cycles(struct device *dev, u32_t pwm,
return -EINVAL;
}
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
if (pwm > MAX_PWM_OUT) {
return -EINVAL;
}

View file

@ -36,7 +36,8 @@ static int sam_pwm_get_cycles_per_sec(struct device *dev, u32_t pwm,
}
static int sam_pwm_pin_set(struct device *dev, u32_t ch,
u32_t period_cycles, u32_t pulse_cycles)
u32_t period_cycles, u32_t pulse_cycles,
pwm_flags_t flags)
{
Pwm *const pwm = DEV_CFG(dev)->regs;
@ -44,6 +45,11 @@ static int sam_pwm_pin_set(struct device *dev, u32_t ch,
return -EINVAL;
}
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
if (period_cycles == 0U || pulse_cycles > period_cycles) {
return -EINVAL;
}

View file

@ -45,7 +45,7 @@ static int cmd_cycles(const struct shell *shell, size_t argc, char **argv)
period = strtoul(argv[args_indx.period], NULL, 0);
pulse = strtoul(argv[args_indx.pulse], NULL, 0);
err = pwm_pin_set_cycles(dev, pwm, period, pulse);
err = pwm_pin_set_cycles(dev, pwm, period, pulse, 0);
if (err) {
shell_error(shell, "failed to setup PWM (err %d)",
err);
@ -73,7 +73,7 @@ static int cmd_usec(const struct shell *shell, size_t argc, char **argv)
period = strtoul(argv[args_indx.period], NULL, 0);
pulse = strtoul(argv[args_indx.pulse], NULL, 0);
err = pwm_pin_set_usec(dev, pwm, period, pulse);
err = pwm_pin_set_usec(dev, pwm, period, pulse, 0);
if (err) {
shell_error(shell, "failed to setup PWM (err %d)", err);
return err;
@ -100,7 +100,7 @@ static int cmd_nsec(const struct shell *shell, size_t argc, char **argv)
period = strtoul(argv[args_indx.period], NULL, 0);
pulse = strtoul(argv[args_indx.pulse], NULL, 0);
err = pwm_pin_set_nsec(dev, pwm, period, pulse);
err = pwm_pin_set_nsec(dev, pwm, period, pulse, 0);
if (err) {
shell_error(shell, "failed to setup PWM (err %d)", err);
return err;

View file

@ -96,7 +96,8 @@ static int pwm_sifive_init(struct device *dev)
static int pwm_sifive_pin_set(struct device *dev,
u32_t pwm,
u32_t period_cycles,
u32_t pulse_cycles)
u32_t pulse_cycles,
pwm_flags_t flags)
{
const struct pwm_sifive_cfg *config = NULL;
u32_t count_max = 0U;
@ -112,6 +113,11 @@ static int pwm_sifive_pin_set(struct device *dev,
return -EFAULT;
}
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
config = dev->config->config_info;
if (config == NULL) {
LOG_ERR("The device configuration is NULL\n");

View file

@ -71,7 +71,8 @@ static u32_t __get_tim_clk(u32_t bus_clk,
* return 0, or negative errno code
*/
static int pwm_stm32_pin_set(struct device *dev, u32_t pwm,
u32_t period_cycles, u32_t pulse_cycles)
u32_t period_cycles, u32_t pulse_cycles,
pwm_flags_t flags)
{
struct pwm_stm32_data *data = DEV_DATA(dev);
TIM_HandleTypeDef *TimerHandle = &data->hpwm;
@ -83,6 +84,11 @@ static int pwm_stm32_pin_set(struct device *dev, u32_t pwm,
return -EINVAL;
}
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
/* configure channel */
channel = (pwm - 1)*CHANNEL_LENGTH;

View file

@ -23,18 +23,25 @@
#include <zephyr/types.h>
#include <stddef.h>
#include <device.h>
#include <dt-bindings/pwm/pwm.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Provides a type to hold PWM configuration flags.
*/
typedef u8_t pwm_flags_t;
/**
* @typedef pwm_pin_set_t
* @brief Callback API upon setting the pin
* See @a pwm_pin_set_cycles() for argument description
*/
typedef int (*pwm_pin_set_t)(struct device *dev, u32_t pwm,
u32_t period_cycles, u32_t pulse_cycles);
u32_t period_cycles, u32_t pulse_cycles,
pwm_flags_t flags);
/**
* @typedef pwm_get_cycles_per_sec_t
@ -57,20 +64,22 @@ struct pwm_driver_api {
* @param pwm PWM pin.
* @param period Period (in clock cycle) set to the PWM. HW specific.
* @param pulse Pulse width (in clock cycle) set to the PWM. HW specific.
* @param flags Flags for pin configuration (polarity).
*
* @retval 0 If successful.
* @retval Negative errno code if failure.
*/
__syscall int pwm_pin_set_cycles(struct device *dev, u32_t pwm,
u32_t period, u32_t pulse);
u32_t period, u32_t pulse, pwm_flags_t flags);
static inline int z_impl_pwm_pin_set_cycles(struct device *dev, u32_t pwm,
u32_t period, u32_t pulse)
u32_t period, u32_t pulse,
pwm_flags_t flags)
{
struct pwm_driver_api *api;
api = (struct pwm_driver_api *)dev->driver_api;
return api->pin_set(dev, pwm, period, pulse);
return api->pin_set(dev, pwm, period, pulse, flags);
}
/**
@ -104,12 +113,14 @@ static inline int z_impl_pwm_get_cycles_per_sec(struct device *dev, u32_t pwm,
* @param pwm PWM pin.
* @param period Period (in microseconds) set to the PWM.
* @param pulse Pulse width (in microseconds) set to the PWM.
* @param flags Flags for pin configuration (polarity).
*
* @retval 0 If successful.
* @retval Negative errno code if failure.
*/
static inline int pwm_pin_set_usec(struct device *dev, u32_t pwm,
u32_t period, u32_t pulse)
u32_t period, u32_t pulse,
pwm_flags_t flags)
{
u64_t period_cycles, pulse_cycles, cycles_per_sec;
@ -128,7 +139,7 @@ static inline int pwm_pin_set_usec(struct device *dev, u32_t pwm,
}
return pwm_pin_set_cycles(dev, pwm, (u32_t)period_cycles,
(u32_t)pulse_cycles);
(u32_t)pulse_cycles, flags);
}
/**
@ -138,12 +149,14 @@ static inline int pwm_pin_set_usec(struct device *dev, u32_t pwm,
* @param pwm PWM pin.
* @param period Period (in nanoseconds) set to the PWM.
* @param pulse Pulse width (in nanoseconds) set to the PWM.
* @param flags Flags for pin configuration (polarity).
*
* @retval 0 If successful.
* @retval Negative errno code if failure.
*/
static inline int pwm_pin_set_nsec(struct device *dev, u32_t pwm,
u32_t period, u32_t pulse)
u32_t period, u32_t pulse,
pwm_flags_t flags)
{
u64_t period_cycles, pulse_cycles, cycles_per_sec;
@ -162,7 +175,7 @@ static inline int pwm_pin_set_nsec(struct device *dev, u32_t pwm,
}
return pwm_pin_set_cycles(dev, pwm, (u32_t)period_cycles,
(u32_t)pulse_cycles);
(u32_t)pulse_cycles, flags);
}
#ifdef __cplusplus

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2019 Vestas Wind Systems A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_PWM_PWM_H_
#define ZEPHYR_INCLUDE_DT_BINDINGS_PWM_PWM_H_
/**
* @name PWM polarity flags
* The `PWM_POLARITY_*` flags are used with pwm_pin_set_cycles(),
* pwm_pin_set_usec(), or pwm_pin_set_nsec() to specify the polarity
* of a PWM pin.
* @{
*/
/** PWM pin normal polarity (active-high pulse). */
#define PWM_POLARITY_NORMAL (0 << 0)
/** PWM pin inverted polarity (active-low pulse). */
#define PWM_POLARITY_INVERTED (1 << 0)
/** @cond INTERNAL_HIDDEN */
#define PWM_POLARITY_MASK 0x1
/** @endcond */
/** @} */
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_PWM_PWM_H_ */

View file

@ -51,7 +51,7 @@ void main(void)
*/
max_period = MAX_PERIOD;
while (pwm_pin_set_usec(pwm_dev, PWM_CHANNEL,
max_period, max_period / 2U)) {
max_period, max_period / 2U, 0)) {
max_period /= 2U;
if (max_period < (4U * MIN_PERIOD)) {
printk("This sample needs to set a period that is "
@ -63,7 +63,7 @@ void main(void)
period = max_period;
while (1) {
if (pwm_pin_set_usec(pwm_dev, PWM_CHANNEL,
period, period / 2U)) {
period, period / 2U, 0)) {
printk("pwm pin set fails\n");
return;
}

View file

@ -48,7 +48,7 @@ void main(void)
while (1) {
if (pwm_pin_set_usec(pwm_dev, PWM_CHANNEL,
PERIOD, pulse_width)) {
PERIOD, pulse_width, 0)) {
printk("pwm pin set fails\n");
return;
}

View file

@ -46,7 +46,7 @@
static int write_pin(struct device *pwm_dev, u32_t pwm_pin,
u32_t pulse_width)
{
return pwm_pin_set_usec(pwm_dev, pwm_pin, PERIOD, pulse_width);
return pwm_pin_set_usec(pwm_dev, pwm_pin, PERIOD, pulse_width, 0);
}
void main(void)

View file

@ -45,7 +45,7 @@ void main(void)
}
while (1) {
if (pwm_pin_set_usec(pwm_dev, 0, PERIOD, pulse_width)) {
if (pwm_pin_set_usec(pwm_dev, 0, PERIOD, pulse_width, 0)) {
printk("pwm pin set fails\n");
return;
}

View file

@ -126,13 +126,14 @@ void board_play_tune(const char *str)
}
if (period) {
pwm_pin_set_usec(pwm, BUZZER_PIN, period, period / 2U);
pwm_pin_set_usec(pwm, BUZZER_PIN, period, period / 2U,
0);
}
k_sleep(duration);
/* Disable the PWM */
pwm_pin_set_usec(pwm, BUZZER_PIN, 0, 0);
pwm_pin_set_usec(pwm, BUZZER_PIN, 0, 0, 0);
}
}

View file

@ -119,7 +119,7 @@ static enum sound_state {
static inline void beep(int period)
{
pwm_pin_set_usec(pwm, SOUND_PIN, period, period / 2);
pwm_pin_set_usec(pwm, SOUND_PIN, period, period / 2, 0);
}
static void sound_set(enum sound_state state)

View file

@ -34,11 +34,11 @@ static void beep(struct k_work *work)
/* The "period / 2" pulse duration gives 50% duty cycle, which
* should result in the maximum sound volume.
*/
pwm_pin_set_usec(pwm, BUZZER_PIN, period, period / 2U);
pwm_pin_set_usec(pwm, BUZZER_PIN, period, period / 2U, 0);
k_sleep(BEEP_DURATION);
/* Disable the PWM */
pwm_pin_set_usec(pwm, BUZZER_PIN, 0, 0);
pwm_pin_set_usec(pwm, BUZZER_PIN, 0, 0, 0);
/* Ensure there's a clear silent period between two tones */
k_sleep(K_MSEC(50));

View file

@ -86,19 +86,19 @@ static int test_task(u32_t port, u32_t period, u32_t pulse, u8_t unit)
if (unit == UNIT_CYCLES) {
/* Verify pwm_pin_set_cycles() */
if (pwm_pin_set_cycles(pwm_dev, port, period, pulse)) {
if (pwm_pin_set_cycles(pwm_dev, port, period, pulse, 0)) {
TC_PRINT("Fail to set the period and pulse width\n");
return TC_FAIL;
}
} else if (unit == UNIT_USECS) {
/* Verify pwm_pin_set_usec() */
if (pwm_pin_set_usec(pwm_dev, port, period, pulse)) {
if (pwm_pin_set_usec(pwm_dev, port, period, pulse, 0)) {
TC_PRINT("Fail to set the period and pulse width\n");
return TC_FAIL;
}
} else { /* unit == UNIT_NSECS */
/* Verify pwm_pin_set_nsec() */
if (pwm_pin_set_nsec(pwm_dev, port, period, pulse)) {
if (pwm_pin_set_nsec(pwm_dev, port, period, pulse, 0)) {
TC_PRINT("Fail to set the period and pulse width\n");
return TC_FAIL;
}