时钟树
简介
时钟就像心脏一样,推动单片机各个部分运作以及执行相应的指令。例如IIC/SPI时钟线、定时器、ADC采样间隔等。
内部时钟简介
以下三种不同的时钟源可被用来驱动系统时钟(SYSCLK)
HSI(高速内部时钟):HSI时钟信号由内部8MHz的RC振荡器产生,可直接作为系统时钟或在2分频后作为PLL输入。
HSE(高速外部时钟):来源为外部无源晶振,速度8M(可提供精确主时钟)。
PLL(锁相环时钟):来源为HSI/2或HSE经过倍频所得。
其余为:
LSE(低速外部时钟):一个32.768kHz的低速外部晶体或陶瓷谐振器。它为实时时钟或者其他定时功能提供一个低功耗且精确的时钟源。
LSI(低速内部时钟):一个低功耗时钟源的角色,它可以在停机和待机模式下保持运行,为独立看门狗和自动唤醒单元提供时钟。 LSI时钟频率大约40kHz(在30kHz和60kHz之间)。
系统时钟选择(SYSCLK)
系统复位后会先选择HSI作为系统时钟,若时钟源被直接选择为HSE或者PLL锁相环时钟时,并不会直接切换时钟源,而是需等被选择时钟稳定运行后才会切换系统时钟源
时钟线
HCLK(AHB先进高性能总线时钟)
SYSCLK(系统时钟)通过AHB预分频之后得到的时钟叫HCLK(AHB总线时钟)
为内存、DMA(直接存储器访问)、SysTick提供时钟
APB1总线时钟
通过桥接2接入AHB系统总线,通过APB1预分频器进行分频,APB1操作速度限于36MHz。但作用于通用定时器时,默认会2倍频,所以为72MHz。此倍频器的值无法更改,但会根据APB1预分频器自动修改倍频数值为x1或x2。
APB2总线时钟
通过桥接1接入AHB系统总线,通过APB2预分频器进行分频,APB2操作于全速(最高72MHz)。
APB连接外设参考图
其他时钟及接口
FCLK自由运行时钟
进入低功耗的停止模式时,HCLK总线会停止运行,所有连接到AHB总线上的外设都会停止运行。
当再次唤醒设备时,就需要通过外部中断与FCLK来完成。
FCLK的时钟真正来源为AHB的预分频器,所以当AHB总线关闭时,FCLK还可以继续运行
Flash编程接口时钟 永远连接HSI
USB接口时钟 来自PLL锁相环
CSS时钟安全系统
当时钟源是HSE或者是经过锁相环倍频的HSE时,可以选择开启CSS功能。当时钟源出问题时,会自动切换为HSI时钟,并且可以产生中断,让使用者可以进行紧急处理。
RTC与看门狗时钟可参考上图时钟树
定时器
简介
定时器分为基本定时器、通用定时器和高级定时器
这里主要介绍通用定时器。
通用定时器
16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时。
时钟来源
1.内部时钟(CK_INT):可上一部分APB1总线输出的时钟,频率为72MHz。
2.外部时钟模式1:可接ETR外部时钟、ITRx其他定时器或TIx捕获通道
3.外部时钟模式2:可以接ETR外部时钟。
4.编码器模式:读取正交编码器波形
时基单元
由PSC、ARR和CNT组成
PSC预分频器:预分频器可以将计数器的时钟频率按1到65536之间的任意值分频。
ARR自动重装器:ARR是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
影子寄存器:开启后当预分频器数值改变时,并不立刻改变时钟分频系数,而是等当前周期跑完憨批,下一个周期开始执行更新的分频系数。
CNT计数器:计数到自动重装器ARR的值后进行更新事件并置一个标志位,可以通过中断输出控制(判断是否需要中断),需要则到NVIC请求中断。CNT有三种模式,向上计数,向下计数和中央对齐。
计数器溢出频率=CK_PSC/(PSC+1)/(ARR+1)
预分频器PSC传值为需要分频数-1。比如二分频则传值2-1,预分频计数器则0 1 0 1计数改变定时器时钟频率。
通用定时器框图
定时器配置
流程
1.选择时钟源RCC开启时钟
2.选择时基单元的时钟源
3.配置时基单元
4.配置输出中断控制
5.配置NVIC
6.运行控制,使能计数器
标准库函数
//略
void TIM_DeInit(TIM_TypeDef* TIMx);
//时基单元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
//结构体变量赋值
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
//使能计数器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
//使能终端输出控制,中断允许位
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
//时钟源选择
//选择内部时钟
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
//选择ITRx其他定时器时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
//选择TIx捕获通道的时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
//选择ETR通过外部模式1输入的时钟
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);
//选择ETR通过外部模式2输入的时钟
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
//配置ETR引脚预分频器、极性、滤波器这些
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);
//单独函数
//单独写预分频值
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
//改变计数器的计数模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
//自动重器预装功能,开影子寄存器
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
//给计数器写入一个值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
//给自动重装器写一个值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
//获取当前计时器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
//获取当前预分频器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
//获取和清除标志位
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
配置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
//时基单元初始化
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //滤波器时钟时钟分频,用于配置,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //初始化TIM_TimeBaseInit
/*中断输出配置*/
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位
//TIM_TimeBaseInit函数末尾,手动产生了一次更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断,允许中断
/*NVIC中断分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2
/*NVIC配置*/
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure); //初始化NVIC_Init
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
定时器中断函数
void TIM2_IRQHandler(void) //函数名参考启动文件
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//查看TIM2的更新中断标志位
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清除标志位
}
}