From 62210303abf6ea324abe1cb53048860e8ae89bed Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 10 May 2023 08:31:42 +0200 Subject: [PATCH 1/2] cpu/stm32/periph_pwm: allow to use complementary timer channels To use a complementary output of an advanced timer as PWM channel, the output is defined with an offset of 4, i.e. normal outputs are in the range of 0 to 3 (CH1...CH4) and complementary outputs are in the range of 4 to 6 (CH1N...CH3N). If the defined output is less than 4, the normal output is enabled, otherwise the complementary output is enabled. --- cpu/stm32/periph/pwm.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/cpu/stm32/periph/pwm.c b/cpu/stm32/periph/pwm.c index 72a89eef99..09df6d210c 100644 --- a/cpu/stm32/periph/pwm.c +++ b/cpu/stm32/periph/pwm.c @@ -68,9 +68,23 @@ uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) /* configure the used pins */ unsigned i = 0; + uint32_t ccer = 0; + while ((i < TIMER_CHANNEL_NUMOF) && (pwm_config[pwm].chan[i].pin != GPIO_UNDEF)) { gpio_init(pwm_config[pwm].chan[i].pin, GPIO_OUT); gpio_init_af(pwm_config[pwm].chan[i].pin, pwm_config[pwm].af); + if (pwm_config[pwm].chan[i].cc_chan < 4) { + /* OCx output channel used */ + ccer |= TIM_CCER_CC1E << ((pwm_config[pwm].chan[i].cc_chan) << 2); + } + else { +#ifdef TIM_CCER_CC1NE + /* OCxN complementary output channel used */ + ccer |= TIM_CCER_CC1NE << ((pwm_config[pwm].chan[i].cc_chan & 0x03) << 2); +#else + assert(false); +#endif + } i++; } @@ -102,8 +116,7 @@ uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) #ifdef TIM_BDTR_MOE dev(pwm)->BDTR = TIM_BDTR_MOE; #endif - dev(pwm)->CCER = (TIM_CCER_CC1E | TIM_CCER_CC2E | - TIM_CCER_CC3E | TIM_CCER_CC4E); + dev(pwm)->CCER = ccer; dev(pwm)->CR1 |= TIM_CR1_CEN; /* return the actual used PWM frequency */ @@ -138,7 +151,7 @@ void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value) } /* set new value */ - TIM_CHAN(pwm, pwm_config[pwm].chan[channel].cc_chan) = value; + TIM_CHAN(pwm, (pwm_config[pwm].chan[channel].cc_chan & 0x3)) = value; } void pwm_poweron(pwm_t pwm) From d8cf5a0c66ae17de01415da09b934cee68ef87f4 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 19 May 2023 01:07:42 +0200 Subject: [PATCH 2/2] cpu/stm32/periph_pwm: add doc for complementary timer channels --- cpu/stm32/include/periph/cpu_pwm.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cpu/stm32/include/periph/cpu_pwm.h b/cpu/stm32/include/periph/cpu_pwm.h index 9154b32543..f22c1048f0 100644 --- a/cpu/stm32/include/periph/cpu_pwm.h +++ b/cpu/stm32/include/periph/cpu_pwm.h @@ -33,10 +33,21 @@ extern "C" { /** * @brief PWM channel + * + * When using a general-purpose timer for a PWM device, the outputs OC of + * each of the four capture/compare channels can be used as PWM channel. + * The respective capture/compare channel is then specified with 0...3 in + * `cc_chan` for the outputs OC1...OC4. + * + * Advanced timers like TIM1 and TIM8 have additionally three complementary + * outputs OCN of the capture/compare channels, which can also be used + * as PWM channels. These complementary outputs are defined with an offset + * of 4, i.e. they are specified in `cc_chan` with 4...6 for OC1N...OC3N. */ typedef struct { gpio_t pin; /**< GPIO pin mapped to this channel */ - uint8_t cc_chan; /**< capture compare channel used */ + uint8_t cc_chan; /**< Capture/compare channel used: 0..3 for OC1..OC4 + or 4..6 for OC1N..OC3N for advanced timers */ } pwm_chan_t; /**