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

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

ARR determines period, not pulse width; that's determined by CCRx.

JW

Considering the formula, the 0xFFFF / 0x10000, it can be reduced, so the remaining formula I'm using is

autoReload = pulseWidth_us * clock_MHz / prescaler (the -1 at the end shouldn't make such a big difference...)

I've checked the actual values :

clock_Mhz = 45, prescaler = 4

The desired pulse width is e.g. 1032us

This gives an ARR register value of 11610.

However, the actual measured value of the first pulse is 650us... Can't see where the error comes from.

Unless it is working in toggle mode (OCxM = 0b0011)

The prescaler is counting from 0 to PSC as well, so it should rather be (prescaler + 1) everywhere. It does not account for all the difference though, only for 20%.

You should check the system clock frequency and other RCC settings too, especially if using CubeMX generated code to set up the clocks, because is known to be unreliable.

Some starting point if you'd like to rewrite SystemClock_Config.

If I'm setting the prescaler using LL_TIM_SetPrescaler to 4 it is actually set to 5? Well, I missed that. But it makes sense, as it is possible to have a zero value in the register.

So if I'm now re-calculating my values with the new formula I'm getting an ARR of 9288 and a measured pulse time of around 510us, which seems to be exactly half of the desired width. Seems I'm missing a factor of 2 somewhere. Not sure if my function works well, but it seems to return the correct clock value...

uint32_t get_timer_clock_freq()
{
    LL_RCC_ClocksTypeDef RCC_Clocks = { 0 };
    LL_RCC_GetSystemClocksFreq(&RCC_Clocks);
    uint32_t PCLK1_Hz = RCC_Clocks.PCLK1Frequency;
    uint32_t PCLK_Div = LL_RCC_GetAHBPrescaler();
    uint32_t PCLK_DivValue = 1;
    if (PCLK_Div == LL_RCC_AHB_DIV_1) PCLK_DivValue = 1;
    // other dividers here
    return (PCLK1_Hz / PCLK_DivValue);
}

This returns 45 with my current configuration.

I have also used Timer2 for instance, and this one seems to be correct. I assume I'm missing a divider or similar.. Is this function correct for the Timer3?

edit: Thanks for your patience 🙂 I'll check out your link too.

I don't know what those LL functions do, I don't trust any code coming from ST to do its job properly.

The PSC register works that way, just like ARR. The hidden prescaler counter runs from 0 to PSC inclusive, effectively dividing the timer clock by (PSC+1). It does not divide by 0 when PSC=0 as after reset, does it?

I have no idea what this get_timer_clock_freq() function calculates, but I'm sure it's not the timer clock. That depends on APB1 or APB2, not AHB (APB1 for TIM3), and RCC_CFGR_TIMPRE.

Thanks to your link I have found the following link: https://community.st.com/s/question/0D50X00009nL3Tb/function-to-return-value-of-apb1-apb2-timer-clocks

With the function posted in there, I have found that the clock actually was 90MHz, and not 45MHz, which answered the question, where the factor of 2 comes from.

Here's my actual function to get the Timer3 / APB1 clock (according to the ref. manual, Table 57. Ratio between clock timer and pclk)... Thanks again for the help!

uint32_t get_timer_clock_freq()
{
    LL_RCC_ClocksTypeDef RCC_Clocks = { 0 };
    LL_RCC_GetSystemClocksFreq(&RCC_Clocks);    
    uint32_t PCLK1_Hz = RCC_Clocks.PCLK1Frequency;
    if (((RCC->CFGR & RCC_CFGR_TIMPRE) == 0) && ((RCC->D2CFGR & RCC_D2CFGR_D2PPRE1) == 0))
    {
        return PCLK1_Hz;
    }
    else
        // handle other cases here...
        // Depending on the TIMPRE / D2PPRE1 the factor is 2, 4 etc.
}