cancel
Showing results for 
Search instead for 
Did you mean: 

Setting clock to 168MHz on the STM32F407 and enabling MCO1 [bare metal]

Hrishikesh
Senior

Hi,

I'm trying my hand at bare metal programming on the F4 Discovery board. I've been loosely following this code to get the RCC configuration sequence right to set the main sys clock to 168MHz but with my adaptations. I'm able to see 16MHz on MCO1 (PA8). However, I wanted the PLL clock (168MHz) divided by 4 (42MHz) on MCO1. Also, how do I verify that the system clock is indeed 168MHz?

I'm guessing the MCO1 source is defaulting to HSI and not PLLCLK as intended. Or the sys clock in the code below is not getting configured to 168MHz. Any help in debugging this is appreciated.

#include <stdint.h>
#include "stm32f407xx.h"
 
#if !defined(__SOFT_FP__) && defined(__ARM_FP)
  #warning "FPU is not initialized, but the project is compiling for an FPU. Please initialize the FPU before use."
#endif
 
#define PLL_M			4U
#define PLL_N			168U
#define PLL_P			2U
//#define PLL_Q			4U
#define LEDDELAY		1000000
 
void SysClockConfig(void);
int main(void);
 
uint32_t SystemCoreClock;
 
int main(void)
{
	SysClockConfig();
 
	// Enable clock for AHB1 bus
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOA_CLK_ENABLE;
 
	// Configure PA8 for MCO1
	GPIOA->MODER &= ~GPIO_MODER_PIN_8_ALT_FN;
	GPIOA->MODER |= GPIO_MODER_PIN_8_ALT_FN;
 
	GPIOA->AFRH |= GPIO_PIN_8_AFN(0);
 
	GPIOA->OTYPER &= ~GPIO_OTYPER_PIN_8_PUSH_PULL;
	GPIOA->OTYPER |= GPIO_OTYPER_PIN_8_PUSH_PULL;
 
	GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_PIN_8_VERY_HIGH_SPD;
	GPIOA->OSPEEDR |= GPIO_OSPEEDR_PIN_8_VERY_HIGH_SPD;
 
	while (1)
	{
		//
	}
	return 0;
}
 
void SysClockConfig(void)
{
	// Use HSE as the Master clock output 1
	RCC->CFGR |= RCC_CFGR_MCO1(3U) | RCC_CFGR_MCO1PRE(6U);
 
	/* Enable HSE (CR: bit 16) */
	RCC->CR |= RCC_CR_HSE_ON;
	/* Wait till HSE is ready (CR: bit 17) */
	while (!RCC_CR_HSERDY);
 
	/* Enable power interface clock (APB1ENR:bit 28) */
	RCC->APB1ENR |= RCC_APB1ENR_PWR_CLK_ENABLE;
 
	/* set voltage scale to 1 for max frequency (PWR_CR:bit 14)
	 * (0b0) scale 2 for fCLK <= 144 MHz
	 * (0b1) scale 1 for 144 MHz < fCLK <= 168 MHz
	 */
	PWR->CR |= PWR_CR_VOS_SCALE1;
 
	/* set AHB prescaler to /1 (CFGR:bits 7:4) */
	RCC->CFGR |= RCC_CFGR_HPRE(1U);
	/* set APB1 prescaler to /4 (CFGR:bits 12:10) */
	RCC->CFGR |= RCC_CFGR_PPRE1(4U);
	/* set APB2 prescaler to /2 (CFGR:bits 15:13) */
	RCC->CFGR |= RCC_CFGR_PPRE2(2U);
 
	/* Set PLL dividers */
	RCC->PLLCFGR = RCC_PLLCFGR_PLLM(PLL_M) | RCC_PLLCFGR_PLLN(PLL_N) | RCC_PLLCFGR_PLLP(PLL_P) | RCC_PLLCFGR_PLLSRC_HSE;
 
	/* Enable the main PLL (CR: bit 24) */
	RCC->CR |= RCC_CR_PLL_ON;
	/* Wait till the main PLL is ready (CR: bit 25) */
	while (!RCC_CR_PLLRDY);
 
	/* Configure Flash
	 * prefetch enable (ACR:bit 8)
	 * instruction cache enable (ACR:bit 9)
	 * data cache enable (ACR:bit 10)
	 * set latency to 5 wait states (ARC:bits 2:0)
	 * see Table 10 on page 80 in RM0090
	 */
	FLASH->ACR = FLASH_ACR_PREFECTCH_ENABLE | FLASH_ACR_ICEN_ENABLE | FLASH_ACR_DCEN_ENABLE | FLASH_ACR_LATENCY_5_WAIT_STATE;
 
	RCC->CFGR = RCC_CFGR_SW_PLL_AS_SYSCLK;
	while (!RCC_CFGR_SWS_STATUS);
 
	// Update SystemCoreClock variable
	SystemCoreClock = 168000000;
}

4 REPLIES 4
Uwe Bonnes
Principal III

Attach with a debugger to the running system , read the RCC registers and check the RCC setting. Or use a good frequency counter and measure the frequency. Heat up or cool down the CPU and if major deviation appears, you still use HSI.

The MCO configuration will be whatever you've set it too. The register settings, including default values, should be in the Reference Manual (RM).

You understand doing multiple load-store against the same register is inefficient, right? And that OR'ing bits is cumulative.

Use of odd library and defines, not likely to be compilable for others.

You should be able to plumb clocks out the MCO/PA8 pin, program and read-back registers, unpack content per RM.

Clocks can also be inferred via TIM outputs, and UART baud rates/dividers.

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

Thanks. I was checking on an oscilloscope. I had misconfigured the prescalers for the AHB and APB buses. It is working now.

> You understand doing multiple load-store against the same register is inefficient, right? And that OR'ing bits is cumulative.

Thanks! Not sure I fully understand you. Are you saying that I should be setting values into a specific register at once using a value instead of OR'ing them? What method would you recommend?

> Use of odd library and defines, not likely to be compilable for others.

For now, this is okay since its meant only for me and purely as a learning exercise but I do get your point.