cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F756VGH6 unpredictable basic timer

Vilius
Associate II

Hi,

I am at the beginning of my STM32F756VGH6 register programming journey. I have already setup everything, and now I am at the led blink (using a logic analyzer for measuring the time intervals). I decided to make a simple blocking delay function out of a basic timer. The reference manual of STM32F756VGH6 describes the basic timers as really simple and beginner friendly peripheral. I did my best to follow the datasheet and common sense. However, the timer is completely unpredictable right now... Increasing or decreasing the counter prescaler results in some 1.5x or 2.3x incremental steps (prescalers can only by round numbers.) I am not sure is my timer 6 primarily fed with 50 MHz or 100 MHz, because APB1 clock have 2 derivatives, one dedicated only to timer peripherals, so I was going out to find it out by experimenting with the delay value. However, giving the delay function variable of only "1" results in some 6.167 us delay, which is orders of magnitude more than it should be considering even a 50 Mhz clock with the prescaler of 1. Increasing the delay function argument does not make the delay longer proportionally to the increase... I can not find a relation between function variable and the real timer speed. I guess its a beginners mistake and I simply missed something, but at this point I need some help. Thank you in advance. (Code comments explain my core clock setup):

 

#include "main.h"

void Core_Clock_Setup (void){

	RCC->CR |= RCC_CR_HSEON;               //Set the clock source to external crystal/resonator (HSE)
	while (!(RCC->CR & RCC_CR_HSEON));	   //Wait until clock gets stable

	RCC->APB1ENR |= RCC_APB1ENR_PWREN;     //Enable power interface clock
	PWR->CR1  &= ~(1U << 14);
	PWR->CR1  &= ~(1U << 15);              //Set internal voltage regulator to is reset value (scale 1)

	FLASH->ACR &= ~FLASH_ACR_ARTEN;        //Disable ART accelerator
	FLASH->ACR &= ~FLASH_ACR_ARTRST;       //Reset ART accelerator
	FLASH->ACR |= FLASH_ACR_PRFTEN;        //Enable prefetch
	FLASH->ACR |= FLASH_ACR_LATENCY_6WS;   //Set 7 CPU clock cycle flash memory access time (in order to get 200 MHz core clock)

	//@ 25 MHz crystal, 200 MHz core clock configuration down below

	RCC->CFGR |= RCC_CFGR_HPRE_DIV1;       //Core clock division by 1 (core clock is not devided)
	RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;      //APB1 Low speed prescaler of 4 (50 MHz, max is 54 Mhz)
	RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;      //APB2 High speed prescaler of 2 (100 MHz, max is 108 Mhz)

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;//HSE is set to be PLL entry

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_0;    //Setting corresponding PLL prescalers (division by 2)

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLM_1;    //Setting corresponding PLL prescalers (division by 2)
	RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6);
	RCC->PLLCFGR |= (32 << 6);          	//Setting corresponding PLL prescalers ( multiplication by 32)

	RCC->CR |= RCC_CR_PLLON;               //Enable PLL
	while (!(RCC->CR & RCC_CR_PLLRDY));	   //Wait until PLL gets stable

	RCC->CFGR |= RCC_CFGR_SW_PLL;          //PLL is set to be core clock
	//RCC->CFGR |= RCC_CFGR_SW_HSE;        //HSE is set to be core clock
	while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL indeed becomes core clock source
}

void GPIO_Setup(void){
	RCC->AHB1ENR |= (1 << 4);               //Enable clock for GPIO bank E
	GPIOE->MODER |= (1 << 8);               //Bit 9 is zero (by default), bit 8 is one, resulting in 01 combination which represents the digital output pin mode
	GPIOE->OTYPER &= ~(1 << 4);             //PE4 configured as push-pull
	GPIOE->OSPEEDR |= (1 << 8);
	GPIOE->OSPEEDR |= (1 << 9);             //PE4 configuration for very high speed ( 1 1 )
}

void Timer_Setup(void){

	RCC->APB1ENR |= (1 << 4);               //Enable Timer 6 clock
	TIM6->PSC = 1;                          //Timer 6 clock is not devided and is equal to APB1 clock
	TIM6->ARR = 0xFFFF;                     //Auto reload max value of 16 bits (0xFFFF)
	TIM6->CR1 |= (1 << 0);                  //Enable Timer 6 counter
	while(!(TIM6->SR & (1<<0)));            //Wait until timer update bit is set
}

void delay_us (uint16_t us){

	TIM6->CNT = 0;
	while (TIM6->CNT < us);

}

int main (void){

	Core_Clock_Setup();
	Timer_Setup();
	GPIO_Setup();

	while(1){

		GPIOE->BSRR |= (1 << 4);
		delay_us(32000);
		GPIOE->BSRR |= ((1 << 4) << 16);
		delay_us(32000);

	}

}

 




 

1 ACCEPTED SOLUTION

Accepted Solutions

Not really. There is the TIMPRE bit, but that's unlikely to have been changed from default.

Don't look at the code--at least not for right now--look at the values within the relevant RCC registers after running your code vs after running CubeMX code. It takes maybe 5 minutes to do this and it will provide the answer, provided something is in fact different.

Consider writing PLLCFGR all at once to avoid invalid intermediate configurations.

 

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

14 REPLIES 14
TDK
Guru

Timer update frequency = (timer clock) / (PSC + 1) / (ARR + 1)

Note that PSC=1 like you have it results in a division of 2, not 1. That explains your change of 1.5x when you increase it by 1.

CubeMX can give you a good overview of the timer clocks. Generally the timer clock is twice the APB frequency, which is what it is here.

If you feel a post has answered your question, please click "Accept as Solution".
TIM6->PSC = 1;                          //Timer 6 clock is not devided and is equal to APB1 clock

TIM6->PSC = 0; // DIV1

TIM6->PSC  = (TIMCLK_MHZ - 1); // CNT at 1 micro-second

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Vilius
Associate II

Ok, this is slightly revised code:

#include "main.h"

void Core_Clock_Setup (void){

	RCC->CR |= RCC_CR_HSEON;               //Set the clock source to external crystal/resonator (HSE)
	while (!(RCC->CR & RCC_CR_HSEON));	   //Wait until clock gets stable

	RCC->APB1ENR |= RCC_APB1ENR_PWREN;     //Enable power interface clock
	PWR->CR1  &= ~(1U << 14);
	PWR->CR1  &= ~(1U << 15);              //Set internal voltage regulator to is reset value (scale 1)

	FLASH->ACR &= ~FLASH_ACR_ARTEN;        //Disable ART accelerator
	FLASH->ACR &= ~FLASH_ACR_ARTRST;       //Reset ART accelerator
	FLASH->ACR |= FLASH_ACR_PRFTEN;        //Enable prefetch
	FLASH->ACR |= FLASH_ACR_LATENCY_6WS;   //Set 7 CPU clock cycle flash memory access time (in order to get 200 MHz core clock)

	//@ 25 MHz crystal, 200 MHz core clock configuration down below

	RCC->CFGR |= RCC_CFGR_HPRE_DIV1;       //Core clock division by 1 (core clock is not devided)
	RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;      //APB1 Low speed prescaler of 4 (50 MHz, max is 54 Mhz)
	RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;      //APB2 High speed prescaler of 2 (100 MHz, max is 108 Mhz)

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;//HSE is set to be PLL entry

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_1;    //Setting corresponding PLL prescalers (division by 4)

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLM_2;    //Setting corresponding PLL prescalers (division by 4)
	RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6);
	RCC->PLLCFGR |= (128 << 6);          	//Setting corresponding PLL prescalers ( multiplication by 128)

	RCC->CR |= RCC_CR_PLLON;               //Enable PLL
	while (!(RCC->CR & RCC_CR_PLLRDY));	   //Wait until PLL gets stable

	RCC->CFGR |= RCC_CFGR_SW_PLL;          //PLL is set to be core clock
	//RCC->CFGR |= RCC_CFGR_SW_HSE;        //HSE is set to be core clock
	while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL indeed becomes core clock source
}

void GPIO_Setup(void){
	RCC->AHB1ENR |= (1 << 4);               //Enable clock for GPIO bank E
	GPIOE->MODER |= (1 << 8);               //Bit 9 is zero (by default), bit 8 is one, resulting in 01 combination which represents the digital output pin mode
	GPIOE->OTYPER &= ~(1 << 4);             //PE4 configured as push-pull
	GPIOE->OSPEEDR |= (1 << 8);
	GPIOE->OSPEEDR |= (1 << 9);             //PE4 configuration for very high speed ( 1 1 )
}

void Timer_Setup(void){

	RCC->APB1ENR |= (1 << 4);               //Enable Timer 6 clock
	TIM6->PSC = 99;                         //APB1 is 50 Mhz and 100 MHZ for timer
	TIM6->ARR = 0xFFFF;                     //Auto reload max value of 16 bits (0xFFFF)
	TIM6->CR1 |= (1 << 0);                  //Enable Timer 6 counter
	while(!(TIM6->SR & (1<<0)));            //Wait until timer update bit is set
}

void delay_us (uint16_t us){

	TIM6->CNT = 0;
	while (TIM6->CNT < us);

}

int main (void){

	Core_Clock_Setup();
	Timer_Setup();
	GPIO_Setup();

	while(1){

		GPIOE->BSRR |= (1 << 4);
		delay_us(100);
		GPIOE->BSRR |= ((1 << 4) << 16);
		delay_us(100);

	}

}

My clock configuration should be (I believe I have set it so) exactly depicted in this picture:

Vilius_0-1704383980992.png

However, I am still getting unreasonable time intervals:

Vilius_1-1704384030037.png

Any ideas?

TDK
Guru

So 750us per 100 ticks with a x100 prescaler, suggests the timer clock is ~13.3 MHz.

Given the errors in your clock setup (red boxes), I'm guessing the system clock fails to switch over to HSE-based PLL and you are still using the HSI (16 MHz) for the clock, which is roughly in-line with what you're seeing.

Double check clock settings, verify HSE=25 MHz, correct errors.

Check return values from HAL_* clock setup functions.

If you feel a post has answered your question, please click "Accept as Solution".
TDK
Guru

Hovering over errors (red boxes) in STM32CubeMX will tell you the cause of the error. In this case:

TDK_0-1704386805350.png

And:

TDK_1-1704386929029.png

Adjust PLLM/PLLN/PLLP to satisfy this requirement as well as your target clock speed.

If you feel a post has answered your question, please click "Accept as Solution".
Vilius
Associate II

Alright, latest update:

#include "main.h"

void Core_Clock_Setup (void){

	RCC->CR |= RCC_CR_HSEON;               //Set the clock source to external crystal/resonator (HSE)
	while (!(RCC->CR & RCC_CR_HSEON));	   //Wait until clock gets stable

	RCC->APB1ENR |= RCC_APB1ENR_PWREN;     //Enable power interface clock
	PWR->CR1  &= ~(1U << 14);
	PWR->CR1  &= ~(1U << 15);              //Set internal voltage regulator to is reset value (scale 1)

	FLASH->ACR &= ~FLASH_ACR_ARTEN;        //Disable ART accelerator
	FLASH->ACR &= ~FLASH_ACR_ARTRST;       //Reset ART accelerator
	FLASH->ACR |= FLASH_ACR_PRFTEN;        //Enable prefetch
	FLASH->ACR |= FLASH_ACR_LATENCY_6WS;   //Set 7 CPU clock cycle flash memory access time (in order to get 200 MHz core clock)

	//@ 25 MHz crystal, 200 MHz core clock configuration down below

	RCC->CFGR &= ~(((1 << (7 - 4 + 1)) - 1) << 4);
	RCC->CFGR |= (0 << 4);                 //Core clock division by 1 (core clock is not devided)

	RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;      //APB1 Low speed prescaler of 4 (50 MHz, max is 54 Mhz)
	RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;      //APB2 High speed prescaler of 2 (100 MHz, max is 108 Mhz)

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;//HSE is set to be PLL entry

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_0;    //PLLP Setting corresponding PLL prescalers (division by 2)

	RCC->PLLCFGR &= ~((1 << 6) - 1);
	RCC->PLLCFGR |= (16 & ((1 << 6) - 1));  //PLLM Setting corresponding PLL prescalers (division by 16)

	RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6);
	RCC->PLLCFGR |= (256 << 6);          	//PLLN Setting corresponding PLL prescalers ( multiplication by 256)

	RCC->CR |= RCC_CR_PLLON;               //Enable PLL
	while (!(RCC->CR & RCC_CR_PLLRDY));	   //Wait until PLL gets stable

	RCC->CFGR |= RCC_CFGR_SW_PLL;          //PLL is set to be core clock
	//RCC->CFGR |= RCC_CFGR_SW_HSE;        //HSE is set to be core clock
	while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL indeed becomes core clock source
}

void GPIO_Setup(void){
	RCC->AHB1ENR |= (1 << 4);               //Enable clock for GPIO bank E
	GPIOE->MODER |= (1 << 8);               //Bit 9 is zero (by default), bit 8 is one, resulting in 01 combination which represents the digital output pin mode
	GPIOE->OTYPER &= ~(1 << 4);             //PE4 configured as push-pull
	GPIOE->OSPEEDR |= (1 << 8);
	GPIOE->OSPEEDR |= (1 << 9);             //PE4 configuration for very high speed ( 1 1 )
}

void Timer_Setup(void){

	RCC->APB1ENR |= (1 << 4);               //Enable Timer 6 clock
	TIM6->PSC = 49;                         //APB1 is 50 Mhz and 100 MHZ for timer
	TIM6->ARR = 0xFFFF;                     //Auto reload max value of 16 bits (0xFFFF)
	TIM6->CR1 |= (1 << 0);                  //Enable Timer 6 counter
	while(!(TIM6->SR & (1<<0)));            //Wait until timer update bit is set
}

void delay_us (uint16_t us){

	TIM6->CNT = 0;
	while (TIM6->CNT < us);

}

int main (void){

	Core_Clock_Setup();
	Timer_Setup();
	GPIO_Setup();

	while(1){

		GPIOE->BSRR |= (1 << 4);
		delay_us(100);
		GPIOE->BSRR |= ((1 << 4) << 16);
		delay_us(100);

	}

}

 At this clock setup:

Vilius_0-1704389228737.png

However, I only get 100 us delay when using 49 timer prescaler. If I use the 99 for supposed 100 MHz timer clock, I get 200 us of delay. Should this be like that and the timer clock is derived from the basic APB1 clock (the slower peripheral version of APB1) Or I missed some clock configuration setting once again?

Vilius_1-1704389583405.png

I might be too picky on this one, but is 101.something us delay can be justified? Considering a 200 MHz core clock and absolute minimal overhead that 1.3 us of error seems like quite too much for it... Do I just take it for granted or there is something I am doing wrong yet again and I have to fix it? Thank you once again, this conversation is helping me a lot.

Probably there is an error in your clock settings. Consider setting it up in CubeMX and checking your RCC register values against there. There's probably a prescaler you're setting to 1 that actually means 2 or something. 

Your timer precision is 1 us, so +/- 1 us of jitter should be expected. (and actually this is +/- 2 us due to the error above). If you want better resolution, use a 32-bit timer with no prescaler and adjust things accordingly. For example, use PSC=0 and change your function:

void delay_us (uint16_t us){
	TIM6->CNT = 0;
	while (TIM6->CNT < 100 * us);
}

Careful of overflow here.

Better yet, use a 32-bit timer like TIM2/TIM5 and let it free-run and just use the difference. This provides max precision.

void delay_us (uint32_t us){
	uint32_t start = TIM2->CNT;
	while (TIM2->CNT - start < us * 100);
}

 

If you feel a post has answered your question, please click "Accept as Solution".
Vilius
Associate II

Vilius_0-1704396833964.png

This is what CubeMX generates based on these clock settings:

Vilius_1-1704396873242.png

After profound investigation, everything looks just the same that I write to registers (double checked all the macro prescaler values.) 

void Core_Clock_Setup (void){

	RCC->CR |= RCC_CR_HSEON;               //Set the clock source to external crystal/resonator (HSE)
	while (!(RCC->CR & RCC_CR_HSEON));	   //Wait until clock gets stable

	RCC->APB1ENR |= RCC_APB1ENR_PWREN;     //Enable power interface clock
	PWR->CR1  &= ~(1U << 14);
	PWR->CR1  &= ~(1U << 15);              //Set internal voltage regulator to is reset value (scale 1)

	FLASH->ACR &= ~FLASH_ACR_ARTEN;        //Disable ART accelerator
	FLASH->ACR &= ~FLASH_ACR_ARTRST;       //Reset ART accelerator
	FLASH->ACR |= FLASH_ACR_PRFTEN;        //Enable prefetch
	FLASH->ACR |= FLASH_ACR_LATENCY_6WS;   //Set 7 CPU clock cycle flash memory access time (in order to get 200 MHz core clock)

	//@ 25 MHz crystal, 200 MHz core clock configuration down below

	RCC->CFGR &= ~(((1 << (7 - 4 + 1)) - 1) << 4);
	RCC->CFGR |= (0 << 4);                 //Core clock division by 1 (core clock is not devided)

	RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;      //APB1 Low speed prescaler of 4 (50 MHz, max is 54 Mhz)
	RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;      //APB2 High speed prescaler of 2 (100 MHz, max is 108 Mhz)

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;//HSE is set to be PLL entry

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_0;    //PLLP Setting corresponding PLL prescalers (division by 2)

	RCC->PLLCFGR &= ~((1 << 6) - 1);
	RCC->PLLCFGR |= (16 & ((1 << 6) - 1));  //PLLM Setting corresponding PLL prescalers (division by 16)

	RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6);
	RCC->PLLCFGR |= (256 << 6);          	//PLLN Setting corresponding PLL prescalers ( multiplication by 256)

	RCC->CR |= RCC_CR_PLLON;               //Enable PLL
	while (!(RCC->CR & RCC_CR_PLLRDY));	   //Wait until PLL gets stable

	RCC->CFGR |= RCC_CFGR_SW_PLL;          //PLL is set to be core clock
	//RCC->CFGR |= RCC_CFGR_SW_HSE;        //HSE is set to be core clock
	while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL indeed becomes core clock source
}
void Timer_Setup(void){

	RCC->APB1ENR |= (1 << 4);               //Enable Timer 6 clock
	TIM6->PSC = 49;                         //APB1 is 50 Mhz and 100 MHZ for timer
	TIM6->ARR = 0xFFFF;                     //Auto reload max value of 16 bits (0xFFFF)
	TIM6->CR1 |= (1 << 0);                  //Enable Timer 6 counter
	while(!(TIM6->SR & (1<<0)));            //Wait until timer update bit is set
}

Really wonder why is my timer still at 50 Mhz clock... This is a crucial point in further code and timers development. Not asking to do the debugging for me, but are there any other potential hooks causing this problem?

Not really. There is the TIMPRE bit, but that's unlikely to have been changed from default.

Don't look at the code--at least not for right now--look at the values within the relevant RCC registers after running your code vs after running CubeMX code. It takes maybe 5 minutes to do this and it will provide the answer, provided something is in fact different.

Consider writing PLLCFGR all at once to avoid invalid intermediate configurations.

 

If you feel a post has answered your question, please click "Accept as Solution".