Electronic – STM32 dynamically change PWM compare value

pwmstm32

I have set up stm32 timer to work as PWM to control the servo drive.
The control code looks like this:

static void setServo(uint8_t value) {
    const uint16_t timerValue = servoToTimerValue(value);
    TIM21->CCR1 = timerValue;
}

static const uint16_t s_minServoTimerValue = 258;

static uint16_t servoToTimerValue(uint8_t value) {
    return s_minServoTimerValue + value;
}

The concern I have is when I change the CCR1 value there might be a situation when the PWM won't detect the match because it is already skipped the new value but have not reached the old value.

Consider following scenario:

  • compare register CCR1 = 500
  • counter register is at CNT = 400
  • the new compare register would be CCR1 = 300

The timer won't find a match in this cycle and will result in 100% duty cycle this iteration.
How to I avoid it? Is it a legit concern at all?

I have come up with the following logic to mitigate this case:

static void setServo(uint8_t value) {
    const uint16_t timerValue = servoToTimerValue(value);
    if ((timerValue <= TIM21->CNT) && (TIM21->CNT > TIM21->CCR1)) {
        GPIOA->BRR = GPIO_PIN_2;
    }
    TIM21->CCR1 = timerValue;
}

What would be the proper way to reset PWM's channel pin?


My setup is following:

  • Fcpu = 16 MHz
  • Timer prescaler = 62 => Timer frequency 258 kHz
  • Timer period = 5160 ticks ~> 20 ms
  • Timer match is within range 258-516 ticks ~> 1-2 ms

MCU: stm32l011f4

Best Answer

The solution is to update the value synchronously instead of at random time. Perhaps in the overflow interrupt, if no other way is available. You don't say which STM32 you use, but some models have timers that have a preload feature which latch in the actual count from the register when counter starts a new cycle, you should fix your issue by enabling preload mode.