打开昨天找到的STM32F4的参考手册,用着4级压线的英语水平,搭配某道词典,看文档吧。
M4真的跟M3的资料差很远,中文资料少之又少啊,全图书馆就找到一本《ARM Cortex-M4嵌入式实战开发精解--基于STM32F4》这本书的翻译水平跟我的4级差不多。。。已经是STM32F4的唯一的中文书籍了。
--------------------------------------------------------------------------------------
今天就先来研究RCC,开始我还不知道上电之后,这系统时钟到底是多少?外部晶振还是内部晶振?
STM32F4的RCC具有非常大的灵活性,SYSCLK系统时钟可以由HSI、HSE、或者主PLL时钟等驱动。
还有两个次要的时钟源,一个是32KHZ的LSI,一个是32.768KHZ的LSE。
SYSCLK通过分频来配置AHB、APB2、APB1等时钟域,其中AHB最高可达168MHZ,APB2可达84MHZ,APB1可达42MHZ.
但是 USB OTG FS、USB OTG HS、I2S、Ethernet MAC等时钟并非由SYSCLK派生。
SysTick的时钟可选外部时钟源也可以AHB时钟的8分频(这里不知道理解的对不对,因为下面又说又硬件自动匹配。)
----------------------------------------------------------------------------
HSE可以从外部接入一个时钟源或者是晶振、陶瓷振荡器等。(见过8M的也见过25M的)
-
HSI由内部RC振荡器产生的16MHZ时钟信号,可以直接用于系统时钟,也可以当作PLL的输入时钟。
-
主PLL有两个不同的输出,一个是产生高速系统时钟,另一个用于产生USB SDIO RNG这些设备的时钟。
配置主PLL的参数要在PLL使能之前配置好,因为当PLL使能之后,再去设置参数,比如PLL输入时钟源,PLL的M,N,P,Q等都是无效的。
还有一个专用的PLL(PLLI2S),用于生成精准的时钟来实现高质量的音频。
-
LSE时钟来时外部的晶振/陶瓷振荡器,提供一个低功耗-高精度的低速时钟源给RTC。
-
LSI是一个提供给独立看门狗的低功耗的时钟源,即使在STOP或者STANDBY模式下依旧运行,也供给自动唤醒单元(很多东西我还没接触过,只管翻译和理解吧。)
-
剩下的什么 时钟安全保障系统、RTC、AWU的时钟、看门狗时钟,时钟输出性能MCO1、MCO2、定时器测量时钟等我就不看 了,太累了,以后用到再看吧。
------------------------------------------------------------------------------------------
加下来进入最重要的,RCC相关寄存器的介绍
图片我就不插了,在参考手册里的123页~181页
主要的寄存器有
RCC控制寄存器 RCC_CR
这里主要是PLL和PLLI2S的使能和状态、外部时钟的使能、内部时钟的校准、修正、状态、使能等。
PLL配置寄存器 RCC_PLLCFGR
这里寄存器就是关于PLL输出时钟的计算公式参数的设定,PLLQ/PLLP/PLLN/PLLM等。计算公式我看了看程序才知道。
RCC时钟配置寄存器 RCC_CFGR
这个寄存器主要是设置输出时钟MCO12、RTC时钟分频、I2S时钟选择、AHB/APB1/APB2等时钟的分频、系统时钟的切换等。
RCC时钟中断寄存器 RCC_CIR
这个寄存器主要是各种时钟稳定后触发中断的使能以及相关的标志清除。
RCC_AHB1、AHB2、AHB3、APB1、APB2总线相关的寄存器,很多很多。主要是SET和RESET各种外设的时钟源。
最后有一个RCC register map,算是一个表方便查询吧。
--------------------------------------------------------------------------
最后研究一下,库函数下面是如何设置系统时钟的。
程序也不贴了,找个固件库例程都能找到。
首先在startup_stm32f40_41xxx.s里面找到SystemInit函数入口,这个函数会在main函数之前被调用,也就是无须在进入main函数之后再调用。
进入System函数,第一条语句是
RCC->CR |= (uint32_t)0x00000001; 也就是使能HSI内部振荡器。
接着是
RCC->CFGR = 0x00000000;
清零RCC_CFGR寄存器.
接着是
RCC->CR &= (uint32_t)0xFEF6FFFF;
就是将HSE设置为OFF,CSSON和PLL叶设置为OFF。
接着是 复位RCC_PLLCFGR寄存器
RCC->PLLCFGR = 0x24003010;
高4位保留,将PLLQ=4;将PLL和PLLI2S的时钟源设置为HSI;
设置PLLP=2,即系统时钟为主PLL输出时钟的2分频;设置PLLN=192;设置PLLM=16;
然后是 RCC->CR &= (uint32_t)0xFFFBFFFF;
就是将HSEBYP位清零,即HSE时钟不被旁路了。
然后是 RCC->CIR = 0x00000000;
Disable所有中断。
然后函数里调用了另一个函数 SetSysClock();
开始是
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
将RCC_CR寄存器的第16位置1了,也就是Enable HSE;
然后在一个do while循环里不断读取HSEDRY的状态,当HSE时钟已经ready好,该位置1时,就会跳出循环。
(时钟启动超时也会跳出循环。这个超时量被设置为0x05000,每次循环就会自增,超过这个值之前,HSERDY还没置1,也会退出循环。)
退出循环后判断HSERDY的状态,ready则为1状态,否则为0;
当状态为1时,开始
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
打开APB1时钟的电源。
接着是
PWR->CR |= PWR_CR_VOS;
看PWR->CR这个寄存器,这是设置性能与功耗的平衡的,不太懂。
接着是
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
HCLK不分频。
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
PCLK2 = HCLK/2;
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
PCLK1 = HCLK/4;
接着是
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
配置主PLL的参数,这个PLL_M、PLL_P、PLL_N、PLL_M等都是一个宏定义,在函数用一个文件的开头处,可以设置。
计算公式:
SYSCLK = PLL_VCO / PLL_P
PLL_VCP = (HSE_VALUE / PLL_M) * PLL_N
例如,外部晶振HSE_VALUE = 25MHz,PLL_M =25,PLL_N = 336,PLL_P=2;则系统时钟为168Mhz。
HCLK不分频为168MHZ,PCLK2=84MHZ,PCLK1=42MHZ。
接着
RCC->CR |= RCC_CR_PLLON;
Enable the main PLL;
然后一直循环等待PLLRDY置1。
然后有个FLASH存取控制寄存器的设置,懒得看了,跳过。
最后 RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
就是将SYSCLK时钟源切换成主PLL的输出。
最后一个while循环等待,切换成功后退出循环。
------------------------------------------------------------------------------------------------------
到这里,时钟已经配置完成。
SYSCLK,HCLK,PCLK2,PCLK1等时钟都设置完成,当然还有很多USB I2S之类的我还没有关注,用到再搞吧。
----------------------------------------------------------------------------------------------------
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |