记录一下,方便以后翻阅~
之前初学STM32开发板的时候,也写了几篇关于时钟系统配置的文章文章一和文章二,最近又仔细回顾了官方给的SystemInit ()函数,对其做了更好的解读。所谓读书百遍其义自见。
主要的函数:void SystemInit (void)
包含的函数:static void SetSysClock(void);static void SetSysClockTo72(void)
/**
******************************** STM32F10x *******************************************************
* @文件名称: systeminit.c解读版
* @作者名称: 闲人Ne
* @库版本号: V3.5.0
* @工程版本: V1.0.0
* @开发日期: 2020年12月15日
* @摘要简述: 本文件源于system_stm32f10x.c文件里的部分函数,不做任何修改,单独拿出来只为对其做详细解读。
****************************************************************************************************/
/************************************************************************************************
函数名称:SystemInit ()
函数功能:系统初始化函数
入口参数:无
返回参数:无
开发作者:闲人Ne
参考资料:1)RCC寄存器名称:RCC_CR (时钟控制寄存器) RCC_CFGR(时钟配置寄存器)RCC_CIR(时钟中断寄存器)
*************************************************************************************************/
void SystemInit (void)
{
// 针对RCC_CR,HSION[0]内部高速时钟使能,设1,即内部8MHz振荡器开启
RCC->CR |= (uint32_t)0x00000001;
#ifndef STM32F10X_CL
/* 针对RCC_CFGR,SW [0:1] 系统时钟切换, 设00, 即HSI作为系统时钟;
SWS [2:3] 系统时钟切换状态, 设00, 即HSI作为系统时钟;
HPRE [4:7] AHP预分频, 设0000, 即SYSCLK不分频;
PPRE1 [8:10] 低速APB预分频(APB1), 设000, 即HCLK不分频,从而决定PCLK1时钟;
PPRE2 [11:13] 高速APB预分频(APB2),设000, 即HCLK不分频,从而决定PCLK2时钟;
ADCPRE [14:15] ADC预分频, 设00, 即PCLK2 2分频后作为ADC时钟;
MOC [24:26] 微控制器时钟输出, 设000, 即没有时钟输出 */
RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
RCC->CFGR &= (uint32_t)0xF0FF0000; // 不执行,因为我用的是STM32F10X_HD
#endif
/* 针对RCC_CR, HSEON [16] 外部高速时钟使能, 设0, 即外部HSE振荡器关闭;
CSSON [19] 时钟安全系统使能, 设0, 即时钟监测器关闭;
PCCON [24] PLL使能, 设0, PCC关闭 */
RCC->CR &= (uint32_t)0xFEF6FFFF;
// 针对RCC_CR, HSEBYP [18] 外部高速时钟旁路, 设0, 即外部4-16MHz振荡器没有旁路
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* 针对RCC_CFGR, PCCSRC [16] PLL输入时钟源, 设0, 即HSI振荡器时钟经2分频后作为PLL输入时钟;
PLLXTPRE [17] HSE分频器作为PLL输入,设0, 即HSE不分频;
PLLMUL [18: 21]PLL倍频系数, 设0000,即PLL 2倍频输出;
USBPRE [22] USB预分频, 设0, 即PLL时钟1.5倍分频作为USB时钟 */
RCC->CFGR &= (uint32_t)0xFF80FFFF;
#ifdef STM32F10X_CL // 不执行,因为用的是STM32F10X_HD
RCC->CR &= (uint32_t)0xEBFFFFFF;
RCC->CIR = 0x00FF0000;
RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) // 不执行,因为用的是STM32F10X_HD
RCC->CIR = 0x009F0000;
RCC->CFGR2 = 0x00000000;
#else // 执行,因为用的是STM32F10X_HD
/* 针对RCC_CIR, LSIRDYF [0] LSI就绪中断标志, 设0, 即无内部40kHz RC振荡器产生的时钟就绪中断;
LSERDYF [1] LSE就绪中断标志, 设0, 即无外部32kHz振荡器产生的时钟就绪中断;
HSIRDYF [2] HSI就绪中断标志, 设0, 即无内部8MHz RC振荡器产生的时钟就绪中断;
HSERDYF [3] HSE就绪中断标志, 设0, 即无外部4-16MHz振荡器产生的时钟就绪中断;
PLLRDYF [4] PLL就绪中断标志, 设0, 即无PLL上锁产生的时钟就绪中断;
CSSF [7] 时钟安全系统中断标志, 设0, 即无HSE时钟失效产生的安全系统中断;
LSIRDYIE[8] LSI就绪中断使能, 设0, 即LSI就绪中断关闭;
LSERDYIE[9] LSE就绪中断使能, 设0, 即LSE就绪中断关闭;
HSIRDYIE[10] HSI就绪中断使能, 设0, 即HSI就绪中断关闭;
HSERDYIE[11] HSE就绪中断使能, 设0, 即HSE就绪中断关闭;
PLLRDYIE[12] PLL就绪中断使能, 设0, 即PLL就绪中断关闭;
LSIRDYC [16] 清除LSI就绪中断, 设1, 即清除LSI就绪中断标志位LSIRDYF;
LSERDYC [17] 清除LSE就绪中断, 设1, 清除LSE就绪中断标志位LSERDYF;
HSIRDYC [18] 清除HSI就绪中断, 设1, 清除HSI就绪中断标志位HSIRDYF;
HSERDYC [19] 清除HSE就绪中断, 设1, 清除HSE就绪中断标志位HSERDYF;
PLLRDYC [20] 清除PLL就绪中断, 设1, 清除PLL就绪中断标志位PLLRDYF */
RCC->CIR = 0x009F0000;
#endif
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl(); // 不执行
#endif
#endif
/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
/* Configure the Flash Latency cycles and enable prefetch buffer */
SetSysClock(); // 设置系统时钟函数,往下翻会有解读
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; // 不执行
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif
}
/************************************************
函数名称:SetSysClock()
函数功能:设置系统时钟函数
入口参数:无
返回参数:无
开发作者:闲人Ne
*************************************************/
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE(); // 不执行
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24(); // 不执行
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36(); // 不执行
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48(); // 不执行
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56(); // 不执行
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72(); // 执行,因为只定义了SYSCLK_FREQ_72MHz=72000000,往下翻会有解读
#endif
}
/************************************************
函数名称:SetSysClock()
函数功能:设置系统时钟函数
入口参数:无
返回参数:无
开发作者:闲人Ne
*************************************************/
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
// RCC_CR_HSEON=0x00010000,针对RCC_CR,HSEON[16]外部高速时钟使能,设1,即外部HSE振荡器开启
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
do
{
// RCC_CR_HSERDY=0x00020000,针对RCC_CR,HSERDY[17]外部高速时钟就绪标志,为1时,表示外部4-16MHz振荡器就绪
HSEStatus = RCC->CR & RCC_CR_HSERDY; // 如果RCC_CR第[17]=1,那么HSEStatus=1
StartUpCounter++;
}
// 当HSEStatus=1或StartUpCounter=0x0500时,跳出循环,while循环后面加;表示空循环
while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET) // 当RCC_CR第[17]位HSERDY=1时执行if里面的函数
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
// FLASH_ACR_PRFTBE=0x10,针对FLASH_ACR,PRFTBS[5]预取缓冲区状态,设1,即预取缓冲区开启
FLASH->ACR |= FLASH_ACR_PRFTBE;
// 第一步,FLASH_ACR_LATENCY=0x03,针对FLASH_ACR寄存器,[0:1]位先至0,[2:7]位不操作
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
// 第二步,FLASH_ACR_LATENCY_2=0x02, 针对FLASH_ACR寄存器,[0:1]位至10,[2:7]位不操作
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; //LATENCY[0:2]时延,设两个等待状态,当48MHz<SYSCLK≤72MHz
// RCC_CFGR_HPRE_DIV1=0x00000000,主要针对RCC_CFGR,HPRE[4:7]位设0000,对AHB预分频配置为SYSCLK不分频,HCLK=SYSCLK
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
// RCC_CFGR_PPRE2_DIV1=0x00000000,主要针对RCC_CFGR,PPRE2[11:13]位设000,对高速APB2配置为HCLK不分频,PCLK2=HCLK
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
// RCC_CFGR_PPRE1_DIV2=0x00000400,主要针对RCC_CFGR,PPRE1[8:10]位设100,对低速APB1配置为HCLK 2分频,PCLK1=HCLK/2
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
#ifdef STM32F10X_CL
RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);
RCC->CR |= RCC_CR_PLL2ON; // 不执行
while((RCC->CR & RCC_CR_PLL2RDY) == 0); // 不执行
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); // 不执行
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLMULL9); // 不执行
#else
/* PLL configuration: */
/* RCC_CFGR_PLLSRC=0x00010000,RCC_CFGR_PLLXTPRE=0x00020000,RCC_CFGR_PLLMULL=0x003C0000,整合取非后,[16:21]位为000000,其余为1 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
/* RCC_CFGR_PLLSRC_HSE=0x00010000,RCC_CFGR_PLLMULL9=0x001C0000
针对RCC_CFGR,[16:21]位为011101,
PLLSRC [16] PLL输入时钟源, 设1, HSE时钟作为PLL输入时钟;
PLLXTPRE [17] HSE分频器作为PLL输入, 设0, HSE不分频;
PLLMUL [18:21] PLL倍频系数, 设0111, PLL 9倍频输出,PLLCLK=HSE*9=72MHz */
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif
// RCC_CR_PLLON=0x01000000,针对RCC_CR,PLLON[24]PLL使能,PLL使能
RCC->CR |= RCC_CR_PLLON;
// RCC_CR_PLLRDY=0x02000000,即RCC_CR,PLLRDY[25]PLL时钟就绪标志,0 PLL未锁定;1 PLL锁定
while((RCC->CR & RCC_CR_PLLRDY) == 0) // 若RCC_CR的[25]位为1,则跳出循环
{
}
// RCC_CFGR_SW=0x00000003,SW[0:1]系统时钟切换,取非为00,先将RCC_CFGR的[0:1]位至00
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
// RCC_CFGR_SW_PLL=0x00000002,再将RCC_CFGR的[0:1]位至10,即PLL输出作为系统时钟
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
// RCC_CFGR_SWS=0x0000000C,当RCC_CFGR的[2:3]位为10时,跳出循环
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{
}
}
#endif
上述函数系统初始化后,时钟频率72MHz,APB1时钟频率为72MHz,APB2时钟频率36MHz,AHB时钟频率72MHz。