在开发版STM32F429,以HSE, HSI, PLL作为主要的系统时钟的信号来源,并拥有LSI, LSE低速内外部信号时钟源,两者频率分别为32, 32.768kHz
时钟源相当於节拍器的功能,藉由稳定的信号源输出,可以有效配置出单位时间内系统的运算次数。下面探讨三个主要的系统时钟来源的功能与特色:
上图为reference manual中对设置系统时钟的描述,也就是着名的时钟树框图,由於我们的目标是配制系统时间SYSCLK,因此可以把主线任务从上图的红框部分拆分成以下的流程图,这样对接下来的程序范例也好理解
从图中我们可以发现配置系统时间过程中有两大重点
而系统时间通常是使用HSE, HSI, PLL三者之一,可以透过配置暂存器来选择,一般来说系统时间会使用PLL倍频之後的结果
由於外部时钟来源的频率不够大,开发版需要透过PLL锁向环,将输入时钟倍频成适合的系统频率。因此PLL的处理过程围绕在将时钟源切分成约1MHz後再进行放大,我们先来看看PLL系统时间的运算公式:
[(HSE/HSI)/分频因子M] * 倍频因子N / PLLCLK分频因子P
操作流程
举例来说,我们选择25MHz的外部震荡器时钟源,透过将M配置成25,把V时钟输入结果配置成1MHz,然後把N设为360,将输出结果放大成360MHz,最後设置P为2,输出180MHz的PLL系统时钟源
另外假如PLL时钟来源选择HSE,当HSE发生问题时,系统会自动将时钟源切换成HSI
AHB外设时钟HCLK
当我们选择PLL作为系统时钟来源後,首先会输出到高速外设汇流排AHB,同样的我们可以透过软件控制暂存器的AHB分频因子,分频因子可以设置为1, 2, 4, 8, 16, 64, 128, 256, 512。几乎所有周边外设都使用AHB的系统时间频率
APB2外设时钟PCLK2
APB2汇流排时钟经由AHB时钟分频得到,可以透过软件操作暂存器的APB2分频因子,分频因子可以设置为1, 2, 4, 8, 16。需要注意APB2时钟频率不可以超过90MHz
APB1外设时钟PCLK1
APB1汇流排时钟经由AHB时钟分频得到,可以透过软件操作暂存器的APB1分频因子,分频因子可以设置为1, 2, 4, 8, 16。需要注意APB1时钟频率不可以超过45MHz
在接下来的系统时钟配置环节主要涉及以下几个暂存器,详细可以参考reference manual
RCC_CR
RCC_PLLCFGR
RCC_CFGR
关於开发版初始化的主要写在函式SystemInit()
当中,系统时钟的配置当然也不例外。在SystemInit()
我们可以找到SetSysClock()
函式,我们上一小节介绍的诸如PLL时钟设定都在这个函式中完成,以下大致介绍SetSysClock()
:
static void SetSysClock(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* 开启HSE,操作RCC_CR的HSEON位*/
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* 在指定时间内等待硬体将HSERDY置位成1 */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
/* 查看HSE是否配置成功,或者只是因为timeout退出 */
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01; // HSE配置成功,将HSEStatus设为1
}
else
{
HSEStatus = (uint32_t)0x00; // HSE配置失败,将HSEStatus设为0
}
/* 若HSE配置成功,则可以开始系统时钟的处理流程 */
if (HSEStatus == (uint32_t)0x01)
{
/* 配置内部电压调节器,以达到效率与功号之间的平衡 */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
/* AHB分频因子配置成1 */
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
/* APB2分频因子配置成2 */
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
/* APB1分频因子配置成4*/
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
/* 配置RCC_PLLCFGR暂存器,我们主要关注M, N, P因子 */
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
/* 开启PLL */
RCC->CR |= RCC_CR_PLLON;
/* 等待主PLL锁向环被硬体开启 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* 开启Over-drive模式,使系统能够运行更高频率 */
PWR->CR |= PWR_CR_ODEN;
/* 等待Over-drive模式被成功开启 */
while((PWR->CSR & PWR_CSR_ODRDY) == 0)
{
}
/* 将系统切换为Over-drive模式 */
PWR->CR |= PWR_CR_ODSWEN;
/* 等待系统成功切换置Over-drive模式 */
while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
{
}
/* 配置Flash接口暂存器 */
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
/* 选择PLL作为系统时钟来源 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
/* 等待硬体切换系统时钟来源 */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
{
}
}
else
{ /* 使用者可以自行定义若HSE启动失败要做那些处理 */
}
}
理解了SetSysClock()
函式以後我们大致能掌握配置系统时间的操作,若我们想自行编写一个改变PLL系统时钟频率的API,可以简单地抓一下程序编写重点:
还记得在介绍倍频因子N的时候有提到STM32F42xxx, STM32F43xxx系列开发版可以将N设置到最大值432MHz吗?实际上官方参考手册虽建议最大频率为180MHz,不过还是预留空间供超频使用,因此我们希望透过自定义API将系统频率重新设置成216MHz
SetSysClock()
RCC_DeInit(void)
RCC_HSEConfig(uint8_t RCC_HSE)
RCC_WaitForHSEStartUp(void)
RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t PLLM, uint32_t PLLN, uint32_t PLLP, uint32_t PLLQ)
RCC_HCLKConfig(uint32_t RCC_SYSCLK)
RCC_PCLK2Config(uint32_t RCC_HCLK)
RCC_PCLK1Config(uint32_t RCC_HCLK)
RCC_PLLCmd(FunctionalState NewState)
RCC_GetFlagStatus(uint8_t RCC_FLAG)
RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource)
void My_Delay(__IO uint32_t count){
for(;count > 0; count--);
}
/**
* vco range: 192~432Mhz
* Re-set system clock by myself, the maximun frequency is 216Mhz
* @param m Division factor
* @param n Multiplication factor
* @param p Division factor for main system clock
* @param q Division factor for USB OTG FS, SDIO, etc.
* @retval None
*/
void HSE_SetSysCLK(uint32_t m, uint32_t n, uint32_t p, uint32_t q){
RCC_DeInit(); // 将RCC重置到预设模式
RCC_HSEConfig(SET_ON); // 开启HSE
/* 等待HSE启动成功 */
while(!RCC_WaitForHSEStartUp()){
My_Delay(TIMES);
RCC_HSEConfig(SET_ON);
}
/* 配置PLL分倍频因子*/
RCC_PLLConfig(HSE,m,n,p,q);
/* 配置外设时钟分频因子*/
RCC_HCLKConfig(AHB_CFG); // AHB
RCC_PCLK2Config(APB2_CFG); // APB2
RCC_PCLK1Config(APB1_CFG); // APB1
/* 启动PLL */
RCC_PLLCmd(ENABLE);
/* 等待PLL启动成功 */
while(!RCC_GetFlagStatus(RCC_FLAG_PLLRDY)){}
/* 配置PLL为系统时间 */
RCC_SYSCLKConfig(PLLCLK);
}
然後我们只需要在main函式中初始化该API就可以调整系统时钟
#include "bsp_rccclkconfig.h"
#include "bsp_led.h"
int main(void)
{
HSE_SetSysCLK(SYSCLK_M,SYSCLK_N,SYSCLK_P,SYSCLK_Q);
App_Init();
/* Infinite loop */
while (1)
{
App_Thread();
}
}
相关的marco定义如下所示:
#ifndef __RCCCLKCONFIG_H_
#define __RCCCLKCONFIG_H_
#include "stm32f4xx.h"
#define RCC_TEST 0
#define SET_ON 1
#define TIMES 100
#define HSE 1
#define AHB_CFG 1
#define APB2_CFG 4
#define APB1_CFG 5
#define PLLCLK 2
#define PLLM 25
#define PLLQ 9
#define PLLN 432
#define PLLP 0
#define SYSCLK_M PLLM
#define SYSCLK_Q PLLQ
#define SYSCLK_N PLLN
#define SYSCLK_P PLLP
extern void HSE_SetSysCLK(uint32_t m, uint32_t n, uint32_t p, uint32_t q);
#endif /*__RCCCLKCONFIG_H_*/
>>: Jupyter Notebook 输入栏位设计(1)
在引用资料来源的时候,除了上传csv的选项,另外一个就是BigQuery。 早在开始摸索ML之前,G...
蒙特兰在过去曾经讲过,在别人藐视的事中获得成功,是一件了不起的事,因为它证明不但战胜了自己,也战胜了...
1.前言 首先,祝各位中秋佳节愉快~(明天又要继续上班上课了),不知道各位小夥伴连假期间是否在某些方...
藉着 Day 14 建一个 Node.js 容器 所建立的基底,来制作一个 Image 并上传到 D...
**范围界定(Scoping)**是指检查基准安全控制并仅选择适用於您要保护的IT系统的那些控制。例...