STM32学习系列(一)— 时钟
2022-04-23 15:20
该系列是为了记录自己的学习过程,如有错误和不足,欢迎指出,感谢!
STM32时钟部分
一、简介
时钟就如人体的心脏,管理着全身血液的流速(类比),因此想要驱动STM32F103,时钟的初始化很重要。
这一节就是为了理清STM32F103的时钟源和时钟配置。使用HAL库的方式,进行实现,原因:
- 官方已经包装好函数,调用就可以
- 学习阶段,需要控制一下变量,第一次学习,了解有哪些资源,怎么用,如何修改成自己想要的效果,个人觉得这样比较合理。
- 寄存器的要求比较高,需要对C语言和初始化的配置有一定的了解
- 库函数介于寄存器和HAL库中间,且官方更新的态度已经降低,偏向于HAL库和LL库。
二、时钟源
2.1 时钟树
![[Pasted image 20220423154642.png]
这是STM32F103ZE的时钟树(来着韦东山学习手册)
可以看出时钟的来源可以是四个部分,四选一。
- HSI:高速内部时钟,8MHz,内部集成,但精度不足。
- HSE:高速外部时钟,4MHz~16MHz【主要使用的】
- LSI:低速内部时钟,32.768KHz
- LSE:低速外部时钟,40KHz
时钟源简称如何解读:
包括三个部分: H/L 表示高速或者低速
S 表示速度(speed)
I/E 表示内部或者外部
2.2 在HAL库中的表示
主函数
int main(void)
{
HAL_Init(); //初始化HAL库,调用HAL库必须有
Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M
while(1)
{
}
}
Stm32_Clock_Init( )函数
void Stm32_Clock_Init(u32 PLL)
{
HAL_StatusTypeDef ret = HAL_OK;
RCC_OscInitTypeDef RCC_OscInitStructure;
RCC_ClkInitTypeDef RCC_ClkInitStructure;
RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE; //时钟源为HSE
RCC_OscInitStructure.HSEState=RCC_HSE_ON; //打开HSE
RCC_OscInitStructure.HSEPredivValue=RCC_HSE_PREDIV_DIV1; //HSE预分频
RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON; //打开PLL
RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE; //PLL时钟源选择HSE
RCC_OscInitStructure.PLL.PLLMUL=PLL; //传入的参数 //主PLL倍频因子
ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化
if(ret!=HAL_OK) while(1);
//选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2
RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK; //设置系统时钟时钟源为PLL
RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1; //AHB分频系数为1
RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV2; //APB1分频系数为2
RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV1; //APB2分频系数为1
ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_2); //同时设置FLASH延时周期为2WS,也就是3个CPU周期。
if(ret!=HAL_OK) while(1);
}
我们可以看出Stm32_Clock_Init中其实是初始化了两个部分。
第一部分:RCC_OscInitTypeDef, 是选择时钟源的结构体
typedef struct
{
uint32_t OscillatorType; /*选择时钟源 */
uint32_t HSEState; /*HSE的状态 */
uint32_t HSEPredivValue; /*HSE预分频 */
uint32_t LSEState; /*LSE的状态 */
uint32_t HSIState; /*HSI的状态 */
uint32_t HSICalibrationValue; /*HSI校准微调值 */
uint32_t LSIState; /*LSI的状态 */
RCC_PLLInitTypeDef PLL; /*PLL配置结构体 */
} RCC_OscInitTypeDef;
typedef struct
{
uint32_t PLLState; /*PLL状态 */
uint32_t PLLSource; /*PLL来源 */
uint32_t PLLMUL; /*主PLL倍频因子*/
} RCC_PLLInitTypeDef;
/*调用 HAL_RCC_OscConfig()进行初始化*/
第二部分:RCC_ClkInitTypeDef,具体时钟的配置结构体
typedef struct
{
uint32_t ClockType; /*选择要配置的系统时钟 */
uint32_t SYSCLKSource; /*选择系统时钟的来源 */
uint32_t AHBCLKDivider; /* AHB分频系数 */
uint32_t APB1CLKDivider; /* APB1分频系数 */
uint32_t APB2CLKDivider; /* APB2分频系数 */
} RCC_ClkInitTypeDef;
要想进行时钟配置,需要看懂时钟树。从左边的时钟源选择,通过中间部分的分频,到右边总线的时钟配置,是一个完整的流程
三、硬件平台
韦东山STMF103开发板,不需要外部资源
四、编程思维
时钟初始化的一般步骤:
- 时钟源选择
- 分频系数选择
- PLL锁相环配置
- AHB分频系数选择
- APB1分频系数选择
- APB2分频系数选择
五、代码实现
5.1 设置系统时钟为72MHz(HSE_8MHz)
void Stm32_Clock_Init(u32 PLL)
{
HAL_StatusTypeDef ret = HAL_OK;
RCC_OscInitTypeDef RCC_OscInitStructure;
RCC_ClkInitTypeDef RCC_ClkInitStructure;
RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE; //时钟源为HSE,8MHz
RCC_OscInitStructure.HSEState=RCC_HSE_ON; //打开HSE
RCC_OscInitStructure.HSEPredivValue=RCC_HSE_PREDIV_DIV1; //HSE预分频,不分频
RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON; //打开PLL
RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE; //PLL时钟源选择HSE,因为时钟源选择的是HSE
RCC_OscInitStructure.PLL.PLLMUL=PLL; //主PLL倍频因子,输入参数为9倍频,8*9=72MHz
ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化
if(ret!=HAL_OK) while(1);
//选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2
RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK; //设置系统时钟时钟源为PLL
RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1; //AHB分频系数为1 72/1=72MHz
RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV2; //APB1分频系数为2 72/2=36MHz(最大是36MHz)
RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV1; //APB2分频系数为1 72/1=72MHz
ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_2); //同时设置FLASH延时周期为2WS,也就是3个CPU周期。
if(ret!=HAL_OK) while(1);
}
5.2 设置系统时钟为36MHZ(HSI_8MHz)
void Stm32_Clock_Init(u32 PLL)
{
HAL_StatusTypeDef ret = HAL_OK;
RCC_OscInitTypeDef RCC_OscInitStructure;
RCC_ClkInitTypeDef RCC_ClkInitStructure;
RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSI; //时钟源为HSI
RCC_OscInitStructure.HSEState=RCC_HSE_OFF; //关闭HSE
RCC_OscInitStructure.HSIState=RCC_HSI_ON;//打开HEI
RCC_OscInitStructure.HSEPredivValue=RCC_HSE_PREDIV_DIV1; //HSE预分频
RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON; //打开PLL
RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSI_DIV2; //PLL时钟源选择HSI的2分频, 8/2 =4MHz
RCC_OscInitStructure.PLL.PLLMUL=PLL; //主PLL倍频因子,需要传入9分频,4*9=36MHz
ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化
if(ret!=HAL_OK) while(1);
//选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2
RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK; //设置系统时钟时钟源为PLL
RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1; //AHB分频系数为1 36/1=36MHz
RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV2; //APB1分频系数为1 36/1=36MHz
RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV1; //APB2分频系数为1 36/1=36MHz
ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_2); //同时设置FLASH延时周期为2WS,也就是3个CPU周期。
if(ret!=HAL_OK) while(1);
}
六、总结
在模板工程中,一般时钟部分是配置好的,而且我们一般是通过点灯的例程去添加我们的代码,但是如果有其他需求,例如修改系统时钟频率,那么就需要对这一部分的内容,有所了解。
学这章的一个目的:
- 能看懂时钟树
- 能在官方例程的基础上,修改到我们需要的系统频率和总线频率
- 了解HAL库配置的一般过程,参数查找和调用