2021-02-11 05:31 AM
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:
Then I do:
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
2021-02-11 06:21 AM
If the registers are preloaded, you are save to write.
2021-02-11 08:46 AM
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
2021-02-11 10:40 AM
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.
2021-02-16 06:55 AM
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'?
2021-02-16 07:30 AM
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.
2021-02-16 07:56 AM
What is the clock frequency you run the timer (prescaller)? In what mode?
2021-02-16 08:30 AM
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.
2021-02-17 10:44 PM
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.
2021-02-18 01:56 AM
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:
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:
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.