【单片机笔记】STM32F4那些事之-时钟踩过的坑

之前有用过STM32的F4系列单片机,该单片机的强大之处可以度娘一下,因为打算系统的学习下,所以自己做了一个板子用作学习,把资料都准备好,主要是官方的标准库及例程,学32第一件事情就是要搞清楚时钟,相对传统的51,32的时钟相对复杂很多,M3内核如此,M4内核更甚。下面贴出M4的时钟树:

【单片机笔记】STM32F4那些事之-时钟踩过的坑_第1张图片

可以看到时钟树很复杂,我用的芯片是STM32F401系列的,官方给的数据是最高时钟能达到84M,我也有朋友直接超频到120M用的,不过在产品设计上还是要以稳定为主。上图的注释是我后来加的,这里主要记录一下我如何配置系统时钟及踩到坑。在最开始的时候使用官方的例程,发现时钟不对,查底层的时候发现官方测试的目标板使用的是25M的晶体,而我们常用的是8M,我这里也使用的8M晶体,所以我进行了如下修改:

【单片机笔记】STM32F4那些事之-时钟踩过的坑_第2张图片

首先常用的系统源是PLL,而在M4内核上PLL时钟是经过内部16M或者外部晶振经过M除频、N倍频和P除频来的。这里我自己写了一个时钟函数,可以选择外部时钟和内部时钟。代码如下:

#define HSE RCC_PLLSource_HSE
#define HSI RCC_PLLSource_HSI

//RCC_PLLSource:PLL时钟源 RCC_PLLSource_HSE、RCC_PLLSource_HSI
//PLLM:主 PLL和音频PLL输入时钟的分频系数 范围2-63
//PLLN:倍频系数 范围2-510
//	小心: 软件必须正确设置这些位,确保 VCO 输出频率介于 192 和 432 MHz 之间。
//	VCO 输出频率 = VCO 输入频率 × PLLN 并且 192 <= PLLN <= 432
//PLLP:主系统时钟的主 PLL (PLL) 分频系数 范围2、4、6、8
//PLLQ:主 PLL (PLL) 分频系数,适用于 USB OTG FS 范围2-15
//使用时确保晶振频率PLLM分频为1MHz即可
void SysClock_Configuration(uint32_t RCC_PLLSource, uint32_t PLLM)
{
	__IO uint32_t HSEStatus = 0;
	
    RCC_DeInit();  

	if(RCC_PLLSource_HSE == RCC_PLLSource){	//选择外部时钟
		RCC_HSEConfig(RCC_HSE_ON);                                   	//打开外部时钟
		if(RCC_WaitForHSEStartUp() == SUCCESS){
			HSEStatus = 1;
		}
		else{
			RCC_HSEConfig(RCC_HSE_OFF);	//关闭外部时钟
			RCC_HSICmd(ENABLE);	
		}			
	}
	
	RCC_HCLKConfig(RCC_SYSCLK_Div1);                          	//HCLK(AHB)时钟为系统时钟1分频			
	RCC_PCLK1Config(RCC_HCLK_Div2);                            	//PCLK(APB1)时钟为HCLK时钟2分频
	RCC_PCLK2Config(RCC_HCLK_Div1);                            	//PCLK(APB2)时钟为HCLK时钟1分频	

	if(HSEStatus == 1) {                 							
		//PLL时钟配置,外部晶振为8MHz,系统配置为8/PLLM*PLLN/PLLP
		RCC_PLLConfig(RCC_PLLSource_HSE, PLLM, 336, 4, 7);   
	}
	else{
		//PLL时钟配置,内部晶振为16MHz,系统配置为16/16*336/4 =84MHz usb=336/7=48
		RCC_PLLConfig(RCC_PLLSource_HSI, 16, 336, 4, 7);   
	}
	
	RCC_PLLCmd(ENABLE);                                         //开启PLL时钟,并等待PLL时钟准备好
	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);      			//选择PLL时钟为系统时钟
   
	while(RCC_GetSYSCLKSource() != 0x08);                      	//Wait till PLL is used as system clock source
	RCC_ClockSecuritySystemCmd(ENABLE);							//打开时钟安全系统
}

这个函数在主函数的入口处执行,注释的也很详细。传入参数有两个,第一个是选择内部时钟或者外部时钟,代码中有定义,第二个是M除频,在使用外部时钟的时候需要根据目标版的晶体频率选择,以保证经过晶体频率经过M除频后是1MHz的频率。如果是选用的内部时钟,则第二个参数无效,可以看到代码里面直接是16的除频而没有用到输入的参数作为M除频。这是因为内部时钟是固定的16MHz,所以就固定死了。亲测这个函数是有效的。但是我在使用的时候开始就出现了问题。在一些外设的配置中时钟就不对,这里拿串口作为例子。通过串口的配置底层可以看到一个函数:

【单片机笔记】STM32F4那些事之-时钟踩过的坑_第3张图片

【单片机笔记】STM32F4那些事之-时钟踩过的坑_第4张图片

可以看到串口的波特率是系统根据时钟去自动计算的,所以在计算之前需要得到时钟频率,此时会调用上图的获取时钟函数。这个地方也是我采坑的地方。花了点时间终于找到问题,跳进去时钟获取函数去看发现了一个宏定义:

【单片机笔记】STM32F4那些事之-时钟踩过的坑_第5张图片

就是这个,一个是内部时钟一个是外部时钟,继续跟进会看到:

【单片机笔记】STM32F4那些事之-时钟踩过的坑_第6张图片

它上面标的是25Mhz,这就是外设时钟不对的地方。其实时钟那条线都是完全ok的,只不过在配置这个串口的时候调用了这个错误的宏定义,然后就导致外设输出结果不对。找到问题就好办了,把25M改成自己的8M,问题解决,世界安静了。

Urien

2019年11月21日 12:46:45

 

 

你可能感兴趣的:(单片机)