目录
在STM32上如果不使用外部晶振,OSC_IN和OSC_OUT的接法:
RCC
复位
复位的三种类型:系统复位、电源复位、备份域复位。
系统复位
复位时系统将所有寄存器的值重置(除了RCC_CSR寄存器和RCC_BDCR寄存器)。复位源可以通过RCC_CSR寄存器来识别。
引起系统复位的事件:
NRST引脚低电平(外部复位)
IWDG、WWDG复位
软件复位(SW复位)
低功耗管理复位
电源复位
复位时电源将所有寄存器的值重置(除了RCC_BDCR寄存器)。
引起电源复位的事件:
上电/下电复位(POR/PDR复位)
退出待机模式时
备份域复位
备份域复位只影响备份域。
引起备份域复位的事件:
软件复位,通过设置RCC_BDCR:BSRST。
如果两个电源先前都已关闭,则VDD或VBAT上电。
时钟
每个时钟源可以在使用时单独打开或关闭,以降低功耗。
下图为时钟树的总体框图。倒梯形表示选择器,长边表示多个输入,短边表示选择其中一个输出。
STM32五个时钟源:HSI、HSE、LSI、LSE、PLL。
①HSI是高速内部时钟,RC振荡器,频率为8MHz。可以直接作为系统时钟或者用作PLL时钟输入。
②HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
③LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。主要供独立看门狗和自动唤醒单元使用。
④LSE是低速外部时钟,接频率为32.768kHz的石英晶体。一般用于RTC。
⑤PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。
PLL为主PLL,PLLI2S为专用PLL。
主PLL(PLL)由HSE或者HSI提供时钟信号,并具有两个不同的输出时钟。
第一个输出PLLP用于生成高速的系统时钟(最高168MHz)
第二个输出PLLQ用于生成USB FS的时钟(48MHz),随机数发生器的时钟和SDIO时钟。
专用PLL(PLLI2S)用于生成精确时钟,从而在I2S接口实现高品质音频性能。
HSE时钟
来源:
HSE外部晶振/陶瓷谐振器(有源晶振)
HSE用户外部时钟(无源晶振,4-16M,通常使用8M无源晶振)
控制:
RCC_CR:HSEON控制
当使用到无源晶振的时候,一定要使用到起振电容,起振需要一定的时间,所以需要RCC_CR:HSERDY = 1时表示外部4-16Mhz振荡器就绪。
如果使用到有源晶振,OSC_OUT悬空,OSC_IN进入到我们的芯片。
HSI时钟
来源:芯片内部,大小为8M,当HSE故障时,系统时钟会自动切换到HSI,直到HSE启动成功。
控制:RCC_CR时钟控制寄存器的位0:HSION控制。
PLLCLK
来源:(HSI/2、HSE)经过倍频所得。
控制:RCC_CFGR:PLLXTPRE、PLLMUL和PLLSRC。
注意:PLL时钟源头使用HSI/2的时候,PLLMUL最大只能是16,这个时候PLLCLK最大只能是64M,小于ST官方推荐的最大时钟72M。并且根据温度和环境的情况,HSI的频率会有偏差,一般不作为PLL的时钟来源。
系统时钟
锁相环时钟:SYSCLK,最高为72M(ST官方推荐的)
来源:HSI、HSE、PLLCLK。
控制:RCC_CFGR:SW。
注意:通常的配置是SYSCLK=PLLCLK=72M。一般配置RCC_CFGR:SW,然后判断RCC_CFGR:SWS。如:配置RCC_CFGR:SW = 10,然后判断RCC_CFGR:SWS == 10?若是,则切换成功。
HCLK时钟
HCLK:AHB高速总线时钟,速度最高为72M。为AHB总线的外设提供时钟、为Cortex系统定时器提供时钟(SysTick)、为内核提供时钟(FCLK)。
来源:系统时钟分频得到,一般设置HCLK=SYSCLK=72M。
控制:RCC_CFGR:HPRE。
PCLK1时钟
PCLK1:APB1低速总线时钟,最高为36M。为APB1总线的外设提供时钟。2倍频之后则为APB1总线的定时器2-7提供时钟,最大为72M。
来源:HCLK分频得到,一般设置PCLK1=HCLK/2=36M。
控制:RCC_CFGR:PPRE1。
PCLK2时钟
PCLK2:APB2高速总线时钟,最高为72M。为APB2总线的外设提供时钟。不分频则为APB2总线的定时器1和8提供时钟,最大为72M。
来源:HCLK分频得到,一般设置PCLK2=HCLK=72M。
控制:RCC_CFGR:PPRE2。
RTC时钟
RTC时钟:为芯片内部的RTC外设提供时钟。
来源:HSE_RTC(HSE分频得到)、LSE(外部32.768kHz的晶体提供)、LSI(32kHz)。
控制:RCC_BDCR:RTCSEL。
独立看门狗时钟
IWDGCLK:由LSI提供。
MCO输出时钟
MCO:微控制器时钟输出引脚,由PA8复用所得。主要作用是可以对外提供时钟,相当于一个有源晶振。
来源:PLLCLK/2,HSE、HSI、SYSCLK。
控制:RCC_CFGR:MCO。
在STM32上如果不使用外部晶振,OSC_IN和OSC_OUT的接法:
如果使用内部RC振荡器而不使用外部晶振,请按照下面方法处理:
①对于100脚或144脚的产品,OSC_IN应接地,OSC_OUT应悬空。
②对于少于100脚的产品,有2种接法:
第1种:OSC_IN和OSC_OUT分别通过10K电阻接地。此方法可提高EMC性能;
第2种:分别重映射OSC_IN和OSC_OUT至PD0和PD1,再配置PD0和PD1为推挽输出并输出'0'。此方法可以减小功耗并(相对上面)节省2个外部电阻。
系统时钟配置
配置步骤
01、复位RCC RCC_DeInit;
02、打开外部高速时钟晶振HSE RCC_HSEConfig(RCC_HSE_ON);
03、等待外部高速时钟晶振工作 HSEStartUpStatus = RCC_WaitForHSEStartUp();
04、设置AHB时钟 RCC_HCLKConfig;
05、设置APB2时钟 RCC_PCLK2Config;
06、设置APB1时钟 RCC_PCLK1Config;
07、设置PLL RCC_PLLConfig;
08、使能PLL RCC_PLLCmd(ENABLE);
09、等待PLL工作 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
10、设置系统时钟 RCC_SYSCLKConfig;
11、判断PLL是否是系统时钟 while(RCC_GetSYSCLKSource() != 0x08)
12、打开要使用的外设时钟 RCC_APB2PeriphClockCmd()/RCC_APB1PeriphClockCmd()
对system_stm32f10x.c的static void SetSysClockTo72(void)进行解析。
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);
/*等待HSE准备就绪,如果超时,则退出*/
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;
}
//如果HSE启动成功,则程序继续往下执行
if (HSEStatus == (uint32_t)0x01)
{
/* 使能预取缓冲区 */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* 时延,等待2个延迟周期 */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
/* PLL配置: 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);
/* PLL使能 */
RCC->CR |= RCC_CR_PLLON;
/* 等待PLL稳定 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* 选择PLL作为系统时钟源 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* 等待PLLCLK切换成系统时钟*/
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{ /*如果HSE启动失败,应用程序将会有错误的时钟配置。用户可以在这里添加一些代码来处理这个错误 */
}
}
然后仿照system_stm32f10x.c的static void SetSysClockTo72(void),根据固件库写一个void HSE_SetSysClk(uint32_t RCC_PLLMul_x)。
void HSE_SetSysClk(uint32_t RCC_PLLMul_x)
{
ErrorStatus HSEStatus;
//把RCC寄存器复位成复位值
RCC_DeInit();
//使能 HSE 时钟
RCC_HSEConfig(RCC_HSE_ON);
HSEStatus = RCC_WaitForHSEStartUp();
if(HSEStatus == SUCCESS)
{
//使能预取指和等待2个延迟周期
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2);
//配置APH、APB1、APB2时钟
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
//配置PLLCLK = HSE * RCC_PLLMul_x
RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_x);
//使能PLL
RCC_PLLCmd(ENABLE);
//等待PLL时钟稳定
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
//选择系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//等待PLLCLK切换为系统时钟
while(RCC_GetSYSCLKSource() != 0x08);
}
else
{
//如果HSE启动失败,用户可以在这里添加处理错误的代码
}
}
类似,再写一个void HSI_SetSysClk(uint32_t RCC_PLLMul_x)。
void HSI_SetSysClk(uint32_t RCC_PLLMul_x)
{
__IO uint32_t HSIStatus = 0;
//把RCC寄存器复位成复位值
RCC_DeInit();
//使能 HSE 时钟
RCC_HSICmd(ENABLE);
HSIStatus = RCC->CR & RCC_CR_HSIRDY;
if(HSIStatus == RCC_CR_HSIRDY)
{
//使能预取指和等待2个延迟周期
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2);
//配置APH、APB1、APB2时钟
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
//配置PLLCLK = HSE * RCC_PLLMul_x
RCC_PLLConfig(RCC_PLLSource_HSI_Div2,RCC_PLLMul_x);
//使能PLL
RCC_PLLCmd(ENABLE);
//等待PLL时钟稳定
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
//选择系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//等待PLLCLK切换为系统时钟
while(RCC_GetSYSCLKSource() != 0x08);
}
else
{
//如果HSI启动失败,用户可以在这里添加处理错误的代码
}
}
对于配置时钟函数,有以下步骤:
1)复位RCC
2)使能HSE时钟,等待HSE使能完成
3)使能预取指和进行时延等待
4)配置AHB、APB1和APB2
5)配置PLL
6)使能PLL,等待PLL使能完成
7)对系统时钟进行选择,等待选择完成
HAL库_RCC
SystemClock_Config()函数
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
HAL_RCC_ClockConfig()函数
/**
* @brief Initializes the CPU, AHB and APB buses clocks according to the specified
* parameters in the RCC_ClkInitStruct.
* @param RCC_ClkInitStruct pointer to an RCC_OscInitTypeDef structure that
* contains the configuration information for the RCC peripheral.
* @param FLatency FLASH Latency
* The value of this parameter depend on device used within the same series
* @note The SystemCoreClock CMSIS variable is used to store System Clock Frequency
* and updated by @ref HAL_RCC_GetHCLKFreq() function called within this function
*
* @note The HSI is used (enabled by hardware) as system clock source after
* start-up from Reset, wake-up from STOP and STANDBY mode, or in case
* of failure of the HSE used directly or indirectly as system clock
* (if the Clock Security System CSS is enabled).
*
* @note A switch from one clock source to another occurs only if the target
* clock source is ready (clock stable after start-up delay or PLL locked).
* If a clock source which is not yet ready is selected, the switch will
* occur when the clock source will be ready.
* You can use @ref HAL_RCC_GetClockConfig() function to know which clock is
* currently used as system clock source.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)
{
uint32_t tickstart;
/* Check Null pointer */
if (RCC_ClkInitStruct == NULL)
{
return HAL_ERROR;
}
/* Check the parameters */
assert_param(IS_RCC_CLOCKTYPE(RCC_ClkInitStruct->ClockType));
assert_param(IS_FLASH_LATENCY(FLatency));
/* To correctly read data from FLASH memory, the number of wait states (LATENCY)
must be correctly programmed according to the frequency of the CPU clock
(HCLK) of the device. */
#if defined(FLASH_ACR_LATENCY)
/* Increasing the number of wait states because of higher CPU frequency */
if (FLatency > __HAL_FLASH_GET_LATENCY())
{
/* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */
__HAL_FLASH_SET_LATENCY(FLatency);
/* Check that the new number of wait states is taken into account to access the Flash
memory by reading the FLASH_ACR register */
if (__HAL_FLASH_GET_LATENCY() != FLatency)
{
return HAL_ERROR;
}
}
#endif /* FLASH_ACR_LATENCY */
/*-------------------------- HCLK Configuration --------------------------*/
if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK)
{
/* Set the highest APBx dividers in order to ensure that we do not go through
a non-spec phase whatever we decrease or increase HCLK. */
if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
{
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_HCLK_DIV16);
}
if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
{
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, (RCC_HCLK_DIV16 << 3));
}
/* Set the new HCLK clock divider */
assert_param(IS_RCC_HCLK(RCC_ClkInitStruct->AHBCLKDivider));
MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);
}
/*------------------------- SYSCLK Configuration ---------------------------*/
if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_SYSCLK) == RCC_CLOCKTYPE_SYSCLK)
{
assert_param(IS_RCC_SYSCLKSOURCE(RCC_ClkInitStruct->SYSCLKSource));
/* HSE is selected as System Clock Source */
if (RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_HSE)
{
/* Check the HSE ready flag */
if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
{
return HAL_ERROR;
}
}
/* PLL is selected as System Clock Source */
else if (RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK)
{
/* Check the PLL ready flag */
if (__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
{
return HAL_ERROR;
}
}
/* HSI is selected as System Clock Source */
else
{
/* Check the HSI ready flag */
if (__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET)
{
return HAL_ERROR;
}
}
__HAL_RCC_SYSCLK_CONFIG(RCC_ClkInitStruct->SYSCLKSource);
/* Get Start Tick */
tickstart = HAL_GetTick();
while (__HAL_RCC_GET_SYSCLK_SOURCE() != (RCC_ClkInitStruct->SYSCLKSource << RCC_CFGR_SWS_Pos))
{
if ((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}
#if defined(FLASH_ACR_LATENCY)
/* Decreasing the number of wait states because of lower CPU frequency */
if (FLatency < __HAL_FLASH_GET_LATENCY())
{
/* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */
__HAL_FLASH_SET_LATENCY(FLatency);
/* Check that the new number of wait states is taken into account to access the Flash
memory by reading the FLASH_ACR register */
if (__HAL_FLASH_GET_LATENCY() != FLatency)
{
return HAL_ERROR;
}
}
#endif /* FLASH_ACR_LATENCY */
/*-------------------------- PCLK1 Configuration ---------------------------*/
if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
{
assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB1CLKDivider));
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_ClkInitStruct->APB1CLKDivider);
}
/*-------------------------- PCLK2 Configuration ---------------------------*/
if (((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
{
assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB2CLKDivider));
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, ((RCC_ClkInitStruct->APB2CLKDivider) << 3));
}
/* Update the SystemCoreClock global variable */
SystemCoreClock = HAL_RCC_GetSysClockFreq() >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos];
/* Configure the source of time base considering new system clocks settings*/
HAL_InitTick(uwTickPrio);
return HAL_OK;
}
HAL_RCC结构体
typedef struct
{
uint32_t OscillatorType;
uint32_t HSEState;
uint32_t HSEPredivValue;
uint32_t LSEState;
uint32_t HSIState;
uint32_t HSICalibrationValue;
uint32_t LSIState;
RCC_PLLInitTypeDef PLL;
} RCC_OscInitTypeDef;
typedef struct
{
uint32_t ClockType; //RCC_CLOCKTYPE_SYSCLK、RCC_CLOCKTYPE_HCLK、RCC_CLOCKTYPE_PCLK1、RCC_CLOCKTYPE_PCLK2
uint32_t SYSCLKSource; //RCC_SYSCLKSOURCE_HSI、RCC_SYSCLKSOURCE_HSE、RCC_SYSCLKSOURCE_PLLCLK
uint32_t AHBCLKDivider; //RCC_SYSCLK_DIVx
uint32_t APB1CLKDivider; //RCC_HCLK_DIVx
uint32_t APB2CLKDivider; //RCC_HCLK_DIVx
} RCC_ClkInitTypeDef;
HAL_RCC函数
void HAL_RCC_DeInit(void);
HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency);
void HAL_RCC_MCOConfig(uint32_t RCC_MCOx, uint32_t RCC_MCOSource, uint32_t RCC_MCODiv);
void HAL_RCC_EnableCSS(void);
void HAL_RCC_DisableCSS(void);
uint32_t HAL_RCC_GetSysClockFreq(void);
uint32_t HAL_RCC_GetHCLKFreq(void);
uint32_t HAL_RCC_GetPCLK1Freq(void);
uint32_t HAL_RCC_GetPCLK2Freq(void);
void HAL_RCC_GetOscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);
void HAL_RCC_GetClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t *pFLatency);
void HAL_RCC_NMI_IRQHandler(void);
void HAL_RCC_CSSCallback(void);