2024-01-03 11:30 AM
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);
}
}
Solved! Go to Solution.
2024-01-04 12:01 PM
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.
2024-01-03 11:44 AM
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.
2024-01-03 01:38 PM
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
2024-01-04 08:00 AM
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:
However, I am still getting unreasonable time intervals:
Any ideas?
2024-01-04 08:41 AM - edited 2024-01-04 08:42 AM
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.
2024-01-04 08:47 AM - edited 2024-01-04 08:48 AM
Hovering over errors (red boxes) in STM32CubeMX will tell you the cause of the error. In this case:
And:
Adjust PLLM/PLLN/PLLP to satisfy this requirement as well as your target clock speed.
2024-01-04 09:37 AM
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:
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?
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.
2024-01-04 10:04 AM
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);
}
2024-01-04 11:41 AM
This is what CubeMX generates based on these clock settings:
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?
2024-01-04 12:01 PM
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.