0
votes

I built a small board with a STM32L011D3 and an LED. I want to use the LED at 38kHz with a duty cycle of 50%. Now I connected the LED to the wrong pin on the mcu so I can not use the timer as a PWM generator directly. So I was trying to generate the PWM signal manually in the main loop. I thought this would work since I am actually not doing anything else on the mcu.

I configured my clock like the image shows below: clock configuration

The timer TIM2 I configured like the image shows below: timer configuration

So this means I should have 4MHz / 4 / 26 = 38.461kHz

The main loop currently looks like this: main loop

With this setup I get something around 37us on / 37us off. The timings are not very stable.

Could it be, that my instructions to enable/disable the led are too long to be executed all 25us, to get a stable pwm output?

1
Just patch the hardware or order a new PCB? Everything else is a bad solution.Lundin
By posting a picture of text, you have made it impossible for anyone to copy & paste your code into an answer. Don't do that; it discourages answers, which is of no help to you.Clifford
You can simply measure the duration of the calls to HAL_GPIO_WritePinfunctions, for example through register DWT_CYCCNT. I expect that it takes several hundreds of CPU cycles so several us. It might mess your algorithmGuillaume Petitjean
Anyway I don't think you chose the right way to implement. Manage the timer in interrupt mode, every time you receive an interrupt, you toggle the LED. Also, if you have timing issue, don't use the HAL which is very slow because it manages all possible use casesGuillaume Petitjean
At 4MHz and 1.25MIPS/MHz you have time for 125 instructions - that would seem to be plenty if a little tight to get accurate timing, although who knows what kind of overhead is going on within the HAL, or any interrupts that may be running? If you want accurate deterministic timing, you might be better off taking control of all the code rather then using all the CubeMX bloatware.Clifford

1 Answers

2
votes

You are wasting time reading the output - you set it, so you already know what it is:

for(;;)
{
    int out = (timer2Value >= 13) ? GPIO_PIN_SET : GPIO_PIN_RESET ;
    HAL_GPIO_WritePin( GPIOA, GPIO_PIN_7, out ) ;
}

If the timing does not then change, then the problem is more likely that your clock is not running at the frequency you think it is.

A more deterministic solution (short of correcting the hardware design), is to set the timer to generate an interrupt at 38000-1 intervals, and toggle the output in the interrupt handler:

void TIM2_IRQHandler()
{
    static int out = 0 ;
    out = out ? 0 : 1 ;
    HAL_GPIO_WritePin( GPIOA, GPIO_PIN_7, out ) ;
}

Or even just:

void TIM2_IRQHandler()
{
    HAL_GPIO_TogglePin( GPIOA, GPIO_PIN_7 ) ;
}

If you really want the fastest possible output switching, then you might determine the bitband address of the pin and and toggle that directly:

Given:

__IO uint32_t* getBitBandAddress( volatile const void* address, int bit )
{
    __IO uint32_t* bit_address = 0;
    uint32_t addr = reinterpret_cast<uint32_t>(address);

    // This bit maniplation makes the function valid for RAM
    // and Peripheral bitband regions
    uint32_t word_band_base = addr & 0xf0000000;
    uint32_t bit_band_base = word_band_base | 0x02000000;
    uint32_t offset = addr - word_band_base;

    // Calculate bit band address
    bit_address = reinterpret_cast<__IO uint32_t*>(bit_band_base + (offset * 32u) + (static_cast<uint32_t>(bit) * 4u));

    return bit_address ;
}

Then:

__IO uint32_t* gpioA7 = getBitBandAddress( &GPIOA->ODR, 7 ) ;
for(;;)
{
    *gpioA7 = (timer2Value >= 13) ;
}

Similarly in an interrupt:

void TIM2_IRQHandler()
{
    static __IO uint32_t* gpioA7 = getBitBandAddress( &GPIOA->ODR, 7 ) ;

    *gpioA7 = !*gpioA7 ;
}

Though you might choose to initialise gpioA7 externally in the interrupt case.