stm32.cube(五)——HAL.RCC
2018-05-01 来源:eefocus
一、RCC特性
1.1 HIS内部高速时钟
HIS 时钟信号通过一个 8MHz 的 RC 振荡器产生,上电复位时,被自动选做系统时钟。但由于HIS的稳定性较差,受温度、电压等环境参数影响较大,一般只作为备用时钟使用。在芯片初始化的startup.s里,通常会调用一个c编写的函数system_init(),里面会将系统时钟源从HIS设置成HSE。
1.2 HSE外部高速时钟
由晶振或者外部时钟源提供的时钟,较为稳定。
1.3 PLL
PLL可用于将 HIS RC 振荡器的输出时钟频率倍频,具体的原理要参考锁相环电路的相关知识。
1.4 LSE外部低速时钟
LSE 振荡器是一个 32.768kHz 的低速外部晶体或者陶瓷共振器,它用于给RTC功能提供实时时钟。
1.5 LSI内部低速时钟
LSI RC 振荡器作为一个低功耗时钟源,它位看门狗和AWU提供时钟。
1.6 时钟源检测
当HSE、LSE、LSI等时钟被选择启用时,在时钟寄存器里有相应的标志它们是否准备好的位,只有当检测通过时,时钟源才会启动。
1.7 时钟安全系统CSS
CSS一旦启动,会检测外部高速时钟是否安全。只要HSE在上检测到一个失效,HSE就会被禁用。
1.8 RTC时钟
RTCCLK 时钟源可以是 HSE/128,LSE 或者 LSI 时钟。不同的时钟源,在不同的电压源供应下,状态会不同。
1.9 看门狗时钟
如果看门狗被启动了,LSI会被强制打开。
1.10 MCO时钟输出性能
HSI、HSE、PLL、SYSCLK都可以被作为时钟输出,从MCO引脚输出。
1.11 外设时钟
AHB的时钟由系统时钟得到,APB1和APB2的时钟由AHB的时钟得到。
PCLK1——外设时钟,由APB1预分频器输出得到,最大频率为36MHz,提供给挂载在APB1总线上的外设,APB1总线上的外设如下:
RCC_APB1Periph_TIM2 TIM2时钟
RCC_APB1Periph_TIM3 TIM3时钟
RCC_APB1Periph_TIM4 TIM4时钟
RCC_APB1Periph_WWDG WWDG时钟
RCC_APB1Periph_SPI2 SPI2时钟
RCC_APB1Periph_USART2 USART2时钟
RCC_APB1Periph_USART3 USART3时钟
RCC_APB1Periph_I2C1 I2C1时钟
RCC_APB1Periph_I2C2 I2C2时钟
RCC_APB1Periph_USB USB时钟
RCC_APB1Periph_CAN CAN时钟
RCC_APB1Periph_BKP BKP时钟
RCC_APB1Periph_PWR PWR时钟
RCC_APB1Periph_ALL 全部APB1外设时钟
PCLK2——外设时钟,由APB2预分频器输出得到,最大频率可为72MHz,提供给挂载在APB2总线上的外设,APB2总线上的外设如下:
RCC_APB2Periph_AFIO 功能复用IO时钟
RCC_APB2Periph_GPIOA GPIOA时钟
RCC_APB2Periph_GPIOB GPIOB时钟
RCC_APB2Periph_GPIOC GPIOC时钟
RCC_APB2Periph_GPIOD GPIOD时钟
RCC_APB2Periph_GPIOE GPIOE时钟
RCC_APB2Periph_ADC1 ADC1时钟
RCC_APB2Periph_ADC2 ADC2时钟
RCC_APB2Periph_TIM1 TIM1时钟
RCC_APB2Periph_SPI1 SPI1时钟
RCC_APB2Periph_USART1 USART1时钟
RCC_APB2Periph_ALL 全部APB2外设时钟
1.12 时钟中断
当某些时钟被检测到准备完毕时,系统会产生中断。当CSS检测到HSE错误时,也同样会产生中断。
1.13 小结
注:此图出处不明,所以没有办法注明全作者。
二、RCC源文件结构
从系统上电到HSE的启动,这些时钟操作都由startup函数来执行,此过程与HAL的RCC模块无关,不做叙述。
要了解HAL里RCC模块的结构,其实是要回答这么几个问题。面对较为复杂的时钟系统,在输出接口内容已知的提前下,HAL是如何实现它的功能的?它建立了哪些结构体(或单个变量)用以标记设置和状态?它使用哪些函数来支持对RCC的各种操作?整个源文件里代码的逻辑结构是怎样的?
假设我们自己需要写一个RCC通用模块,第一步就是将整个模块根据可以被区分的属性进行分层。
从上面的图可以看出,整个RCC从逻辑上可以分成3层:
最上层的是5个时钟源,HSE、HIS、PLL、LSE和HIS。CSS和MCO直接依赖与这五个时钟源,也可以被划在这一层。
中间层是系统时钟SYSCLK和外设总线时钟APB。
最底层是外设时钟。
对于第一层,RCC模块提供以下函数:
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency);
RCC_ClkInitStruct结构体里包含5个时钟源的设置参数,开启或者关闭、HSE的预分频参数、HIS的校准量。如果不想一次性设置所有的时钟源,对单个时钟源的操作可以通过许多HAL提供的宏定义来进行。
MCO设置函数:
void HAL_RCC_MCOConfig(uint32_t RCC_MCOx, uint32_t RCC_MCOSource, uint32_t RCC_MCODiv)
CSS设置函数和CSS中断处理函数:
void HAL_RCC_EnableCSS(void);
void HAL_RCC_DisableCSS(void);
void HAL_RCC_NMI_IRQHandler(void);
/* User Callbacks in non blocking mode (IT mode) */
void
第二层
HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);
1
RCC_OscInitStruct结构体里包含的信息是系统时钟源的选择、APB1和APB2的分频量。
第三层
对于每个外设时钟,HAL都有两个宏来开关它的时钟。 HAL_RCC_CSSCallback(void);
#define __HAL_RCC_I2C1_CLK_ENABLE() do { \
__IO uint32_t tmpreg; \
SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C1EN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C1EN);\
UNUSED(tmpreg); \
} while(0)
#define __HAL_RCC_BKP_CLK_ENABLE() do { \
__IO uint32_t tmpreg; \
SET_BIT(RCC->APB1ENR, RCC_APB1ENR_BKPEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_BKPEN);\
UNUSED(tmpreg); \
} while(0)
除了设置以外,HAL提供了一系列的Get函数用以获得当前时钟设置。
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)
对于RCC模块的结构梳理完毕,从中可以看到HAL在结构上较之与Stmlib的简化。它把复杂度更多的放到了函数里。牺牲了一点执行效率,增强了代码的可读性和可维护性。