看懂时钟树——掌握系统时钟配置

——基于STM32F107VCT6的理解

 

1、HIS(内部高速时钟:8MHZ)

内部高速时钟是芯片自带的时钟,芯片自带时钟有两个HIS和LSI(内部低速时钟:40kHZ),内部时钟是RC振荡器产生的,不够稳定。一般不长时间作为系统时钟使用,一般做备用,或在切换时使用。

2、HSE(外部高速时钟:3-25MHZ)

STM32提供两组外部时钟接口,HSE和LSE(外部低速时钟)。外部时钟的大小由外部所接晶振确定(本文HSE=25MHZ,LSE=32.768kHZ),所以较为准确。

3、时钟树看图引导

从图中可知SYSCLK(系统时钟)的来源可以是SHE、SHI和PLLCLK(分频器1),这里通过SW(两位二进制数00、01、10、11)来选择,具体可参考《STM32F10xxx参考手册》第88页,CSS的作用是用来检测HSE是否正常的。HSE和SHI都是固定不变的(硬件不变的情况下),所以想要设置不同的时钟频率出来一般选择PLLCLK作为系统时钟。PLLCKL的时钟源有3中不同的情况:(1)SHI经过2分频,经过PLLSCR选择(一位二进制数:0选择图中上面的路线,1选择下面路线);(2)SHE经过PREDIV1SCR选择(0选择上面路线,1选择下面路线),在经过PREDIV1(分频器1),再经过PLLSCR选择;(3)SHE经过PREDIV2分频,进入PLL2MUL倍频,经过PREDIV1SCR选择(0选择上面路线,1选择下面路线),在经过PREDIV1(分频器1),再经过PLLSCR选择。三种情况如下:

对于情况(1)和(2)产生的的时钟频率相对较少,有多种的情况都不能产生,而情况(3)则有多种选择,不能产生的频率,也可以产生一个误差小于0.1MZH与目标频率相近的评率。本文采用(3)这条路线。

4、设计步骤

(1)、切换内部时钟:SHI使能(SHION写1)、等待SHI稳定(SHIDRY被硬件置1)、系统时钟切换为内部时钟(SW写00)、等待切换成功(SWS被硬件置00);

说明:若某条线路被作为了SYSCLK,则改线路的所有的配置都不能被更改,只能暂时切换到其他路线。SHI只有被使能了才能切换成功。

(2)、配置相关分频、倍频系数。关闭PLL2,PLL使能(PLLON、PLL2ON写0)选择PREDIV2、PREDIV1的分频系数,选着PLLMUL2、PLLMUL的倍频系数,PREDIV1SCR、PLLSCR的时钟源;打开SHE(SHEON写1),等待SHE稳定(SHERDY被硬件写1),打开PLL2,PLL使能(PLLON、PLL2ON写1),等待准备就绪(PLLRDY、PLL2RDY被硬件写1);

(3)、SYSCLK切换成PLLCLK。SYSCLK由SHI切换成PLLCLK(SW写10),等待切换成功(SWS被置为10)

5、程序设计(系统时钟设置子函数)

方案一:通过运算得到与目标频率最相近的分频、倍频方案,并进行设置。

优点:只要改变带入参数的类型,可计算产生小数的频率;

缺点:每次都要进行计算,运算量大,要时间计算;

//1、寄存器的声明定义(只声明用到的)
#define	RCC_CR       *(uint32_t *)0x40021000
#define	RCC_CFGR     *(uint32_t *)0x40021004
#define	RCC_CFGR2    *(uint32_t *)0x4002102c

/**********************************
//重设系统时钟函数
//可设置范围:4-72MHZ
//带入参数:  需要设置的系统时钟频率
//带入参数类型:int
//返回值:实际设置的成的系统时钟频率
//注意事项:
//1、低于4MHZ的不能设置,程序在
// while((RCC_CR & 0x0a000000) != 0x0a000000 );死循环
//2、高于72MHZ系统时钟设置为72MHZ
***********************************/
double ResetSysClK(int Sysclk_Value)
{
	unsigned char PLLMUL,PLLMUL2,M1,M2,D1,D2;
	unsigned int DIV1,DIV2;
	double clk_set,clk_temp,abs_clk_temp,abs_clk_min = 72.0;
	float    MUL[7]       = {4,5,6,7,8,9,6.5};
	float    MUL2[9]      = {8,9,10,11,12,13,14,16,20};
	unsigned int MUL_Code[7]  = {2,3,4,5,6,7,13};
	unsigned int MUL2_code[9] = {6,7,8,9,10,11,12,14,15};
	for(M1 = 0;M1 < 7;M1++) //PLLMUL[8]
		for(M2 = 0;M2 < 9;M2++)//PLL2MUL
			for(D1 = 1; D1 < 17;D1++)//PREDIV1[16]
				for(D2 = 1;D2 < 17 ;D2++)//PREDIV1[16]
				{
					//计算当前组合的时钟频率
					clk_temp = (25.0*MUL[M1]*MUL2[M2]) / ((double)D1*(double)D2);
					//时钟频率在1-72MZH的组合计进入判断
					if(  clk_temp<=72.0 && clk_temp >=1.0 )
					{
						//计算当前组合与想要得到的时钟频率差
						abs_clk_temp = Sysclk_Value - clk_temp;
						//若当前组合产生的时钟频率就是想要的时钟频率
						//则不用继续遍历后面的组合,跳出所有循环
						if(abs_clk_temp == 0.0)
						{
							clk_set = clk_temp;
							PLLMUL  = M1;
							PLLMUL2 = M2;
							DIV1    = D1;
							DIV2		= D2;
							M1 = M2 = 10;
							D1 = 20;
							break;//只能跳出最小的循环
						}
						if(abs_clk_temp < 0.0)
							abs_clk_temp = (-abs_clk_temp);
						//遍历所有组合找出时钟频率组合与目标时钟频率相差最小的组合
						if(abs_clk_temp < abs_clk_min)
						{
							//保留当前更合适的组合
							abs_clk_min = abs_clk_temp;
							clk_set = clk_temp;
							PLLMUL  = M1;
							PLLMUL2 = M2;
							DIV1    = D1;
							DIV2		= D2;
						}
					}
				}
				
		//HSI时钟使能
	RCC_CR = ((RCC_CR | 0x00000001));
	//延时直到HSI准备就绪
  while((RCC_CR & 0x00000002) == 0);
	//切换内部时钟HSI
	RCC_CFGR = (RCC_CFGR & 0xfffffffc);			
	while((RCC_CFGR & 0x0000000c) != 0);	
	//HSE使能(打开)
	RCC_CR = (RCC_CR & 0xfffeffff)|0x00010000;			
	while((RCC_CR & 0x00020000) != 0x00020000);			
	//关闭PLLON和PLL2ON
	RCC_CR = (RCC_CR & 0xf0ffffff);
	//设置MUL的倍率、PLLSRC(选择PREDIV1输出作为PLL输入时钟)
	RCC_CFGR = (RCC_CFGR&0xffc1ffff)|0x00010000|(MUL_Code[PLLMUL]<<18);
	//设置MUL2、DIV2、DIV1、PREDIV1SRC(PLL2作为PREDIV1的时钟源)
	RCC_CFGR2 = (RCC_CFGR2 & 0xfffef000)|0x00010000|( MUL2_code[PLLMUL2]<<8)|( (DIV2-1)<<4 )|(DIV1-1);
	//PLL、PLL2使能
	RCC_CR |= 0x05000000;
	/* Wait till PLL2 and PLL is ready */
  while((RCC_CR & 0x0a000000) != 0x0a000000 );
	/* Select PLL as system clock source */
  RCC_CFGR |= 0x00000002;    
  /* Wait till PLL is used as system clock source */
  while ((RCC_CFGR & 0x00000008) != (uint32_t)0x08);  
	return clk_set;
}

 

方案二:预先将能4-72MHZ的整数频率的最佳方案计算出来,放入数组。

优点:可以直接通过查表得到分频倍频信息,不用计算运行时间短;

缺点:只能输入只能是4-72的整数。代码量较多。

#define	*RCC_CR       (uint32_t *)0x40021000
#define	*RCC_CFGR     (uint32_t *)0x40021004
#define	*RCC_CFGR2    (uint32_t *)0x4002102c

//对应位的编码数组
//其二进制码经过位移合并到相应位就可以了
//顺序:MUL_Code、MUL2_Code、DIV1_Code、DIV2_Code
int data[69][4] = {
	{2,      7,      14,     14,},
	{2,      6,      9,      15,},
	{2,      7,      9,      14,},
	{5,      7,      14,     14,},
	{2,      6,      9,      9,},
	{2,      7,      9,      9,},
	{2,      6,      4,      15,},
	{2,      9,      9,      9,},
	{2,      7,      4,      14,},
	{2,      11,     9,      9,},
	{2,      12,     9,      9,},
	{2,      7,      3,      14,},
	{2,      6,      4,      9,},
	{5,      12,     8,      15,},
	{2,      7,      4,      9,},
	{7,      11,     10,     13,},
	{2,      6,      3,      9,},
	{4,      12,     9,      9,},
	{2,      9,      4,      9,},
	{7,      7,      7,      10,},
	{2,      10,     4,      9,},
	{2,      6,      1,      15,},
	{2,      11,     4,      9,},
	{4,      7,      4,      9,},
	{2,      12,     4,      9,},
	{3,      11,     3,      13,},
	{2,      7,      1,      14,},
	{2,      11,     2,      13,},
	{2,      6,      4,      4,},
	{4,      9,      4,      9,},
	{5,      12,     5,      11,},
	{2,      12,     3,      9,},
	{2,      7,      4,      4,},
	{5,      9,      3,      12,},
	{7,      11,     6,      10,},
	{4,      11,     4,      9,},
	{2,      6,      1,      9,},
	{2,      14,     2,      12,},
	{4,      12,     4,      9,},
	{3,      9,      1,      15,},
	{2,      9,      4,      4,},
	{2,      7,      1,      9,},
	{7,      7,      3,      10,},
	{13,     11,     2,      14,},
	{2,      10,     4,      4,},
	{5,      12,     4,      9,},
	{2,      6,      0,      15,},
	{3,      15,     6,      6,},
	{2,      11,     4,      4,},
	{3,      12,     2,      10,},
	{4,      7,      4,      4,},
	{2,      9,      1,      9,},
	{2,      12,     4,      4,},
	{5,      11,     3,      9,},
	{3,      11,     1,      13,},
	{4,      9,      1,      13,},
	{2,      7,      0,      14,},
	{4,      11,     1,      15,},
	{2,      11,     2,      6,},
	{5,      7,      4,      4,},
	{2,      14,     4,      4,},
	{2,      11,     1,      9,},
	{4,      9,      4,      4,},
	{3,      12,     1,      12,},
	{5,      12,     2,      11,},
	{5,      11,     2,      10,},
	{2,      12,     1,      9,},
	{5,      11,     1,      15,},
	{4,      10,     4,      4,},
};
//实际设置成功的频率数组
//与上面一一对应
float set_clk[69]={
	4.000000,       5.000000,       6.000000,       7.000000,       
	8.000000,       9.000000,       10.000000,      11.000000,      
	12.000000,      13.000000,      14.000000,      15.000000,      
	16.000000,      17.013889,      18.000000,		18.993506,      
	20.000000,      21.000000,      22.000000,      23.011364,      
	24.000000,      25.000000,      26.000000,      27.000000,      
	28.000000,      29.017857,      30.000000,      30.952381,      
	32.000000,      33.000000,		34.027778,      35.000000,      
	36.000000,      37.019231,      37.987013,      39.000000,      
	40.000000,      41.025641,      42.000000,      42.968750,      
	44.000000,      45.000000,      46.022727,      46.944444,      
	48.000000,		49.000000,      50.000000,      51.020408,      
	52.000000,      53.030303,      54.000000,      55.000000,      
	56.000000,      56.875000,      58.035714,      58.928571,      
	60.000000,      60.937500,      61.904762,      63.000000,
	64.000000,      65.000000,      66.000000,      67.307692,      
	68.055556,      68.939394,      70.000000,      71.093750,      
	72.000000 };
	/*重设系统时钟函数*/
float ResetSysClK1(int Sysclk_Value)
{
		//HSI时钟使能
	*RCC_CR = ((*RCC_CR | 0x00000001));
	//延时直到HSI准备就绪
  while((*RCC_CR & 0x00000002) == 0);
	//切换内部时钟HSI
	* RCC_CFGR = (*RCC_CFGR & 0xfffffffc);			
	while((*RCC_CFGR & 0x0000000c) != 0);	
	//HSE使能(打开)
	* RCC_CR = (*RCC_CR & 0xfffeffff)|0x00010000;			
	while((*RCC_CR & 0x00020000) != 0x00020000);			
	//关闭PLLON和PLL2ON
	*RCC_CR = (*RCC_CR & 0xf0ffffff);
	//设置MUL的倍率、PLLSRC(选择PREDIV1输出作为PLL输入时钟)
	*RCC_CFGR = (*RCC_CFGR&0xffc1ffff)|0x00010000|(data[Sysclk_Value-4][0]<<18);
	//设置MUL2、DIV2、DIV1、PREDIV1SRC(PLL2作为PREDIV1的时钟源)
	*RCC_CFGR2 = (*RCC_CFGR2 & 0xfffef000)|0x00010000|( data[Sysclk_Value-4][1]<<8)|( data[Sysclk_Value-4][3]<<4 )|data[Sysclk_Value-4][2];
	//PLL、PLL2使能
	*RCC_CR |= 0x05000000;
	/* Wait till PLL2 and PLL is ready */
  while((*RCC_CR & 0x0a000000) != 0x0a000000 );
	/* Select PLL as system clock source */
  *RCC_CFGR |= 0x00000002;    
  /* Wait till PLL is used as system clock source */
  while ((*RCC_CFGR & 0x00000008) != (uint32_t)0x08);  
	return set_clk[Sysclk_Value-4];
	
}

 

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值