×
单片机 > 单片机程序设计 > 详情

stm32.cube(五)——HAL.RCC

发布时间:2020-08-28 发布时间:
|

一、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的简化。它把复杂度更多的放到了函数里。牺牲了一点执行效率,增强了代码的可读性和可维护性。




『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
Keil(MDK-ARM)系列教程(七)_菜单