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