目录
2.2在工程 SYSTEM 分组下面定义的 sys.c文件中的时钟初始化函数 Stm32_Clock_Init:
2.3结构体 RCC_OscInitTypeDef 的定义:
一、stm32F103时钟树
1、stm32F103时钟系统图:
1.1stm32五个时钟源
在
STM32
中,有五个时钟源,为
HSI
、
HSE
、
LSI
、
LSE
、
PLL
。从时钟频率来分可以分为
高速时钟源和低速时钟源,HIS、HSE 以及 PLL 是高速时钟,LSI 和 LSE 是低速时钟。
从来源可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式获取时
钟源,其中
HSE
和
LSE
是外部时钟源,其他的是内部时钟源。
标记:H-high S-speed I-inter E-external L-low
①
HSI
是高速内部时钟,
RC
振荡器,频率为
8MHz
。
②
HSE
是高速外部时钟,可接石英
/
陶瓷谐振器,或者接外部时钟源,频率范围
4MHz~16MHz
。
正点原子的开发板接的是
8M
的晶振。
③
LSI
是低速内部时钟,
RC
振荡器,频率为
40kHz
。独立看门狗的时钟源只能是
LSI
,同
时
LSI
还可以作为
RTC
的时钟源。
④
LSE
是低速外部时钟,接频率为
32.768kHz
的石英晶体。这个主要是
RTC
的时钟源。
⑤
PLL
为锁相环倍频输出,其时钟输入源可选择为
HSI/2
、
HSE
或者
HSE/2
。倍频可选择为
2~16
倍,但是其输出频率最大不得超过
72MHz
。
1.2A~E讲解:
A. MCO 是
STM32
的一个时钟输出
IO(PA8)
,它可以选择一个时钟信号输出,可以
选择为
PLL
输出的
2
分频、
HSI
、
HSE
、或者系统时钟。这个时钟可以用来给外
部其他系统提供时钟源。
B. 这里是 RTC
时钟源,从图上可以看出,
RTC
的时钟源可以选择
LSI
,
LSE
,以及
HSE
的
128
分频。
C. 从图中可以看出 C
处
USB
的时钟是来自
PLL
时钟源。
STM32
中有一个全速功能
的
USB
模块,其串行接口引擎需要一个频率为
48MHz
的时钟源。该时钟源只能
从
PLL
输出端获取,可以选择为
1.5
分频或者
1
分频,也就是,当需要使用
USB
模块时,
PLL
必须使能,并且时钟频率配置为
48MHz
或
72MHz
。
D. D 处就是
STM32
的系统时钟
SYSCLK
,它是供
STM32
中绝大部分部件工作的时
钟源。系统时钟可选择为
PLL
输出、
HSI
或者
HSE
。系统时钟最大频率为
72MHz
,
当然你也可以超频,不过一般情况为了系统稳定性是没有必要冒风险去超频的。
E. 这里的 E
处是指其他所有外设了。从时钟图上可以看出,其他所有外设的时钟最
终来源都是
SYSCLK
。
SYSCLK
通过
AHB
分频器分频后送给各模块使用。这些模块包
括:
①、
AHB
总线、内核、内存和
DMA
使用的
HCLK
时钟。
②、通过
8
分频后送给
Cortex
的系统定时器时钟,也就是
systick
了。
③、直接送给
Cortex
的空闲运行时钟
FCLK
。
④、送给
APB1
分频器。
APB1
分频器输出一路供
APB1
外设使用
(PCLK1
,最大
频率
36MHz)
,另一路送给定时器
(Timer)2
、
3
、
4
倍频器使用。
⑤、送给
APB2
分频器。
APB2
分频器分频输出一路供
APB2
外设使用
(PCLK2
,
最大频率
72MHz)
,另一路送给定时器
(Timer)1
倍频器使用。
需要理解的是
APB1
和
APB2
的区别,
APB1
上面连接的是低速外设,包括电源接口、
备份接口、
CAN
、
USB
、
I2C1
、
I2C2
、
UART2
、
UART3
等等,
APB2
上面连接的是高速外设包
括
UART1
、
SPI1
、
Timer1
、
ADC1
、
ADC2
、所有普通
IO
口
(PA~PE)
、第二功能
IO
口等。居宁
老师的《稀里糊涂玩
STM32
》资料里面教大家的记忆方法是
2>1
,
APB2
下面所挂的外设的时钟要比 APB1
的高。
简要概括:可以使用三种不同的时钟源来驱动系统时钟 (SYSCLK):
● HSI 振荡器时钟
● HSE 振荡器时钟
● 主 PLL (PLL) 时钟
器件具有以下两个次级时钟源:
● 32 kHz 低速内部 RC (LSI RC),该 RC 用于驱动独立看门狗,也可选择提供给 RTC 用于停机/待机模式下的自动唤醒。
● 32.768 kHz 低速外部晶振(LSE 晶振),用于驱动 RTC 时钟 (RTCCLK) 。对于每个时钟源来说,在未使用时都可单独打开或者关闭,以降低功耗。
2、stm32F103时钟系统配置-HAL库
2.1HAL 库的 SystemInit 函数-代码略
SystemInit
主要做了如下三个方面工作:
1
) 复位
RCC
时钟配置为默认复位值(默认开始了
HIS
)
2
) 外部存储器配置
3
) 中断向量表地址配置
HAL
库的
SystemInit
函数并没有像标准库的
SystemInit
函数一样进行时钟的初始化配置。
HAL
库的
SystemInit
函数除了打开
HSI
之外,没有任何时钟相关配置,所以使用
HAL
库我们必须编
写自己的时钟配置函数。
2.2在工程 SYSTEM 分组下面定义的 sys.c文件中的时钟初始化函数 Stm32_Clock_Init:
作用是进行时钟系统配置,除了配置 PLL 相关参数确定 SYSCLK 值之外,还配置了 AHB,APB1 和 APB2 的分频系数,也就是确定了 HCLK,PCLK1 和 PCLK2 的时钟值。
//时钟系统配置函数
//PLL:选择的倍频数,RCC_PLL_MUL2~RCC_PLL_MUL16
//返回值:0,成功;1,失败
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);
//设置系统时钟时钟源为 PLL
RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;
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_PLL_MUL9)
时候设置的入口参数值,我们可以计算出,PLL
时钟为
PLLCLK=HSE*9 =8MHz*9=72MHz
,同时我们选择系统时钟源为 PLL , 所 以 系 统 时 钟
SYSCLK=72MHz
。
AHB
分频系数为
1
,故其频率为 HCLK=SYSCLK/1=72MHz。
APB1
分频系数为
2
,故其频率为
PCLK1=HCLK/2=36MHz
。
APB2 分频系数为 1
,故其频率为
PCLK2=HCLK/1=72/1=72MHz
。最后我们总结一下通过调用函Stm32_Clock_Init(RCC_PLL_MUL9)之后的关键时钟频率值:
SYSCLK(系统时钟) =72MHz
PLL 主时钟 =72MHz
AHB
总线时钟(
HCLK=SYSCLK/1 )
=72MHz
APB1
总线时钟(
PCLK1=HCLK/2
) =36MHz
APB2
总线时钟(
PCLK2=HCLK/1
) =72MHz
2.3结构体 RCC_OscInitTypeDef 的定义:
typedef struct
{
uint32_t OscillatorType; //需要选择配置的振荡器类型
uint32_t HSEState; //HSE 状态
uint32_t HSEPredivValue; // Prediv1 值
uint32_t LSEState; //LSE 状态
uint32_t HSIState; //HIS 状态
uint32_t HSICalibrationValue; //HIS 校准值
uint32_t LSIState; //LSI 状态
RCC_PLLInitTypeDef PLL; //PLL 配置
}RCC_OscInitTypeDef;
前面几个参数主要是用来选择配置的振荡器类型。比如我们要开启
HSE
, 那么我们会设置 OscillatorType
的值为
RCC_OSCILLATORTYPE_HSE
,然后设置
HSEState
的值为 RCC_HSE_ON
开启
HSE。
成员变量是
PLL
,它是结构体
RCC_PLLInitTypeDef
类型。它的作用是配置
PLL 相关参数,我们来看看它的定义:
typedef struct
{
uint32_t PLLState; //PLL 状态
uint32_t PLLSource; //PLL 时钟源
uint32_t PLLMUL; //PLL VCO 输入时钟的乘法因子
}RCC_PLLInitTypeDef;
该结构体主要用来设置
PLL
时钟源以及相关分频倍频参数。
3、STM32F1 时钟使能和配置
外设时钟使能在
HAL
库中都是通过宏定义标识符来实现的。首先,我们来看看 GPIOA
的外设时钟使能宏定义标识符:
#define __HAL_RCC_GPIOA_CLK_ENABLE() do { \
__IO uint32_t tmpreg; \
SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);\
tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);\
UNUSED(tmpreg); \
} while(0U))
//设置寄存器 RCC_APB2ENR 的相关位为 1,哪个位,是由宏定义标识RCC_APB2ENR_IOPAEN 的值决定.
#define RCC_APB2ENR_IOPAEN ((uint32_t)0x00000001)
//设置寄存器
RCC->APB2ENR
寄存器的位
2
为
1
APB2ENR
寄存器的位
2
描述如下:
位 2 IOPAEN:IO 端口 A 时钟使能
由软件置 1 和清零
0:禁止 IO 端口 A 时钟
1:使能 IO 端口 A 时钟
使用方法为:
__HAL_RCC_GPIOA_CLK_ENABLE();//使能 GPIOA 时钟
几个常用使能外设时钟的宏定义标识符使用方法:
__HAL_RCC_DMA1_CLK_ENABLE();//使能 DMA1 时钟
__HAL_RCC_USART2_CLK_ENABLE();//使能串口 2 时钟
__HAL_RCC_TIM1_CLK_ENABLE();//使能 TIM1 时钟
几个常用的禁止外设时钟的宏定义标识符使用方法:
__HAL_RCC_DMA1_CLK_DISABLE();//禁止 DMA1 时钟
__HAL_RCC_USART2_CLK_DISABLE();//禁止串口 2 时钟
__HAL_RCC_TIM1_CLK_DISABLE();//禁止 TIM1 时钟
4、端口复用和重映射
4.1端口复用
比如说 STM32F103ZET6 有 5 个串口,我们可以查手册知道,串口 1 的引脚对应的 IO 为 PA9,PA10.PA9,PA10 默认功能是 GPIO,所以当 PA9,PA10 引脚作为串口 1 的 TX,RX 引脚使用的时候,那就是端口复用:

4.2串口1复用功能的一般步骤
以串口 1 为例来讲解配置 GPOPA.9,GPIOA.10 口为串口 1 复用功能的一般步骤:
1)先打开对应的 IO 时钟和复用功能外设时钟
——HAL_RCC_GPIOA_CLK_ENABLE(); //使能 GPIOA 时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能 USART1 时钟
__HAL_RCC_AFIO_CLK_ENABLE(); //使能辅助功能 IO 时钟
2)在 GIPOx_MODER 寄存器中将所需 IO(对于串口 1 是 PA9,PA10)配置为复用功能
3)对 IO 口的其他参数,例如上拉/下拉以及输出速度等进行配置。
上面三步,在我们 HAL 库中是通过
HAL_GPIO_Init
函数来实现的,参考代码如下:
GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化 PA9
通过上面的配置,PA9 复用为串口 1 的发送引脚。