Timer Output turns back on after ARR reload in one pulse PWM mode?
I have been developing embedded software for on and off for 15 years, and STM32 for 5. I have a pretty simple goal for a timer application on the WB55 microcontroller, and I've been banging my head for the last 2 days with little success, so I'm asking the community if there is something simple I'm missing.
I'm trying to use Timer 16 and Timer 17 on the STM32WB55 to each output one pulse per software call. This is for a very typical servo PWM control application, where a single positive pulse of 1.0 msec to 2.0 msec is needed every 20 msec. I'm using my own hard real-time scheduler to drive the control loop, and when the software calculates the desired pulse duration, I want to output the pulse with minimum latency after that calculation is done. This means that I don't want to use a continuous timer PWM and adjust the pulse width on the fly, because if my software scheduling drifts out of sync with the PWM period, there could be up to 20 msec of latency before the updated pulse duration goes out to the servo. Instead, I would just like my software to command a single pulse immediately after determining the desired pulse duration, and then keep doing that every 20 msec, every time the software updates its pulse duration.
I'm using MXCube, and HAL. I'll go to the LL drivers if I need to, but would mean re-writing working code elsewhere in my application, so that's a last resort. Besides, what could be simpler than just using HAL to output one positive timer pulse?
My most straightforward attempt is to use the PWM mode 1 in one-pulse mode, setting the desired pulse duration to 1.5 msec and setting the auto-reload value to 4 msec, which is more than the max possible pulse width of 3 msec. The problem is that the timer output starts out fine with a 1.5 msec pulse, but when the ARR expires, the output goes high again.
The signal then stays high until I come in with software with a HAL_TIM_PWM_Stop command. I have been unable to find any register setting that prevents the signal from going high at the end of the ARR period.
If I try to send the HAL_TIM_PWM_Stop command during the time when the signal is low but before ARR expires, then for some reason the next several attempt to re-start the timer have no effect.
I have also tried using output compare mode with one pulse selected, and for some reason it only works for one pulse per system reset. I have dug into the timer registers to compare them between the initial successful pulse and the next failed pulse command, and at the point immediately after the successful pulse turns on, when the failed command does not, the register values for all Timer16 registers are identical. BDTR->MOE is on for both of them, etc. but in the first case the output goes high and in all subsequent cases the output stays flat. I don't know how to fix that.
The closest I have been able to come to getting what I want is to use PWM mode 2 and reverse the output polarity, so that the timer output starts out low, and then turns on for one pulse of ARR - CCR duration, with CCR of latency. To update the pulse on each software cycle, I would need to change both ARR and CCR every time, and I would have at least one timer count of unwanted delay before the pulse goes out. I will fall back to this if I need to, but it seems needlessly inefficient.
What am I missing? Thanks in advance for any help.