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
Uwe Bonnes
Principal III

If the registers are preloaded, you are save to write.

SLasn.1
Associate II

Thank you Uwe, but then how do you explain the issue without the nop()?

I did a bit more investigation about why it fails when the nop() is not there .

It seems it only has an issue when the bit setting (TIM3->EGR |= TIM_EGR_UG) is exactly at the beginning of a 32 bit word, i.e. an adress divisible by 4.

For example if that instruction is at 0xfb64 in the memory space, then I can add or remove any multiple of 4 bytes before and the problem persists. If I add or remove 1, 2 or 3 bytes, then the problem is gone.

Kinda very nerdy at this point, but I guess it is something to do with the fact that the STM8 has a 32 bit wide pre-fetching buffer... If anybody has the courage to help me understand, I think the answer is in chapter 5 (Pipeline execution) of this manual: https://www.st.com/resource/en/programming_manual/cd00161709-stm8-cpu-programming-manual-stmicroelectronics.pdf

SLasn.1
Associate II

Just FYI I also tried the same test (adding 1, 2, 3 or 4 bytes before the bit of code shown) but this time with the nop() after the UG bit setting.

With the nop() it works all the time, regardless of its position in the memory space.

Cristian Gyorgy
Senior III

Hi Simon!

You say: "When the nop() is not there, then the output acts like if the first ARR was 7 and not 9". May I ask how you "saw" this?

So, the problem is that with the nop instruction you see a '9' on the timer output and without the nop you see a '7'?

SLasn.1
Associate II

Hi Cristian,

Good question! Like I said on the debugger I can only see the preload registers so I do not technically know what the shadow registers are, but I am using this timer to output a PWM signal so the way I "see" the value is by looking at the output signal.

And yes that is the problem :)

I actually tried putting an "11", then I get 9, ie what I want.

But then if the alignment of the code moves (as I explained above), it goes to 11.

I have not tried other values.

Cristian Gyorgy
Senior III

What is the clock frequency you run the timer (prescaller)? In what mode?

SLasn.1
Associate II

Here is my full timer setup:

/** ######## TIMER CONFIGURATION ########
Timer		PSC  =>  freq
TIM3		128 => 125kHz
*/
 
 
#define		TIM_CCMR_OCM_PWM_MODE1		((uint8_t)(0x60 & TIM_CCMR_OCM))	// OCxM=110 => PWM Mode 1
#define		TIM_CCMR_OCM_PWM_MODE2		((uint8_t)(0x70 & TIM_CCMR_OCM))	// OCxM=111 => PWM Mode 2
 
#define		TIM_SMCR_TS_ITR1			((uint8_t)(0x10 & TIM_SMCR_TS))		//   TS=001 => internal trigger ITR1 (=TIM1 TRGO)
#define		TIM_SMCR_SMS_TRIGGER		((uint8_t)(0x06 & TIM_SMCR_SMS))	//  SMS=110 => trigger standard mode (starts timer on trigger)
 
 
 
#define 	SETUP_TIM3_PSCR_VALUE		((uint8_t)7) // prescaler = 2^7=128 => 125kHz / every 8us
 
 
#define 	SETUP_TIM3_CCR_VALUE	((uint8_t)(2+160)) // delay after TIM1 before starting the "UART"	
#define 	SETUP_TIM3_ARR_VALUE	((uint8_t)(SETUP_TIM3_CCR_VALUE+16-1)) // 16 * 8us = 128us
 
 
static inline void setup_timers(void) {
 
	/*** TIM3 setup ***/
	// set ARR preload, one-pulse-mode (timer stops after update event) and URS="Update Request Source" to prevent EGR_UG to set UIF flag 
	TIM3->CR1 = TIM_CR1_RESET_VALUE | TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_URS; 
	//TIM3->CR2 = TIM_CR2_RESET_VALUE; // nothing to change here
	
	TIM3->SMCR = TIM_SMCR_RESET_VALUE | TIM_SMCR_TS_ITR1 | TIM_SMCR_SMS_TRIGGER; // sets timer to start on TIM1 TRGO
	TIM3->CCMR1 = TIM_CCMR1_RESET_VALUE | TIM_CCMR_OCM_PWM_MODE1 | TIM_CCMR_OCxPE; // setup PWM on channel 1 (OCxPE required by PWM)
	TIM3->CCMR2 = TIM_CCMR2_RESET_VALUE | TIM_CCMR_OCM_PWM_MODE1 | TIM_CCMR_OCxPE; // setup PWM on channel 2 (OCxPE required by PWM)
	TIM3->CCER1 = TIM_CCER1_RESET_VALUE | TIM_CCER1_CC1E | TIM_CCER1_CC2E; // enables channel 1 output on PD0 and channel 2 output on PB1
	
	TIM3->PSCR = (uint8_t)(SETUP_TIM3_PSCR_VALUE & TIM_PSCR_PSC);
	TIM3->ARRH = (uint8_t)(SETUP_TIM3_ARR_VALUE << 8); // will actually be zero (default value is 0xFF)
	TIM3->ARRL = SETUP_TIM3_ARR_VALUE;
	//TIM3->CCR1H = (uint8_t)(SETUP_TIM3_CCR12_VALUE << 8); // will be zero - and default value is zero
	TIM3->CCR1L = SETUP_TIM3_CCR_VALUE;
	//TIM3->CCR2H = (uint8_t)(SETUP_TIM3_CCR12_VALUE << 8); // will be zero - and default value is zero
	TIM3->CCR2L = SETUP_TIM3_CCR_VALUE;
	TIM3->BKR = TIM_BKR_RESET_VALUE | TIM_BKR_MOE; // needed to enable all outputs enabled with CCER1_CCxE
	TIM3->EGR |= TIM_EGR_UG; // generates an UEV to initialize the values of PSCR, ARR, and CRR1&2
	// we do not enable the timer as it will be done automatically on each TIM1 TRGO
}

Then after a user action I change the CCR and ARR with the code I put in my first post.

Cristian Gyorgy
Senior III

Well, I was thinking it is a matter of UG event synchronisation, and this could insert a dead cycle, but you don't use the timer at full speed, SYSCLK/1. Even so, I would expect to see the last written value of 4, right after the software UG event, but you say it is 9.

Anyway, try to safely write the registers, by writing first the high byte, even if this stays 0x00. This way, the UG event will not update the registers until the low byte is written. There is a synchronization mechanism.

SLasn.1
Associate II

Thank you Cristian, actually happy you bring it up, I was also wondering if the system did not like the fact I only write the low byte. But according to the datasheet the register is updated when you write the low byte, also that works perfectly fine apart from that very particular case. There's no point writing the high byte because it will give an extra cycle after I set the UG bit, just like the nop() does, so for sure it will work.

But actually I think this is it: the hardware *does* expect one CPU cycle at least between setting the UG bit and writing the preload register, which will always happen if I was writing the high byte, and that is why it is not mentioned in the datasheet. But since I am not using the high byte, and I run the CPU at full speed, then, on the cycle right after I set the UG bit:

  • the preload ARR value (9) is moved to the shadow ARR (because I sat the UG bit on the previous cycle)
  • at the same time I am writing the preload ARR with the new value (11)
  • The MCU gets confused and the value written in the shadow ARR is incorrect (7 instead of 9)

And this can only happen if the instruction is aligned correctly (I guess - this implicates deep understanding of the core and instruction fetching which I have not).

That's my guess anyways.

----

And just FYI, because I am a bit confused about where you see the value of 4:

If you look at the first code I sent:

  • At this point the timer is stopped (and will not be started within the next lines - I made sure of that)
  • ARR = 9 -> this sets the preload ARR to 9
  • EGR |= UG -> moves the preload value (9) into the shadow ARR (which should be used next time the timer is started)
  • ARR = 11 -> this sets the preload ARR to 11

So basically the next time the timer is started, it will work with an ARR of 9, and at the end, on the UEV, the value of 11 will be loaded, so that when the timer restarts it will work with an ARR of 11.

Looking at the PWM output, I can see that on the first "round" of the timer, the ARR is 7 (instead of 9), then on the second "round" the ARR is indeed 11. So as I said above something goes wrong when the preload value is moved to the shadow value.