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

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

——基于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];

}

相关数据

评测宝骏560怎么样及宝骏560的性能如何
365淘房APP官网下载

评测宝骏560怎么样及宝骏560的性能如何

⌛ 08-07 👁️ 2791
每一款都狠实用!超全高尔夫app分享📱
365游戏厅网页登录

每一款都狠实用!超全高尔夫app分享📱

⌛ 07-30 👁️ 4755
vivo手机怎么设置应用锁
365bet资讯

vivo手机怎么设置应用锁

⌛ 08-19 👁️ 6605
联系太多老师,最后不想去了怎么办?
365bet资讯

联系太多老师,最后不想去了怎么办?

⌛ 07-16 👁️ 2639