cancel
Showing results for 
Search instead for 
Did you mean: 

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.

0693W00000UnkpmQAB.pngThe 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.

1 ACCEPTED SOLUTION

Accepted Solutions

> What am I missing?

Reading of TIM chapter in RM.

PWM mode means two transitions per period, both ahead in time: one at CNT matching CCR, the other at CNT matching ARR.

PWM mode 1 and PWM mode 2 with inverted outputs are in fact identical, as long as a it's single-output timer channel,so you get no change with that.

What you thought when you read "one pulse mode", without actually trying to read the RM and understand how the timer works, is, that when you "start" the timer its output will automagically go to one and upon CNT matching CCR it will go to zero. This is a common misconception among those who use CubeMX without actually reading the manual. One pulse mode (i.e. setting TIMx_CR1.OPM) does not change how the output behaves - and that's what I wrote above and what's quite clearly described in the RM - just that CNT automatically stops when reaches ARR and zeroes.

So, if you *want* to have a transition at counter start, you have to make that transition somehow. So, ask yourself, what's "start", actually? It's probably setting TIMx_CR1.CEN, isn't it. That's software, anyway. So you can do extra steps, too. One way to set output manually to a given level is to set the given channel to Force high and then to PWM back again. Yes, that may introduce loss of precision, if this process can be interrupted.

Or just simply accept the one cycle latency from "start". I don't see why would you need to change two values to change the pulse length: you need to set ARR only, leave CCR at 1. You want to have ARR preload off (TIMx_CR1.ARPE=0) for that.

Or leave ARR unchaned, change only CCR and accept the fact that the trailing-edges of pulses are spaced regularly apart, not the leading edges. Your servo most probably doesn't care.

Or don't force your idea of scheduling, run the normal continuous PWM and perform duty (i.e. next CCR value) calculation in the Update interrupt of that timer. Be sure to set CCR preloading, in that way you have a whole timer cycle for the calculation for next period to be performed.

> 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?

Using registers directly. Simple enough and maps straightforwardly to the manual. What could be simpler than a program which behaves exactly as the manual describes?

JW

View solution in original post

2 REPLIES 2

> What am I missing?

Reading of TIM chapter in RM.

PWM mode means two transitions per period, both ahead in time: one at CNT matching CCR, the other at CNT matching ARR.

PWM mode 1 and PWM mode 2 with inverted outputs are in fact identical, as long as a it's single-output timer channel,so you get no change with that.

What you thought when you read "one pulse mode", without actually trying to read the RM and understand how the timer works, is, that when you "start" the timer its output will automagically go to one and upon CNT matching CCR it will go to zero. This is a common misconception among those who use CubeMX without actually reading the manual. One pulse mode (i.e. setting TIMx_CR1.OPM) does not change how the output behaves - and that's what I wrote above and what's quite clearly described in the RM - just that CNT automatically stops when reaches ARR and zeroes.

So, if you *want* to have a transition at counter start, you have to make that transition somehow. So, ask yourself, what's "start", actually? It's probably setting TIMx_CR1.CEN, isn't it. That's software, anyway. So you can do extra steps, too. One way to set output manually to a given level is to set the given channel to Force high and then to PWM back again. Yes, that may introduce loss of precision, if this process can be interrupted.

Or just simply accept the one cycle latency from "start". I don't see why would you need to change two values to change the pulse length: you need to set ARR only, leave CCR at 1. You want to have ARR preload off (TIMx_CR1.ARPE=0) for that.

Or leave ARR unchaned, change only CCR and accept the fact that the trailing-edges of pulses are spaced regularly apart, not the leading edges. Your servo most probably doesn't care.

Or don't force your idea of scheduling, run the normal continuous PWM and perform duty (i.e. next CCR value) calculation in the Update interrupt of that timer. Be sure to set CCR preloading, in that way you have a whole timer cycle for the calculation for next period to be performed.

> 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?

Using registers directly. Simple enough and maps straightforwardly to the manual. What could be simpler than a program which behaves exactly as the manual describes?

JW

Good point about just leaving CCR at 1. That extra latency is small enough to be a non-issue.