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);

	}

}

 




 

14 REPLIES 14
Vilius
Associate II

Seems like TIMPRE bit saved me this time... I still dont get the logic behind it, here is a datasheet description:

TIMPRE: Timers clocks prescalers selection
This bit is set and reset by software to control the clock frequency of all the timers connected
to APB1 and APB2 domain.
0: If the APB prescaler (PPRE1, PPRE2 in the RCC_CFGR register) is configured to a
division factor of 1, TIMxCLK = PCLKx. Otherwise, the timer clock frequencies are set to
twice to the frequency of the APB domain to which the timers are connected:
TIMxCLK = 2xPCLKx.
1:If the APB prescaler (PPRE1, PPRE2 in the RCC_CFGR register) is configured to a
division factor of 1, 2 or 4, TIMxCLK = HCLK. Otherwise, the timer clock frequencies are set
to four times to the frequency of the APB domain to which the timers are connected:
TIMxCLK = 4xPCLKx.

I only get the right time intervals when I set the TIMPRE to 1, on the other hand, I am still using this exact clock setup:

Vilius_0-1704619322285.png

But in the code (the delay function) I treat my timer clock as 100 MHz one. If TIMxCLK = 4xPCLKx like the datasheet tells, the timer clock should be 50Mhz x4 which is beyond the APB1 timer clock capabilities... Guess I am really missing out something...

Hmm, still think we're missing something here.

If you used HAL, you could use HAL_Delay to check correctness of system clock.

You could enable and test the DWT->CYCCNT counter to see if system clock frequency is as expected (200 MHz).

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

Clock is indeed 200 MHZ. Turned out to be a dodgy STM32 Macro...

Vilius_0-1705425443001.png

Good old bitwise operations like:

RCC->PLLCFGR &= ~(1 << 16);
RCC->PLLCFGR &= ~(1 << 17);

finally did it. Now I can sleep in peace

> Clock is indeed 200 MHZ. Turned out to be a dodgy STM32 Macro...

Which macro?

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

The one that I used to set PLL P divider