一、系统时钟配置

基本概念

HSE 振荡器时钟:可以由外部源(可能是其他单片机输出的时钟信号)和外部晶振提供。野火霸天虎 V2 开发板是由外部晶振提供信号源,使用时需要先确定稳定 (RCC->CR的HSERDY)​并使能 (RCC->CR的HSEON)​后使用。
HSI 系统内部时钟:开机上电后系统首先使用的时钟,SYSCLK 默认初始使用的时钟。
PLLCLK 时钟:由 HSE 时钟经过 PLL 机构进行分频、倍频之后得到的时钟。
SYSCLK 系统时钟:可以设置成 HSI、HSE 以及 PLLCLK 作为输入。

时钟树

时钟树左侧都是和片外进行信号通信的输入输出的时钟源信号,右侧箭头最终指向系统给各个片内设备的时钟。

先关注系统时钟,也就是 HCLK到AHB总线、内核、存储器和DMA的时钟。通过时钟树可以看到,到 AHB 总线、内核、存储器和 DMA 的时钟由 SYSCLK 提供,而 SYSCLK 由 HSI、HSE 或 PLLCLK 三者其中一个提供,通常为 PLLCLK。而 PLLCLK 的源头是 HSE 经过 PLL 的各种变频变化后得到。

这样系统的时钟的来源就很清晰了,这样就可以配置系统时钟了。

代码编写

手动写代码配置时钟,通过正常配置时钟后点亮 LED,并通过配置时钟频率来控制 LED 闪烁的速度快慢。

仅展示时钟配置的函数部分(部分参考标准库固件时钟配置函数)

#include "bsp_myclkconfig.h"

void MyClkconfig(){
	__IO uint32_t HSE_READY = 0;
	RCC->CR |= ((uint32_t)RCC_CR_HSEON);
	
	while(1){
		HSE_READY = RCC->CR & RCC_CR_HSERDY;
		if(HSE_READY == (0xFFFF & RCC_CR_HSERDY)){
			break;
		}
	}
	RCC->APB1ENR |= (uint32_t)(RCC_APB1ENR_PWREN);
	PWR->CR |= PWR_CR_VOS_1;
	RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
	RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
	RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
	// 由于PLLCFGR中PLLP的值2、4、6、8对应00、01、10、11,所以需要搞(((2 >> 1) - 1) << 16)这么个操作才能把值弄对。
	RCC->PLLCFGR |= 25 | 336<<6 | (((2 >> 1) - 1) << 16) | 7 << 24;
	RCC->CR |= RCC_CR_PLLON;
	
	while(1){
		if((RCC->CR & RCC_CR_PLLRDY) == 0){
			break;
		}
	}

	/* Configure Flash prefetch, Instruction cache, Data cache and wait state */
	FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;

	/* Select the main PLL as system clock source */
	RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
	RCC->CFGR |= RCC_CFGR_SW_PLL;

	/* Wait till the main PLL is used as system clock source */
	while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL)
			;
	{
	}
	
	
}

BUG 点记录

在编写到判断 HSE 时钟是否初始化完成的时候,下意识地写了 if(HSE_READY == 1)​的时候 break 跳出 while 循环的代码,以为能用,结果一直不通。后面 debug 后才发现,当 HSE_READY​的值为 1 的时候,RCC->CR & RCC_CR_HSERDY​的值应该是 0x0200​,也就是说是第 14 位的值为 1 才对,所以应该要判断是否等于 0x0200​才对。但是这么写兼容性又不是很强,所以考虑写成 0xFFFF & RCC_CR_HSERDY(虽然也不会真的移植到其他 F4xx 板子上用)。

二、配置 MCO 输出时钟信号

配置 MCO 后可以通过 GPIO 口输出时钟信号,作为一个信号源给其他的设备。

配置方式:先初始化 MCOx 对应的 GPIO,再使能 MCOx 即可。

手册翻阅

  1. 先查 MCOx 对应的 GPIO

    在《STM32F4xx 参考手册》中找到 6.2.10 时钟输出功能,可以看到有 MCO1 和 MCO2,其中 MCO1 对应 PA8,MCO2 对应 PC9。

  2. 对 MCO 进行基础配置

    在《STM32F4xx 参考手册》中找到 6.3.3 RCC 时钟配置寄存器 (RCC_CFGR),MCOx 可以选择时钟源,MCOxPRE 可以配置分频。

代码编写

init_MCO1.c

void init_MCO1(){
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIOA_CONFIG;
	GPIOA_CONFIG.GPIO_Pin = GPIO_Pin_8;
	GPIOA_CONFIG.GPIO_Mode = GPIO_Mode_AF;
	GPIOA_CONFIG.GPIO_OType = GPIO_OType_PP;
	GPIOA_CONFIG.GPIO_Speed = GPIO_Low_Speed;
	
	GPIO_Init(GPIOA, &GPIOA_CONFIG);
	
	RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_2);
}

由于没有示波器,好像测不出结果,没办法验证,先这样了。