cancel
Showing results for 
Search instead for 
Did you mean: 

First timer delay missing -- AN4776 wrong?

I'm new here but have already gotten lots of good advice, so I'll press my luck and ask another question. Can't believe it hasn't come up before, but search doesn't find anything.

Simplest possible timer example. Start counting down from ARR value, busy loop wait for finish. No interrupts, no capture/compare, no output ... nothing. The code is right out of AN4776 "General-purpose timer cookbook.

It works, except that the first time it's called the delay is always zero. The following code is for TIM6 on an STM32F767ZI Nucleo-144 board, but I've had the same problem forever, including an STM32L476 Discovery, and bare 32F103xx and 32L0x1 chips (and with many different timers).

I really want to use one-pulse mode, but it doesn't matter -- the problem happens with or without it. Also see commented-out lines in the code. Explicitly setting CNT to zero, setting the SR register UIF flag, setting the PSC prescaler every time instead of just once at program initialization -- nothing helps.

It's not a fatal problem because I usually work around it by doing a "fake" first delay and then all the later ones work correctly. Still, I'd like to understand what's going on. The demo code below should turn the LED on for 4 seconds, off for 2, and then blink at 1 Hz indefinitely. Instead, there's a tiny, brief flash, then 2 seconds delay, and then the 1 Hz blinking.

Anyone have time to build and run this code (should be super simple to port to any system) and tell me what I'm doing wrong?

// for STM32F767 Nucleo-144 evaluation board
 
#include <stm32f767xx.h>
 
// default STM32F767xx clock is 16MHz HSI
static const uint32_t   MAIN_CLOCK_KHZ = 16000;
 
 
 
void delay(
uint32_t    milliseconds)
{
    // initialize
    TIM6->SR  = 0;
    TIM6->ARR = milliseconds;
 // TIM6->CNT = 0;
 // TIM6->SR  = TIM_SR_UIF;
 
    // enable and start one-pulse timer
    TIM6->CR1 = TIM_CR1_CEN;  // | TIM_CR1_OPM
 
    // wait for timer to finish
    while (!(TIM6->SR & TIM_SR_UIF))
        asm("nop");
}
 
 
 
int main()
{
    // enable peripherals
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
 
    // reset peripherals
    RCC->AHB1RSTR |= RCC_AHB1RSTR_GPIOBRST;
    RCC->APB1RSTR |= RCC_APB1RSTR_TIM6RST;
    RCC->AHB1RSTR  = 0;
    RCC->APB1RSTR  = 0;
 
    // set on-board green LED port to output
    GPIOB->MODER = GPIO_MODER_MODER0_0;
 
    // set prescaler for 1 count per millisecond
    TIM6->PSC = MAIN_CLOCK_KHZ - 1;
 
    // turn LED on for 4 seconds
    GPIOB->BSRR = GPIO_BSRR_BS_0;
    delay(4000);
 
    // turn LED off for 2 seconds
    GPIOB->BSRR = GPIO_BSRR_BR_0;
    delay(2000);
 
    // infinite loop, blink LED at 1 Hz, 10% duty cycle
    while (1) {
        // turn LED on for 0.1 seconds
        GPIOB->BSRR = GPIO_BSRR_BS_0;
        delay(100);
 
        // turn LED off for 0.9 seconds
        GPIOB->BSRR = GPIO_BSRR_BR_0;
        delay(900);
    }
}

1 ACCEPTED SOLUTION

Accepted Solutions

No, it's the other way round - if ARPE=0, there is no shadowing and ARR is written directly. So that's OK.

But PSC *is* shadowed unconditionally, ie. the value written into PSC gets used in the prescaler only after the first update. ("Libraries" (SPL, Cube) force this update writing 1 to TIMx_EGR.UG).

JW

View solution in original post

6 REPLIES 6

Hi

For first cycle ARR value is transferred to shadow register only after UpdateEVent.

This can be eliminated by setting in initialization code, ARPE bit of CR1 , so when write the ARR the value will transferred immediately to shadow reg.

Observed also that timer , after first enable(TIM6->CR1 = TIM_CR1_CEN) continues to run (countdown and reload again) .

No, it's the other way round - if ARPE=0, there is no shadowing and ARR is written directly. So that's OK.

But PSC *is* shadowed unconditionally, ie. the value written into PSC gets used in the prescaler only after the first update. ("Libraries" (SPL, Cube) force this update writing 1 to TIMx_EGR.UG).

JW

Wow!!! Thanks, JW (and Vangelis)!! This has been bothering me for almost two years! Setting the UG bit in the EGR register after setting the PSC works!

Yes, a very careful re-reading of RM0410 (and I'm sure the other reference manuals) shows it contains things like:

28.3.2 RM0410 Counting mode

...

When an update event occurs, all the registers are updated and the update flag (UIF bit in

the TIMx_SR register) is set (depending on the URS bit):

  • The buffer of the prescaler is reloaded with the preload value (contents of the TIMx_PSC register)

and:

28.4.5 TIM6/TIM7 event generation register (TIMx_EGR)

Bit 0 UG: Update generation

This bit can be set by software, it is automatically cleared by hardware.

0: No action.

1: Re-initializes the timer counter and generates an update of the registers. Note that the prescaler counter is cleared too (but the prescaler ratio is not affected).

Like I said in my initial post, I'd tried setting the UIF bit in the SR register but forgot that it's "rc_w0" so can only be cleared, not set, by software.

I've always joked that for every feature in these MCUs (other vendors as well as ST) there's a secret bit hidden in another register somewhere. That bit is cleared by default which means, "Make this feature not work". I can't tell you how many times I've forgotten to enable a peripheral with the correct bit in an RCC xxxENR register and spent 15 minutes trying to figure out why writing to the peripheral's registers had no effect.

I suppose this is an argument for using the HAL and LL libraries instead of doing "bare metal" programming, but I won't go that far. If the libraries didn't produce code that was so big and slow I might use them, plus I still claim that setting wrong values in the ***_InitTypeDef structs gets you right back to these same kinds of problems.

And I still think the RMs could be clearer, and that there needs to be more register-level example code as others have requested. If nothing else, the example in AN4776 should set EGR UG or at least mention it.

Thanks again. You've restored my faith in ST MCUs. 😉 Now if you could just do the same kind of magic with I2C multi-master arbitration ... 😉 😉 😉

> I suppose this is an argument for using the HAL and LL libraries

These "libraries" do indeed set EGR.UG upon initial "timebase" setup. This of course sets SR.UIF, and users of "libraries" then often complain that the first interrupt comes unexpectedly early.

JW

dbgarasiya
Senior II

If i want to configure timer of 5 sec how can i do it

what are configuration to do ?

dbgarasiya
Senior II

Thanks