drivers: pwm_nrfx: Add support for PWM_POLARITY_INVERTED flag

Add support for inverting of PWM channel outputs in the pwm_nrfx driver
by properly handling the `PWM_POLARITY_INVERTED` flag.
The dts properties that were used so far for inverting of the outputs
("nordic,invert" and "chX-inverted") are kept as they are needed for
setting of the initial polarity, i.e. for setting the inactive state
of the outputs before any PWM signal generation is requested for them.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2022-04-20 09:00:00 +02:00 committed by Carles Cufí
commit e7a075f460

View file

@ -34,7 +34,7 @@ struct pwm_nrfx_data {
uint16_t current[NRF_PWM_CHANNEL_COUNT]; uint16_t current[NRF_PWM_CHANNEL_COUNT];
uint16_t countertop; uint16_t countertop;
uint8_t prescaler; uint8_t prescaler;
uint8_t inverted_channels; uint8_t initially_inverted;
}; };
@ -112,6 +112,15 @@ static bool any_other_channel_is_active(uint8_t channel,
return false; return false;
} }
static bool channel_psel_get(uint32_t channel, uint32_t *psel,
const struct pwm_nrfx_config *config)
{
*psel = nrf_pwm_pin_get(config->pwm.p_registers, channel);
return (((*psel & PWM_PSEL_OUT_CONNECT_Msk) >> PWM_PSEL_OUT_CONNECT_Pos)
== PWM_PSEL_OUT_CONNECT_Connected);
}
static int pwm_nrfx_pin_set(const struct device *dev, uint32_t pwm, static int pwm_nrfx_pin_set(const struct device *dev, uint32_t pwm,
uint32_t period_cycles, uint32_t pulse_cycles, uint32_t period_cycles, uint32_t pulse_cycles,
pwm_flags_t flags) pwm_flags_t flags)
@ -124,13 +133,9 @@ static int pwm_nrfx_pin_set(const struct device *dev, uint32_t pwm,
const struct pwm_nrfx_config *config = dev->config; const struct pwm_nrfx_config *config = dev->config;
struct pwm_nrfx_data *data = dev->data; struct pwm_nrfx_data *data = dev->data;
uint8_t channel = pwm; uint8_t channel = pwm;
bool inverted = (flags & PWM_POLARITY_INVERTED);
bool was_stopped; bool was_stopped;
if (flags) {
/* PWM polarity not supported (yet?) */
return -ENOTSUP;
}
if (channel >= NRF_PWM_CHANNEL_COUNT) { if (channel >= NRF_PWM_CHANNEL_COUNT) {
LOG_ERR("Invalid channel: %u.", channel); LOG_ERR("Invalid channel: %u.", channel);
return -EINVAL; return -EINVAL;
@ -156,8 +161,7 @@ static int pwm_nrfx_pin_set(const struct device *dev, uint32_t pwm,
/* Check if period_cycles is either matching currently used, or /* Check if period_cycles is either matching currently used, or
* possible to use with our prescaler options. * possible to use with our prescaler options.
* Don't do anything if the period length happens to be zero. * Don't do anything if the period length happens to be zero.
* In such case, pulse cycles will be right below limited to 0 * In such case, the channel is treated as inactive.
* and this will result in making the channel inactive.
*/ */
if (period_cycles != 0 && period_cycles != data->period_cycles) { if (period_cycles != 0 && period_cycles != data->period_cycles) {
int ret = pwm_period_check_and_set(config, data, channel, int ret = pwm_period_check_and_set(config, data, channel,
@ -167,10 +171,8 @@ static int pwm_nrfx_pin_set(const struct device *dev, uint32_t pwm,
} }
} }
/* Store new pulse value bit[14:0], and polarity bit[15] for channel. */ data->current[channel] =
data->current[channel] = ( PWM_NRFX_CH_VALUE(pulse_cycles >> data->prescaler, inverted);
(data->current[channel] & PWM_NRFX_CH_POLARITY_MASK)
| (pulse_cycles >> data->prescaler));
LOG_DBG("channel %u, pulse %u, period %u, prescaler: %u.", LOG_DBG("channel %u, pulse %u, period %u, prescaler: %u.",
channel, pulse_cycles, period_cycles, data->prescaler); channel, pulse_cycles, period_cycles, data->prescaler);
@ -182,27 +184,22 @@ static int pwm_nrfx_pin_set(const struct device *dev, uint32_t pwm,
* is disabled after all channels appear to be inactive. * is disabled after all channels appear to be inactive.
*/ */
if (!pwm_channel_is_active(channel, data)) { if (!pwm_channel_is_active(channel, data)) {
/* If pulse 0% and pin not inverted, set LOW. uint32_t psel;
* If pulse 100% and pin inverted, set LOW.
* If pulse 0% and pin inverted, set HIGH.
* If pulse 100% and pin not inverted, set HIGH.
*/
bool channel_inverted_state =
data->inverted_channels & BIT(channel);
bool pulse_0_and_not_inverted = if (channel_psel_get(channel, &psel, config)) {
(pulse_cycles == 0U) /* If pulse 0% and pin not inverted, set LOW.
&& !channel_inverted_state; * If pulse 100% and pin inverted, set LOW.
bool pulse_100_and_inverted = * If pulse 0% and pin inverted, set HIGH.
(pulse_cycles == period_cycles) * If pulse 100% and pin not inverted, set HIGH.
&& channel_inverted_state; */
uint32_t psel = bool pulse_0_and_not_inverted =
nrf_pwm_pin_get(config->pwm.p_registers, channel); (pulse_cycles == 0U) && !inverted;
bool pulse_100_and_inverted =
(pulse_cycles == period_cycles) && inverted;
uint32_t value = (pulse_0_and_not_inverted ||
pulse_100_and_inverted) ? 0 : 1;
if (pulse_0_and_not_inverted || pulse_100_and_inverted) { nrf_gpio_pin_write(psel, value);
nrf_gpio_pin_clear(psel);
} else {
nrf_gpio_pin_set(psel);
} }
if (!any_other_channel_is_active(channel, data)) { if (!any_other_channel_is_active(channel, data)) {
@ -259,23 +256,23 @@ static int pwm_nrfx_init(const struct device *dev)
return ret; return ret;
} }
data->inverted_channels = 0; data->initially_inverted = 0;
for (size_t i = 0; i < ARRAY_SIZE(data->current); i++) { for (size_t i = 0; i < ARRAY_SIZE(data->current); i++) {
uint32_t psel = nrf_pwm_pin_get(config->pwm.p_registers, i); uint32_t psel;
/* Mark channels as inverted according to what initial state
* of their outputs has been set by pinctrl (high idle state if (channel_psel_get(i, &psel, config)) {
* means that the channel is inverted). /* Mark channels as inverted according to what initial
*/ * state of their outputs has been set by pinctrl (high
if (((psel & PWM_PSEL_OUT_CONNECT_Msk) >> PWM_PSEL_OUT_CONNECT_Pos) * idle state means that the channel is inverted).
== PWM_PSEL_OUT_CONNECT_Connected) { */
data->inverted_channels |= data->initially_inverted |=
nrf_gpio_pin_out_read(psel) ? BIT(i) : 0; nrf_gpio_pin_out_read(psel) ? BIT(i) : 0;
} }
} }
#endif #endif
for (size_t i = 0; i < ARRAY_SIZE(data->current); i++) { for (size_t i = 0; i < ARRAY_SIZE(data->current); i++) {
bool inverted = data->inverted_channels & BIT(i); bool inverted = data->initially_inverted & BIT(i);
data->current[i] = PWM_NRFX_CH_VALUE(0, inverted); data->current[i] = PWM_NRFX_CH_VALUE(0, inverted);
} }
@ -362,7 +359,7 @@ static int pwm_nrfx_pm_action(const struct device *dev,
ch0_pin, ch1_pin, ch2_pin, ch3_pin); \ ch0_pin, ch1_pin, ch2_pin, ch3_pin); \
static struct pwm_nrfx_data pwm_nrfx_##idx##_data = { \ static struct pwm_nrfx_data pwm_nrfx_##idx##_data = { \
COND_CODE_1(CONFIG_PINCTRL, (), \ COND_CODE_1(CONFIG_PINCTRL, (), \
(.inverted_channels = \ (.initially_inverted = \
(PWM_CH_INVERTED(idx, 0) ? BIT(0) : 0) | \ (PWM_CH_INVERTED(idx, 0) ? BIT(0) : 0) | \
(PWM_CH_INVERTED(idx, 1) ? BIT(1) : 0) | \ (PWM_CH_INVERTED(idx, 1) ? BIT(1) : 0) | \
(PWM_CH_INVERTED(idx, 2) ? BIT(2) : 0) | \ (PWM_CH_INVERTED(idx, 2) ? BIT(2) : 0) | \