cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 - GP Timer 3 OC - Generation of Square Wave with dynamically varying Pulse Width

DSchl.1
Associate III

Hi everyone

I'm faced with another task using the STM32H755.. the goal is to output a square wave with one of the output compare channels of a general purpose timer (Timer3). The difficulty is, that the period varies with each period of the square wave.

The following sketch shall give a rough idea of how I intended to solve this problem.

I'm having the basic configuration of the involved GPIOs and the Timer3 up and running, generation of a static square wave with a fixed pulse width works fine.

I have prepared an array of pulse widths, each index of the array corresponds with one half period of the square wave. The output compare is configured to toggle if the counter has reached the compare value (counting up). For this I'm setting the ARR and the CCR register to the same value, according to the value from the array.

In addition, I have enabled the update event / interrupt. The intention is to start the timer with the first value from the array for the first half period. After the trigger of the update event interrupt I'm getting the next value out of the array in the ISR and re-set the ARR and CCR registers on the fly with the timer enabled and running.

Is this procedure basically possible? What settings do I have to consider in addition?

First problem I had was, that the ARR and CCR registers were updated in the Timer3, but the pulse width did not change...

Any help would be appreciated

Daniel.

16 REPLIES 16
TDK
Guru

> Is this procedure basically possible?

Yes, there is nothing conceptually impossible with what you're suggesting. You want to update the ARR value every time the timer updates. Since you're toggling the bit on the update event, as opposed to doing OC mode, the CCR register isn't needed. You want it to trigger once per period, so just set CCR=0.

You need to set up a (probably circular) DMA transfer from your array to the ARR register in the timer, and enable all the relevant flags.

If you feel a post has answered your question, please click "Accept as Solution".
berendi
Principal

I'd too suggest leaving CCR alone, setting it to 0 (or maybe 1).

If the duty ratio is fixed (i.e. always 50% or some other fixed value), you can consider another approach, set CCR=ARR=1 and PWM mode 1, and modify the prescaler (PSC) instead, so you don't have to store each entry twice in the wavelength table.

Regardless of which method you are using, enable the preload feature on ARR (ARPE) and CCR (CCxPE when used). PSC is always preloaded, don't look for its enable bit.

When the frequency approaches the MHz range, the time required to serve interrupts starts to severely affect system performance, consider using DMA to update the timer registers.

DSchl.1
Associate III

edit: removed because duplicate answer

DSchl.1
Associate III

Thank you for the confirmation. I have removed the setting of the CCR register as I'm using the LL_TIM_OCMODE_TOGGLE :

From the Ref. manual, Register CCMR1 - OC1M = 0011: Toggle - OC1REF toggles when TIMx_CNT=TIMx_CCR1.

So I assume the update event is generated, if the CNT register reaches zero (CCR1 = 0).

The issue I'm having now is, that in my ISR, which I assume is executed on the update event, I try to update the ARR register on the fly :

void ISR(void)
{
    LL_TIM_ClearFlag_UPDATE(TIM3);
    
    // Get the new pulse duration value from the array
    // and convert it to the ARR value (0...0xFFFF max).
    uint32_t autoReload = ....
    
    LL_TIM_SetAutoReload(TIM3, autoReload);
}

... and this does not work unfortunately. It seems the auto reload value is not considered and the period of the square wave does not change at all.

edit: I have also considered the preload feature of the ARR register... It does not work with either setting (enabled or disabled).

Regarding the DMA : Is this really necessary or can it be done without? The array with the pulse widths can basically change (so my software needs to adapt to different values), but the array size and the values in the array are fixed and predefined parameters.

I'm not using the CCR any more at the moment, due to the suggestion from TDK above.

Thank you, that seems to be a nice solution. I'll consider giving this a try.

Regarding the preloading : If I understand correctly I just have to enable the ARPE and write to the same ARR register? I'm not sure about this.. the preloading has to be done before the update event, so the update can transfer the preload value to the shadow register? This would mean that in my ISR the preloaded value to be loaded needs to be ready. Are my assumptions correct?

I need to generate frequencies in the audible spectrum, so this should not pose a problem.

Ok, finally managed to get it working. Please don't ask what the problem was, it was my own fault 😉 The ISR above seems to do its job.

However, the pulse widths differ from what I've set in the ARR register. I'm calculating the ARR value from the pulse width defined in the array (us) :

maxPulseLength_us = 0xFFFF / clock_MHz * prescaler

autoReload = 0xFFFF * pulseWidth_us / maxPulseLength_us

I know there's no consideration of limits a.t.m... But the calculation should be correct isn't it?

> Regarding the preloading : If I understand correctly I just have to enable the ARPE and write to the same ARR register?

Yes.

When preloading is on, the "real", "acting" ARR (in ST lingo, "shadow") is hidden to the user.

> the preloading has to be done before the update event, so the update can transfer the preload value to the shadow register?

Yes. Generally, you want your ISR to be shorter (in execution time) than the shortest period.

JW

CNT counts from 0 to ARR, so it is doing (ARR+1) cycles, and watch out for integer truncate errors

maxPulseLength_us = 0x10000 * prescaler / clock_MHz;

autoReload = 0x10000 * pulseWidth_us / maxPulseLength_us - 1;

Thanks for the confirmation. Currently, my ISR is just resetting the update interrupt flag and setting the new ARR value (pre-calculated). This is working fine basically.

The only issue remaining is the difference between set pulse width (ARR value) and measured pulse width. The measured width seems to be around 2/3 of the expected...