cancel
Showing results for 
Search instead for 
Did you mean: 

Timers - should I "wait" before updating ARR after UEV?

SLasn.1
Associate II

Hi =)

Short version: should there be time (cycles) between updating ARR/CCR registers and generating an update event by setting the UG bit in TIMx_EGR?

Long version:

I am using one timer, which is automatically enabled (triggered) by another timer.

I made sure that:

  • The timer is not enabled and will not be started before the setup (lines below)
  • Setting the UG bit will not call the interrupt (URS set in in TIMx_CR)
  • Both ARR and CCR are pre-loaded (ARPE set in TIMx_CR and OC1PE set in TIMx_CCMR)

Then I do:

  • set ARR and CRR values into their preload register
  • generate a UEV (UG bit in TIMx_EGR) to load the values into the shadow registers
  • Then change again (right away) the values of the preload registers so that the shadows are automatically updated on the next UEV
  • Then - later - the timer is started

I get very strange result - if I add a nop() or pretty much any instruction anywhere in the whole code as long as it is "before" (i.e. lower address) the first line below, then the code works fine. My guess is that is just "lucky" and it should not work.... ?

So my guess is that I need to wait at least one cycle after the UEV before updating the preload registers again - but I could not find any info about that in the manuals.

Here is my code, uncommenting or not the nop():

TIM3->CCR1L = 2; // these values will be taken into account right away (see below)
TIM3->ARRL = 9;
 
TIM3->EGR |= TIM_EGR_UG; // generate and UEV to load the CCR and ARR values right above
//nop();
 
TIM3->CCR1L = 4; // these values will be taken into account on the next UEV
TIM3->ARRL = 11;

When the nop() is not there, then the output acts like if the first ARR was 7 and not 9, the rest all works fine. Very strange, it is like it "did not have the time" and missed one bit? ha :D

I already checked with the Debugger and it shows all the values are fine - but I can only check the preload registers and not the shadow registers.

Thank you!

Simon

11 REPLIES 11
Cristian Gyorgy
Senior III

The instruction alignment only affects the fetching when the CPU takes a jump, and the prefetch buffer is emptied - not this case (or, when after the jump only long 4/5 byte instructions are being fetched). So, what happens is that the full instruction cannot be fetched in one cycle and this way the execution is prolonged with one cycle. In the code you posted all instructions are 4 byte long, so only the first one is affected by instruction alignment.

" the hardware *does* expect one CPU cycle at least between setting the UG bit" - not true, but like I said, there is a synchronization mechanism for the UG event, and this might delay the actual updating of timer registers with 1 cycle (not sure if more than 1 is also possible). By writing the high byte you ensure that the update event does not alter the registers until the low byte is written.

In your posted code, right after setting the UG bit, you write again the CCRL register with the value of 4:

    TIM3->ARRL = 9;
     
    TIM3->EGR |= TIM_EGR_UG; // generate and UEV to load the CCR and ARR values right above
    //nop();
     
    TIM3->CCR1L = 4; // these values will be taken into account on the next UEV

, this is why I wrote that I would expect to see a 4 (for CCR value).

SLasn.1
Associate II

Ok I understand the 4 now, but that is in the CCR register...

(30min later)

YES I just made it a step further! Your comment actually made me realize I looked at the timing wrong when looking at the PWM output: I was not looking how long the ARR was, but only at "ARR minus CCR" (=time the signal is low in my case). And it went from 7 to 5, so I assumed the ARR went from 9 to 7 (with a CCR of 2).

BUT the truth is the ARR was perfectly fine , it was the CCR that was changing from 2 to 4!

So it does not explain everything but makes more sense now - basically when I do this:

TIM3->EGR |= TIM_EGR_UG;
TIM3->CCR1L = 4;

The preload CCR is set to 4 before the preload reg value (previously 2) is moved to the shadow reg.

And as said this only happens when the first instruction is aligned with a 4-byte word.

I think this actually makes sense since like you said the actual updating of the timer registers can be delayed by 1 cycle, but I guess no more since having a nop() is enough for it to work, whatever the alignment.

I did test with writing the high byte first, and it works. But impossible to tell if it is thanks to the locking of the registers or because it delays the writing of the low byte by 1 cycle just like the nop() does.

Anyways - I think we kinda have an explanation now, and the nop() solution works fine 🙂

Thank you for the discussion in any case!