完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
上一篇博客总结了时钟框图、时钟相关寄存器。在博客的最后又总结了如何在main.c中重新设置系统时钟并通过MCO(PA8复用引脚)输出系统时钟,再通过示波器察看系统时钟的波形。上一篇博客也总结到,在main.c中重新设置系统时钟时,如果设置不成功,就依旧采用默认的系统时钟;当然重新设置系统时钟时,AHB、APB1、APB2等分频系数、flash等不需要再重新设置,因为程序编译时已经在底层的system.stm32f10x.c中设置了这些,如果外设没有特殊要求,在main.c中重新设置时钟时这些就不用再设置了。
本博客对system.stm32f10x.c中时钟相关的函数进行总结,通过本篇博客应该就可以清楚知道默认的系统时钟是怎么来的?如何在底层中修改系统时钟?如果外部晶振接的不是8MHZ,那么此时默认的系统时钟是多少、怎么计算? 一、SystemInit() 打开system_stm32f10x.c文件,找到SystemInit()函数 现一句句分析: 1、打开HSI时钟 RCC-》CR |= (uint32_t)0x00000001 //打开HSI时钟 HSI在这里打开后就不会再关闭了,所以也不用等待它使能就绪。HSI打开的目的很明确,就是在HSE、PLL作为系统时钟源发生故障时,通过CSS的中断来自动将HSI切换为系统时钟源,所以HSI一直开启。 2、关闭MCO、ADCPRE、PPRE2、PPRE1、HPRE、SWS、SW RCC-》CFGR &= (uint32_t)0xF0FF0000; //关闭MCO、ADCPRE、PPRE2、PPRE1、HPRE、SWS、SW 还是觉得这个底层有问题,位27应该要置0(我的板子是HD,不是CL),但是根据寄存器,位27是保留位,应该与1进行与运算,所以这个地方有问题,但不影响使用。 这行代码设置的东西: 即:关闭MCO时钟输出、AHB不分频、APB1不分频、APB2不分频、ADC 2分频、SHI作为系统时钟 3、使能PPL和CSS、关闭HSE RCC-》CR &= (uint32_t)0xFEF6FFFF; //使能PPL和CSS、关闭HSE 4、HSEBYP清零 RCC-》CR &= (uint32_t)0xFFFBFFFF; 位18置零。表示HSE没有被旁路 5、PLLSRC、PLLXTPRE、PLLMUL、USBPRE RCC-》CFGR &= (uint32_t)0xFF80FFFF; 位16~22清零,表示:USB预分配系数1.5、PLLMUL 2倍频输出、SHE不分频做PLL的时钟来源、PLL输入时钟源为HSI/2。如下图紫色部分所示。 6、CSSC、PLLRDYC RCC-》CIR = 0x009F0000; 此代码清除CSS时钟的系统中断、清除PLL就绪中断 7、进入SysClock(); 接下来就进入SysClock()函数了。 8、总结(重要) 从之前的七小节得到以下结论: 现在的状态: 1、HSI:打开,并作为系统时钟 2、HSE:关闭 3、MCO:关闭 4、PLL:打开 5、CSS:打开 6、CSS系统中断标志位:清除 7、PLL就绪中断:清除 8、HSE旁路:未旁路 9、USB预分频:1.5,USB采用SYSCLK/1.5时钟 10、AHB预分频:1,AHB输出后为系统时钟的大小 11、APB1预分频:1,到APB1外设的时钟PCLK1=SYSCLK 12、APB2预分频:1,到APB2外设的时钟PCLK2=SYSCLK 13、ADC预分频:2,到ADC的时钟ADCCCLK=SYSCLK/2 14、PLLMUL选择器输出倍频:2 15、SHE可直接提供PLL时钟来源 16、HSI/2可直接提供PLL时钟来源 在时钟框图中标注(绿色) 注意以下几点: 1、APB1低速外设总线的时钟最大为36MHZ,所以这个地方之后可能需要更改倍频系数 2、ADC的最大时钟为14MHZ,所以这个地方之后可能需要更改倍频系数 二、SetSysClock() 底层更改系统时钟的方法 SystemInit()函数执行到最后就进入SetSysClock()函数了 点击进入SetSysClock() 可以看到定义不同的标识符,就执行对应的函数,从名称上也能看出是在配置哪个频率的系统函数 打开定义,注意此时只有72MHZ的函数和标识被定义,其他的都被注释了,所以只能用72MHZ相关的标识和函数打开定义(如果打不开,就一个个的试吧) 所以也看出来了,要想在底层中修改系统时钟,就在这里修改,如更改为36MHZ,就将SYSCLK_FREQ_36MHz解除注释,将SYSCLK_FREQ_72MHz再注释掉: 现分别看看这几个时钟配置函数 (一)SetSysClockTo72()函数 1、使能HSE时钟 RCC-》CR |= ((uint32_t)RCC_CR_HSEON); 即: RCC-》CR |= 0x00010000; 该行代码是使能HSE时钟 2、等待HSE时钟就绪 do { HSEStatus = RCC-》CR & RCC_CR_HSERDY; StartUpCounter++; } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); 分析一下这几行代码 即 HSEStatus = RCC-》CR & 0x00020000; 当HSE就绪后,位17变为1,此时 HSEStatus!=0 StartUpCounter != HSE_STARTUP_TIMEOUT是保证在一定时间内HSE就绪,不能无期限的等待它就绪,当就绪后,while后面的判断条件就不成立了,跳出循环。 当然保证HSE不会出错的情况下,这几行代码还可以改为: while((RCC-》CR & 0x00020000)==0) { } 3、flash设置 上图中框中代码是判断HSE是否就绪,如果就绪就执行接下来的代码。如果没有就绪,也不会重新执行HSE使能了,说明HSE出现了问题,系统时钟配置也就无法成功。 /* Enable Prefetch Buffer */ FLASH-》ACR |= FLASH_ACR_PRFTBE; /* Flash 2 wait state */ FLASH-》ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); FLASH-》ACR |= (uint32_t)FLASH_ACR_LATENCY_2; flash相关文件:stm32f10xxx闪存编程参考手册.pdf 这三行代码的定义的标识不好理解,直接换成十六进制数: /* Enable Prefetch Buffer */ FLASH-》ACR |= 0x10; /* Flash 2 wait state */ FLASH-》ACR &= (uint32_t)((uint32_t)~)0x03); FLASH-》ACR |= (uint32_t)0x02; 注意:stm32中二进制数是低位对齐,高位补零的,所以可以用一个8位二进制数对32位寄存器运算,只需前面补零就行。 第一行代码: 该行代码使能预取缓冲区 第二行代码: 该行代码是为了将位1:0置0,当然位31:8这些保留位也变为了0。 第三行代码: 该行的目的是把位1置1 此时位2:0位010,意思是设置设置SYSCLK周期与闪存访问时间的比例为2(目前是把系统时钟设置为72MHZ) 这里还有有点问题的,在第二行代码中,没有把位2置0,(底层有好几处不影响使用、但逻辑错误的地方)所以可以改为: FLASH-》ACR &= (uint32_t)((uint32_t)~)0x07);//第二行修改 所以flash设置为:每两个时钟周期,访问一次闪存。 4、设置分频系数(APB1) /* HCLK = SYSCLK */ RCC-》CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; /* PCLK2 = HCLK */ RCC-》CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; /* PCLK1 = HCLK */ RCC-》CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; 前两行的标识都为0,,或运算后CFGR寄存器的状态不变,就不用看了。直接第三行: 或运算后CFGR寄存器的位10变为1,即PPRE1为100 即设置APB1的分频系数为/2。 此时时钟框图状态:(绿色标注为SystemInit()后的状态,蓝色标注为新加状态) 5、设置倍频系数(PLLMUL) /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ RCC-》CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); RCC-》CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); 不好理解,把定义的标识重新用16进制数表示: RCC-》CFGR &= ~(0x00010000 | 0x00020000 |0x003C0000); RCC-》CFGR |= 0x00010000| 0x001C0000; 还是不好理解,再简化,将或运算和取反运算求出来: RCC-》CFGR &=0xffc0ffff; RCC-》CFGR |=0x001d0000; 第一行 目的是将位21:16置0。 第二行 目的是将位20、19、18、16置1 经过这俩行代码,位21:18=0111,位16=1 即这俩行代码设置PLL时钟源为HSE提供、PLLMUL为9倍频。 时钟框图中标注:(绿色为SystemInit()函数设置后状态,蓝色为本函数新设置的状态) 6、使能PLL时钟 RCC-》CR |= RCC_CR_PLLON; 将标识写为16进制数: RCC-》CR |= 0x01000000; 所以该行代码使能PLL时钟 接下来的代码等待PLL时钟就绪,之前已经总结过了,不在赘述。 while((RCC-》CR & RCC_CR_PLLRDY) == 0) { } 7、将PLL设置为系统时钟 RCC-》CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); RCC-》CFGR |= (uint32_t)RCC_CFGR_SW_PLL; 将标识改为16进制数: RCC-》CFGR &= (uint32_t)((uint32_t)~(0x00000003)); RCC-》CFGR |= (uint32_t)0x00000002; 即: RCC-》CFGR &= 0xfffffffc; RCC-》CFGR |= 0x00000002; 第一行 将CFGR寄存器的位1:0设置为00 第二行 将CFGR寄存器的位1:0设置为10 这俩行代码将PLL设置为系统时钟。 之后就是等待PLL时钟就绪: while ((RCC-》CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) { } 8、总结(重要) 目前的时钟状态: 72MHZ系统时钟来源:HSE(8M)经过9倍频得到PLL时钟,PLL时钟做系统时钟输入 如果外接的晶振为24MHZ,则只需将PLLMUL的倍频系数9改为3即可。还有一些晶振比如15M,不管如何设置分频、倍频都无法达到72MHZ,此时系统时钟只能选用《72MHZ的一个值。 (二)SetSysClockTo56()函数 代码与SetSysClockTo72()函数基本一样,唯一不同的是设置PLLMUL的倍频系数。 1、设置倍频系数(PLLMUL) /* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */ RCC-》CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); RCC-》CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL7); 将标识写成16进制的形式: /* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */ RCC-》CFGR &= (uint32_t)((uint32_t)~(0x00010000 | 0x00020000 | 0x003C0000)); RCC-》CFGR |= (uint32_t)(0x00010000 | 0x00140000); 还是不好理解,将或运算和取反运算算出来: /* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */ RCC-》CFGR &= 0xffc0ffff; RCC-》CFGR |= 0x00150000; 第一行: 将CFGR寄存器的位21:16置0 第二行: 将CFGR寄存器的位18、为16置1. 经过这俩行代码后,位21:18=0101,位16=1,位17=0 所以这俩行代码将HSE直接设置为PLL时钟源的输入,PLLMUL倍频系数为7,此时PLL时钟为7*8MHZ=56MHZ 之后就是使能PLL时钟、等待PLL时钟就绪、将PLL设置为系统时钟、等待系统时钟就绪,这些代码与SetSysClockTo72()函数的代码一样。 2、总结(重要) SetSysClockTo56()函数的设置与SetSysClockTo72()函数基本相同,唯一不同的地方是PLLMUL倍频系数的设置。 56MHZ时钟来源为:HSE(8MHZ)经过7倍频(PLLMUL)生成PLL时钟,PLL做为系统时钟输出。 时钟框图:(绿色为SystemInit()函数设置后的结果,蓝色为本函数设置的结果) (三)SetSysClockTo48()函数 代码与SetSysClockTo72()函数基本一样,唯一不同的是设置PLLMUL的倍频系数。 1、设置倍频系数(PLLMUL) /* PLL configuration: PLLCLK = HSE * 6 = 48 MHz */ RCC-》CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); RCC-》CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL6); 将标识改为16进制数: /* PLL configuration: PLLCLK = PREDIV1 * 6 = 48 MHz */ RCC-》CFGR &= (uint32_t)~(0x00010000| 0x00020000 | 0x003C0000); RCC-》CFGR |= (uint32_t)()0x00010000 | 0x00100000); 将或运算和取反运算算出来: /* PLL configuration: PLLCLK = PREDIV1 * 6 = 48 MHz */ RCC-》CFGR &= 0xffc0ffff; RCC-》CFGR |= 0x00110000; 第一行: 将CFGR寄存器位21:16置为0 第二行: 将位20和位16置1 此时位21:18=0100,位17=0,位16=1。 所以这俩行代码将HSE直接设置为PLL时钟源的输入,PLLMUL倍频系数为6,此时PLL时钟为6*8MHZ=48MHZ 之后就是使能PLL时钟、等待PLL时钟就绪、将PLL设置为系统时钟、等待系统时钟就绪,这些代码与SetSysClockTo72()函数的代码一样。 2、总结(重要) SetSysClockTo48()函数的设置与SetSysClockTo72()函数基本相同,唯一不同的地方是PLLMUL倍频系数的设置。 48MHZ时钟来源为:HSE(8MHZ)经过6倍频(PLLMUL)生成PLL时钟,PLL做为系统时钟输出。 时钟框图:(绿色为SystemInit()函数设置后的结果,蓝色为本函数设置的结果) (四)SetSysClockTo36()函数 代码与SetSysClockTo72()函数基本一样,不同的是设置PLLMUL的倍频系数、PLLXTPRE选择器设置。 1、设置倍频系数(PLLMUL)、设置选择器(PLLXTPRE) /* PLL configuration: PLLCLK = (HSE / 2) * 9 = 36 MHz */ RCC-》CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); RCC-》CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL9); 将标识改为16进制数: /* PLL configuration: PLLCLK = (HSE / 2) * 9 = 36 MHz */ RCC-》CFGR &= (uint32_t)((uint32_t)~(0x00010000 | 0x00020000 | 0x003C0000)); RCC-》CFGR |= (uint32_t)(0x00010000 |0x00020000| 0x001C0000); 将或运算和取反运算算出来: /* PLL configuration: PLLCLK = (HSE / 2) * 9 = 36 MHz */ RCC-》CFGR &= 0xffc0ffff; RCC-》CFGR |= 0x001f0000; 第一行: 将CFGR寄存器的位21:16置0 第二行: 将位20:16置1 执行这两行代码后,位21:18=0111;位17=1;位16=1; 所以是将HSE经过2分频后再9倍频作为PLL时钟来源,然后PLL时钟做系统时钟输入。此时PLL时钟为:9×(8MHZ/2)=36MHZ 之后就是使能PLL时钟、等待PLL时钟就绪、将PLL设置为系统时钟、等待系统时钟就绪,这些代码与SetSysClockTo72()函数的代码一样。 2、总结(重要) SetSysClockTo36()函数的设置与SetSysClockTo72()函数基本相同,唯一不同的地方是PLLMUL倍频系数的设置。 36MHZ时钟来源为:HSE(8MHZ)2分频后再经过9倍频作为PLL时钟来源,然后PLL时钟作为系统时钟来源。 时钟框图:(绿色为SystemInit()函数设置后的结果,蓝色为本函数设置的结果) (五)SetSysClockTo24()函数 代码与SetSysClockTo72()函数基本一样,不同的是设置PLLMUL的倍频系数、PLLXTPRE选择器设置。 1、设置倍频系数(PLLMUL) /* PLL configuration: = (HSE / 2) * 6 = 24 MHz */ RCC-》CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); RCC-》CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL6); 将标识改为16进制数: /* PLL configuration: = (HSE / 2) * 6 = 24 MHz */ RCC-》CFGR &= (uint32_t)((uint32_t)~(0x00010000 | 0x00020000 | 0x003C0000)); RCC-》CFGR |= (uint32_t)(0x00010000 | 0x00020000 | 0x00100000); 将或运算和取反运算算出来: /* PLL configuration: = (HSE / 2) * 6 = 24 MHz */ RCC-》CFGR &=0xffc0ffff; RCC-》CFGR |= 0x00130000; 第一行: 将CFGR寄存器的位21:16置0 第二行: 将CFGR寄存器的为20、位17、位16置1。 执行这两行代码后,位21:18=0100;位17=1;位16=1。 所以是将HSE经过2分频后再6倍频作为PLL时钟来源,然后PLL时钟做系统时钟输入。此时PLL时钟为:6×(8MHZ/2)=24MHZ 之后就是使能PLL时钟、等待PLL时钟就绪、将PLL设置为系统时钟、等待系统时钟就绪,这些代码与SetSysClockTo72()函数的代码一样。 2、总结(重要) SetSysClockTo24()函数的设置与SetSysClockTo72()函数基本相同,唯一不同的地方是PLLMUL倍频系数的设置。 24MHZ时钟来源为:HSE(8MHZ)经过2分频后再经过6倍频(PLLMUL)生成PLL时钟,PLL做为系统时钟输出。此时PLL时钟为:6×(8MHZ/2)=24MHZ 时钟框图:(绿色为SystemInit()函数设置后的结果,蓝色为本函数设置的结果) (六)SetSysClockToHSE()函数 代码与SetSysClockTo72()函数基本一样,不同的是不用设置PLLMUL的倍频系数、PLLXTPRE选择器设置,直接将HSE设置为系统时钟,操作CFGR寄存器的位1:0即可。然后就等待系统时钟就绪,不用再对PLL时钟进行使能、等待就绪。 1、设置 /* Select HSE as system clock source */ RCC-》CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); RCC-》CFGR |= (uint32_t)RCC_CFGR_SW_HSE; 将标识改为16进制数: /* Select HSE as system clock source */ RCC-》CFGR &= (uint32_t)((uint32_t)~(0x00000003)); RCC-》CFGR |= (uint32_t)0x00000001; 将取反运算算出来 /* Select HSE as system clock source */ RCC-》CFGR &= 0xfffffffc; RCC-》CFGR |= 0x00000001; 第一行: 该行代码将CFGR寄存器位1:0清零 第二行: 该行代码将CFGR寄存器位0置1。 执行这两行代码后CFGR寄存器的位1:0=01。 2、总结(重要) SetSysClockToHSE()函数的设置与SetSysClockTo72()函数基本相同,不同的是不用设置PLLMUL的倍频系数、PLLXTPRE选择器设置,直接将HSE设置为系统时钟,操作CFGR寄存器的位1:0即可。然后就等待系统时钟就绪,不用再对PLL时钟进行使能、等待就绪。 系统时钟=HSE(8MHZ) 时钟框图:(绿色为SystemInit()函数设置后的结果,蓝色为本函数设置的结果) |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1777 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1621 浏览 1 评论
1080 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
728 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1678 浏览 2 评论
1938浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
731浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
570浏览 3评论
595浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
554浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 11:28 , Processed in 0.879598 second(s), Total 76, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号