STM32: One Second Blinky no HAL - Page 1
Forum section: electronics / general

author: Dabbot
author_rank: Regular Contributor
timestamp: August 11, 2021, 01:18:21 pm
content: This is a blinky I wrote for a STM32F411RE (NUCLEO-F411RE) with STM32CubeIDE, but only using CMSIS. No HAL or code generation.My goal here is to better understand the clock tree, and also to ensure SystemCoreClockUpdate() functions correctly. All going well, SystemCoreClock will reflect the correct frequency, which is then used to configure SysTick and pulse LED every second, for any valid system clock configuration.So far it has worked as intended when using the PLL with HSI or HSE, and with different AHB prescaler values. Am I on the right track? I'm barely a week into the STM32 and ARM world.Code:/* NUCLEO-F411RE System Core Clock Configuration Test (One Second Blinky)*//* Includes */#include <stdint.h>#include <stm32f411xe.h>/* High Speed Internal Oscillator frequency in Hz*/#define HSI_FREQ 16000000/* High Speed External Oscillator frequency in Hz - Supplied by on-board STLink */#define HSE_FREQ 8000000/* SystemCoreClock - Stores calculated clock frequency in Hz */uint32_t SystemCoreClock = 0;/* Incremented by SysTick every millisecond */volatile uint32_t milliseconds = 0;/* System initialisation */void SystemInit( void ) {/***** Initialise System Clock *****//* Enable Power Interface clock */RCC->APB1ENR = RCC_APB1ENR_PWREN;/* Set regulator voltage scaling output */PWR->CR = PWR_CR_VOS_1;/* Scale 2 mode (reset value) <= 84 MHz *//* Configure Flash Access Control */FLASH->ACR =FLASH_ACR_DCEN |/* Enable data cache */FLASH_ACR_ICEN |/* Enable instruction cache */FLASH_ACR_PRFTEN |/* Enable prefetching */FLASH_ACR_LATENCY_2WS;/* Latency: 2 WS (3 CPU cycles) (64 MHz < HCLK ≤ 90 MHz @ 2.7 V - 3.6 V) *//* Configure High Speed External clock and wait for confirmation */RCC->CR |=RCC_CR_CSSON |/* Enable HSE Clock Security System (optional) */RCC_CR_HSEBYP |/* Enable High Speed External oscillator bypass */RCC_CR_HSEON;/* Enable High Speed External oscillator */while( !( RCC->CR & RCC_CR_HSERDY ) );/* Configure Main PLL */RCC->PLLCFGR =//RCC_PLLCFGR_PLLSRC_HSI |/* Clock Source: High Speed Internal */RCC_PLLCFGR_PLLSRC_HSE |/* Clock Source: High Speed External *///( 16 << RCC_PLLCFGR_PLLM_Pos ) |/* Clock Source Divider (/M): /16 (HSI) */( 8 << RCC_PLLCFGR_PLLM_Pos ) |/* Clock Source Divider (/M): /8 (HSE) */( 336 << RCC_PLLCFGR_PLLN_Pos ) |/* VCO multiplication factor (*N): *336 */( 1 << RCC_PLLCFGR_PLLP_Pos ) |/* PLL Divider for Main System Clock (/P): /4 */( 4 << RCC_PLLCFGR_PLLQ_Pos );/* PLL Divider for USB OTG FS and SDIO (/Q): /4 *//* Enable Main PLL and wait for lock */RCC->CR |= RCC_CR_PLLON;while( !( RCC->CR & RCC_CR_PLLRDY ) );/* Configure prescalers */RCC->CFGR =RCC_CFGR_HPRE_DIV1 |/* AHB prescaler: /1 */RCC_CFGR_PPRE1_DIV2 |/* APB1 low speed prescaler: /2 */RCC_CFGR_PPRE2_DIV1;/* APB2 high speed prescaler: /1 *//* Select System Clock source and wait for confirmation*/RCC->CFGR |= RCC_CFGR_SW_PLL;/* System Clock: Main PLL */while( ( RCC->CFGR & RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL );/***** TODO: Initialise LSI / LSE *****//***** TODO: Initialise FPU *****/}void SystemCoreClockUpdate( void ) {uint16_t ahb = 1;uint64_t pllout = 0;/* Determine AHB prescaler value */if ( RCC->CFGR & RCC_CFGR_HPRE_3 ) {ahb = 2 << ( ( RCC->CFGR & ( RCC_CFGR_HPRE_Msk ^ RCC_CFGR_HPRE_3 ) ) >> RCC_CFGR_HPRE_Pos );if ( RCC->CFGR & RCC_CFGR_HPRE_2 ) {ahb = ahb << 1;}}/* Determine system clock source */switch( RCC->CFGR & RCC_CFGR_SWS_Msk ){/* High Speed External */case RCC_CFGR_SWS_HSE:/* High Speed External clock frequency divided by AHB prescaler */SystemCoreClock = HSE_FREQ / ahb;break;/* Main PLL */case RCC_CFGR_SWS_PLL:/* Fetch Main PLL multiplication factor "N" */pllout = ( RCC->PLLCFGR & RCC_PLLCFGR_PLLN_Msk ) >> RCC_PLLCFGR_PLLN_Pos;/* Multiply by Main PLL clock source frequency*/if ( RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC_HSE_Msk ) {/* High Speed External */pllout *= HSE_FREQ;} else {/* High Speed Internal */pllout *= HSI_FREQ;}/* Divide by division factor "M" for the main PLL input clock */pllout /= ( RCC->PLLCFGR & RCC_PLLCFGR_PLLM_Msk ) >> RCC_PLLCFGR_PLLM_Pos;/* Divide by division factor "P" for the main system clock */pllout /= ( ( ( RCC->PLLCFGR & RCC_PLLCFGR_PLLP_Msk ) >> RCC_PLLCFGR_PLLP_Pos ) + 1 ) * 2;/* Divide by AHB prescaler */SystemCoreClock = pllout / ahb;break;/* High Speed Internal */default:/* High Speed Internal clock frequency divided by AHB prescaler */SystemCoreClock = HSI_FREQ / ahb;}}/* Interrupt handler for SysTick */void SysTick_Handler( void ) {milliseconds++;}int main( void ){/* Update SystemCoreClock variable */SystemCoreClockUpdate();/* Enable GPIOA clock */RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;/* Configure GPIOA Pin 5 as push-pull output */GPIOA->MODER |= GPIO_MODER_MODER5_0;/* Configure SysTick for 1KHz interrupt */if ( SysTick_Config( SystemCoreClock / 1000 ) != 0 ) {/* Turn LED on and loop forever to indicate error */GPIOA->BSRR = GPIO_BSRR_BS5;while( 1 );}/* Main loop */while( 1 ) {/* Pulse LED every second */GPIOA->BSRR = GPIO_BSRR_BS5;/* Set GPIOA Pin 5 high (LED) */while( milliseconds < 20 );/* Wait at least 20 milliseconds */GPIOA->BSRR = GPIO_BSRR_BR5;/* Set GPIOA Pin 5 low (LED) */while( milliseconds < 1000 );/* Wait until 1 second has passed */milliseconds = 0;}}