使用的单片机机型为STM32F103C8T6
文章目录
定时器
TIM(Timer) 定时器
可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
不仅具备基本的定时中断功能,还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
根据复杂度和应用场景分为高级定时器、通用定时器、基本定时器三种类型
本STM32机型的定时器资源:TIM1,TIM2,TIM3,TIM4,即一个高级定时器,三个通用定时器
基本定时器
基本定时器只有 内部时钟(系统时钟) 这一时钟源
时钟信号先到达 触发控制器
,控制选择如下二者功能之一:
- TRGO 为主模式触发 DAC ,用于输出
模拟信号
- 时基单元:由预分频器,CNT计数器,自动重装寄存器组成。用于计时,计数,触发定时中断
- 预分频器:用于对时钟源进行分频。如时钟频率为 72MHz,预分频器值为72,则输出到 CNT计数器的频率为72/72=1MHz。
- CNT计数器:随时钟频率计数加一。如频率为1MHz,则每1us加一
- 自动重装寄存器:类似阈值,
当 CNT计数等于自动重装值时,触发定时器中断
通用定时器
通用定时器相比基础定时器看上去就复杂很多,主要多了如下功能:
- 内外时钟源选择:内部时钟、外部输入
- 输入捕获:测PWM,占空比
- 输出比较:输出PWM。可用于控制舵机,直流电机
- 编码器:测量编码器速度,方向
- 主从触发模式功能:定时器联调
高级定时器
后续补充。。。。。。。。。。。。。。。。。
时钟源
时钟树
STM32的时钟树由多个时钟源和时钟分频组成,为STM32芯片提供各种时钟信号
STM32的基础时钟源有4个振荡器时钟:HSI(高速内部时钟)、HSE(高速外部时钟)、LSI(低速内部时钟)、LSE(低速外部时钟)。这是最底层的时钟来源
其中
- 40kHz低速内部 LSI 振荡器,可用于驱动独立看门狗,或通过程序选择驱动 RTC(Real-Time Clock 实时时钟),用于从停机/待机模式下自动唤醒系统
- 32.768kHz低速外部 LSE 振荡器也可驱动 RTC
我们常说的系统时钟,为SYSCLK
,由三种不同的时钟源驱动:HSI、HSE、PLL
此机型的主频为72MHz,但 HSE 最高频率为16MHz,HSI 频率为 8MHz,都达不到主频。那系统主频是怎么来的呢?
其实就是将 HSI 或 HSE 接入 PLL,通过 PLL 倍频产生的
系统时钟通过选择器选择时钟源,输入源有 HSI(内部高速时钟)、HSE(外部高速时钟)、PLL锁相环
通常STM32启动时,先使用 HSI 内部时钟,同时 HSI 接入 PLL锁相环进行倍增,待倍增后的 PPLCLK 稳定后,主频再切换至 72MHz
外设时钟
输出到定时器的频率都为72MHz
APB1预分频虽然会降低频率,但通向定时器2-7,还会进行倍增,又会恢复至72MHz
APB2同理
定时中断
下图为通用定时器的定时中断结构
分为三个部分:时钟选择,时基单元,中断控制
时钟选择
计数器时钟可由下列时钟源提供:
- RCC内部时钟,频率为72MHz
- 外部时钟模式2:GPIO控制的外部输入触发 ETR
- 外部时钟模式1:外部输入脚(TIx),用于输入捕获
- 内部触发输入(ITRx):使用另一个定时器作为另一个定时器的预分频器,如可以配置一个定时器 Timer1 作为另一个定时器 Timer2 的预分频器,实现定时器联调
时基单元
由预分频器,CNT计数器,自动重装寄存器组成
预分频器PSC
预分频器可以将计数器的时钟频率按1到65536之间的任意值进行分频。如系统频率为72MHz,预分频为2,则计数器接收的频率为72/2=36MHz
如此图示代表有影子寄存器,作用同缓冲器,可以在工作时被修改,新的预分频器参数在下一次更新事件到来时被采用,如下图
原先,预分频值为0,即不分频,计数器时钟等于系统时钟。但在计数器工作时,修改了预分频值,如果没有影子寄存器,那么新的分频就会立即应用,导致前后计数频率不同。使用影子寄存器,会先保存新的预分频值,在发生一次更新事件后才应用新的预分频值
- 计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
自动重装寄存器ARR
同样有影子寄存器,当 CNT计数器值等于 ARR 时,触发定时器中断
- 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
如预分频PSC = 7200 - 1,代表计时器频率为72MHz/7200=1KHz,每1ms计时器加一
ARR = 1000 - 1,即1000次计数器加一才会溢出,即1000个1ms
最终效果就为计数器每1s溢出一次,频率为1Hz
CNT计数器
计数器有三种模式
-
向上计数:计数器值从0计数加一到ARR,产生中断
-
向下计数:计数器值从ARR计数减一到0,产生中断
-
中央对齐:计数器从0计数加一到 ARR,产生中断;然后再计数减一到0,产生中断
计数器时序
编程实例
定时器内部时钟
- 程序目标:实现计时功能,单位为s
- 程序原理:使用定时器2,时钟源选择内部时钟,配置时基单元,实现每1s产生一次定时器中断,在中断处理函数中对计数加一
接线图如下:
定时器配置流程参照下图:分为时钟选择,时基单元,中断控制。相关函数声明在stm32f10x_tim.h
时钟选择
使用TIM2,因为为外设,需要开启时钟
c
//开启TIM2的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
选择内部时钟源
c
//配置时钟源
//配置内部时钟源为TIM2,若不配置,则默认为内部时钟
TIM_InternalClockConfig(TIM2);
时基单元
配置 PSC 和 ARR,实现每1s产生一次中断的效果
采用向上计数
c
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数,从0到重装值
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //重装值 - 1,因为从0开始,ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器,PSC的值。如配置为72,即系统频率72MHz / 72 = 1MHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
中断控制
c
//中断部分
//时基单元初始化完成后会发起一次更新事件,需要清理一下,避免影响复位
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
//开启TIM2的更新中断
TIM_ITConfig(TIM2, TIM_FLAG_Update, ENABLE);
//中断优先级组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//NVIC中断嵌套初始化
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能开关
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级
NVIC_Init(&NVIC_InitStructure);
//开启TIM2
TIM_Cmd(TIM2, ENABLE);
PS:在TIM_TimeBaseInit
中,完成时基单元初始化时,会产生一次更新中断,需要手动清除,否则会出现程序复位为1,而不是0的情况
完整初始化代码如下:
c
/**
* @brief 定时器初始化
* @parm 无
* @retval 无
*/
void Timer_Init(void)
{
//开启TIM2的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//配置时钟源
//配置内部时钟源为TIM2,若不配置,则默认为内部时钟
TIM_InternalClockConfig(TIM2);
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数,从0到重装值
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //重装值 - 1,因为从0开始,ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器,PSC的值。如配置为72,即系统频率72MHz / 72 = 1MHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//中断部分
//时基单元初始化完成后会发起一次更新事件,需要清理一下,避免影响复位
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
//开启TIM2的更新中断
TIM_ITConfig(TIM2, TIM_FLAG_Update, ENABLE);
//中断优先级组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//NVIC中断嵌套初始化
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能开关
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级
NVIC_Init(&NVIC_InitStructure);
//开启TIM2
TIM_Cmd(TIM2, ENABLE);
}
中断处理函数的声明在startup_stm32f10x_md.s
c
//定时器2中断处理函数
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_FLAG_Update) == SET)//判断是否是更新中断
Num++;
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
}
完整程序链接:【STM32】基础定时器
定时器外部时钟源
- 程序目标:使用对射式红外传感器控制GPIO口,遮挡时输出低电平,TIM2以该GPIO为外部时钟源,实现计次
- 程序原理:定时器初始化大抵相同,还需配置GPIO 和 外部时钟源
接线图如下:
首先开启相关外设时钟
c
//开启TIM2的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//开启GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
配置GPIO
c
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
配置外部时钟源
c
//配置时钟源
//配置外部时钟源
// 不分频 低电平或下降沿触发 滤波
TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0x0F);
后续时基单元和中断控制配置大抵相同
完整代码如下:
c
/**
* @brief 定时器初始化
* @parm 无
* @retval 无
*/
void Timer_Init(void)
{
//开启TIM2的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//开启GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置时钟源
//配置外部时钟源
// 不分频 低电平或下降沿触发 滤波
TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0x0F);
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数,从0到重装值
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //重装值 - 1,因为从0开始,ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //预分频器,PSC的值。如配置为72,即系统频率72MHz / 72 = 1MHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//中断部分
//时基单元初始化完成后会发起一次更新事件,需要清理一下,避免影响复位
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
//开启TIM2的更新中断
TIM_ITConfig(TIM2, TIM_FLAG_Update, ENABLE);
//中断优先级组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//NVIC初始化
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能开关
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//开启TIM2
TIM_Cmd(TIM2, ENABLE);
}
完整程序链接:【STM32】定时器外部时钟源计次
以上就是本篇博客的所有内容,感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。