STM32:RCC
1 STM32F1的复位方式
1.1 系统复位
作用:将RCC_CSR的复位标志、备份区域除外的所有reg值复位为复位值;
备份区域:LSE后备寄存器、RCC_BDCR、RTC的相关寄存器;
如果使用备用电池Vbat,那么VDD断电后,备份区域会使用Vbat备份;
1.1.1 NRST引脚上的低电平(外部复位)
1.1.2 窗口看门狗计数终止(WWDG复位)
1.1.3 独立看门狗计数终止(IWDG复位)
1.1.4 低功耗管理复位
1.1.5 软件复位(SW复位)
配置CM3内核中的"中断应用和复位控制寄存器"中的SYSRESETREQ位置’1’,可实现软件复位;
1.2 电源复位
1.2.1 芯片上电/掉电复位;
1.2.2 从待机模式返回;
1.3 备份域复位
1.3.1 只针对备份区域的备份域复位,详见参考手册章节7.1
2 RCC
RCC全称 reset clock controller 复位和时钟控制器;
作用是控制芯片的复位信号,内核及外设的时钟;由于芯片的外设时钟较多,参考手册对其进行了树形图整理;
2.1 寄存器
rcc_CR控制寄存器: 配置HSE,HSI,PLL的使能,复位默认使能HSI时钟;
rcc_CFGR时钟配置寄存器: 配置HSE,HSI,PLL的参数,搭配时钟树使用清晰易懂,时钟树已标注出;
rcc_xxx外设复位寄存器: 复位对应的外设寄存器为复位值;
rcc_xxx外设时钟使能寄存器:使能对应的外设时钟,每个外设都有一个独立的时钟使能bit,外设使用前需要使能时钟;
rcc_CIR时钟中断寄存器: 目测不相关,懒得写;
rcc_CSR控制状态寄存器: 目测不相关,懒得写;
rcc_BDSR备份域控制寄存器:目测用不上,懒得写;
2.2 时钟树
2.2.1 晶振时钟源
HSI:8MHz的内部低速晶振时钟,可作为系统时钟源、PLL时钟源;
HSE:4-16MHz的外部高速晶振时钟,可作为系统时钟源、PLL时钟源,f1战舰版的HSE为8MHz;
LSI :约40kHz的内部低速低功耗晶振时钟,为IWDG独立看门狗时钟源、待机停机模式下的自动唤醒单元时钟源;
LSE:32.768kHz的外部低速低功耗晶振时钟,为RTC实时时钟源;
以系统时钟sysclk举例可知,sysclk时钟可以通过HSI,HSE或PLLCLK生成;
PLLCLK由HSI或HSE分频倍频而来;如果HSI输入pll作为系统时钟源,那么sysclk最大64MHz;2分频后倍频max16倍可不就是64M嘛;
2.2 AHB,APB1,APB2外设连接;
下图来自STM32F103ZET6数据手册,为器件功能概览,挂在总线上的外设部分作为时钟树外设的补充;
使用外设不仅需要使能该外设时钟,还要使能外设所在IO的IO_port时钟;每个外设时钟源都可以单独开关来优化功耗;
3 标准库函数
void RCC_DeInit(void);//复位CR,CFGR,CIR; void RCC_HSEConfig(uint32_t RCC_HSE);//CR_HSEON,CR_HSEBYP; ErrorStatus RCC_WaitForHSEStartUp(void);//CR_HSERDY; void RCC_AdjustHSICalibrationValue(uint8_t HSICalibrationValue); void RCC_HSICmd(FunctionalState NewState);//位带操作CR_HSION; void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul); void RCC_PLLCmd(FunctionalState NewState);//位带操作CR_PLLON; //顾名思义,懒得看了; void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource); uint8_t RCC_GetSYSCLKSource(void); void RCC_HCLKConfig(uint32_t RCC_SYSCLK); void RCC_PCLK1Config(uint32_t RCC_HCLK); void RCC_PCLK2Config(uint32_t RCC_HCLK); void RCC_ITConfig(uint8_t RCC_IT, FunctionalState NewState); void RCC_USBCLKConfig(uint32_t RCC_USBCLKSource); void RCC_ADCCLKConfig(uint32_t RCC_PCLK2); void RCC_LSEConfig(uint8_t RCC_LSE); void RCC_LSICmd(FunctionalState NewState); void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource); void RCC_RTCCLKCmd(FunctionalState NewState); void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks); void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState); void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState); void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState); void RCC_BackupResetCmd(FunctionalState NewState); void RCC_ClockSecuritySystemCmd(FunctionalState NewState); void RCC_MCOConfig(uint8_t RCC_MCO); FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG); void RCC_ClearFlag(void); ITStatus RCC_GetITStatus(uint8_t RCC_IT); void RCC_ClearITPendingBit(uint8_t RCC_IT);
/***以下HSI,PLL的配置,使用的是bit-band位带操作; ***位带操作:将目标寄存器的特定bit映射到bit-band位带区域的特定地址; *************对位带地址的数据操作映射到目标寄存器的对应位数据; *******公式:STM32_GPIO_3.3; ***/
RCC_HSICmd(FunctionalState NewState)
/***以下HSI的配置,使用的是bit-band位带操作;***/ #define HSION_BitNumber 0x00 #define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */ #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) #define RCC_BASE (AHBPERIPH_BASE + 0x1000) #define RCC_OFFSET (RCC_BASE - PERIPH_BASE) #define CR_OFFSET (RCC_OFFSET + 0x00) #define PERIPH_BB_BASE ((uint32_t)0x42000000) /*!< Peripheral base address in the bit-band region */ #define CR_HSION_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (HSION_BitNumber * 4)) /** * @brief Enables or disables the Internal High Speed oscillator (HSI). * @note HSI can not be stopped if it is used directly or through the PLL as system clock. * @param NewState: new state of the HSI. This parameter can be: ENABLE or DISABLE. * @retval None */ void RCC_HSICmd(FunctionalState NewState) { /* Check the parameters */ assert_param(IS_FUNCTIONAL_STATE(NewState)); *(__IO uint32_t *) CR_HSION_BB = (uint32_t)NewState; }
RCC_PLLCmd(FunctionalState NewState)
/***PLLON_BitNumber为什么是0x18,怎么对应上的不理解;这个问题很简单,结合“STM32:GPIO”3.3小结解析就可以了;***/ /* Alias word address of PLLON bit */ #define PLLON_BitNumber 0x18 #define CR_PLLON_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (PLLON_BitNumber * 4)) void RCC_PLLCmd(FunctionalState NewState) { /* Check the parameters */ assert_param(IS_FUNCTIONAL_STATE(NewState)); *(__IO uint32_t *) CR_PLLON_BB = (uint32_t)NewState; }
4 rcc默认配置
在启动文件中,Reset_Handler中断调用了__main函数来对软件进行初始化,__mian函数具体执行了什么主要是mdk的事,内容太长我就不看了;
但是__main函数调用了SystemInit()函数来配置程序rcc的初始化,这个很重要,所以这里我们来看一下SystemInit()函数;
rcc一开始首先使用HSI时钟,然后修改成HSE时钟通过pll倍频9倍作为sysclk时钟;f1战舰版外部晶振8M;
//system_stm32f10x.c STM32F10X_HD的SystemInit()实际只配置了下面5个中文注释部分,然后调用SetSysClock(); void SystemInit (void) { RCC->CR |= (uint32_t)0x00000001; //使能HSI时钟; #ifndef STM32F10X_CL RCC->CFGR &= (uint32_t)0xF8FF0000; //sysclk选HSI,AHB,APB1,APB2不分频;ADC2分频; #else RCC->CFGR &= (uint32_t)0xF0FF0000; #endif /* STM32F10X_CL */ RCC->CR &= (uint32_t)0xFEF6FFFF; //HSE,PLL,CSS时钟安全系统不使能; RCC->CR &= (uint32_t)0xFFFBFFFF; //[18]HSEEBYP正常接线; RCC->CFGR &= (uint32_t)0xFF80FFFF; //[22:16]与0,配置了PLLCLK,可是没使能PLL时钟哟; #ifdef STM32F10X_CL RCC->CR &= (uint32_t)0xEBFFFFFF; RCC->CIR = 0x00FF0000; RCC->CFGR2 = 0x00000000; #elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) RCC->CIR = 0x009F0000; RCC->CFGR2 = 0x00000000; #else RCC->CIR = 0x009F0000; //Disable all interrupts and clear pending bits #endif /* STM32F10X_CL */ #if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL) #ifdef DATA_IN_ExtSRAM SystemInit_ExtMemCtl(); #endif /* DATA_IN_ExtSRAM */ #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; /* Vector Table Relocation in Internal SRAM. */ #else SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */ #endif }
//system_stm32f10x.c 这个函数下面的宏只定义了SYSCLK_FREQ_72MHz,所以调用SetSysClockTo72(); 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(); #endif
#elif defined SYSCLK_FREQ_72MHz /***下面这个函数执行步骤如下3步,搭配时钟树和寄存器分析非常清晰;作为本文的巩固和联系非常适合,方便理解; 1 使能CR_HSE时钟,先配置好CFGR_[13:7]总线时钟; 2 先配置时钟树中HSE时钟到PLLCLK的CFGR_[21:16],然后使能CR_PLL时钟; 3 清0SW[1:0],然后配置为10b,配置sysclk时钟来自pllclk; ***/ static void SetSysClockTo72(void) { __IO uint32_t StartUpCounter = 0, HSEStatus = 0; /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ /* Enable HSE */ RCC->CR |= ((uint32_t)RCC_CR_HSEON);//[16]使能HSE /* Wait till HSE is ready and if Time out is reached exit */ do { HSEStatus = RCC->CR & RCC_CR_HSERDY; StartUpCounter++; } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET) { HSEStatus = (uint32_t)0x01; } else { HSEStatus = (uint32_t)0x00; } if (HSEStatus == (uint32_t)0x01) { /* 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; /* HCLK = SYSCLK [7:4]不分频 */ RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; /* PCLK2 = HCLK [13:11]不分频 */ RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; /* PCLK1 = HCLK [10:8]2分频*/ RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; #ifdef STM32F10X_CL /* Configure PLLs ------------------------------------------------------*/ /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ 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); /* Enable PLL2 */ RCC->CR |= RCC_CR_PLL2ON; /* Wait till PLL2 is ready */ while((RCC->CR & RCC_CR_PLL2RDY) == 0) { } /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ 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 //执行代码;清0[21:16],然后使能[16],[21:18]0111b倍频7+2=9倍; /* 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); #endif /* STM32F10X_CL */ /* Enable PLL [24]*/ RCC->CR |= RCC_CR_PLLON; /* Wait till PLL is ready */ while((RCC->CR & RCC_CR_PLLRDY) == 0) { } /*先清0[1:0],然后赋值10b,选sysclk时钟为pll;*/ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; /* Wait till PLL is used as system clock source */ while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) { } } else { /* If HSE fails to start-up, the application will have wrong clock configuration. User can add here some code to deal with this error */ } } #endif
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?