【STM32标准库】【基础知识】通用定时器和定时器中断
STM32F4系列定时器
文章目录
文章基于适用于STM32F4系列,作者使用STM32F401CCU6开发板。
本文章基于此系列和开发板展开讨论。
定时器
什么是定时器
定时器其实就是一种计数器,它可以计算边沿(或电平)的次数,如果这个电平变化的频率确定,则可以根据这个计数值来区分时间。
定时器分类
分为高级定时器,通用定时器,基本定时器
这三个分别是向下包含的,也就是说高级定时器功能包含通用定时器和基本定时器,通用定时器功能包含基本定时器
高级定时器比通用定时器多了关于电机驱动输出功能,需要使用时再细说
基本定时器的功能和数量较少,一般用于DAC(数模转换)和DMA(直接存储器访问)且只能使用内部时钟
一般情况我们使用通用定时器即可,本文重点介绍通用定时器
STM32F4定时器
类型 | 编号 | 分辨率 | 计数方向 | DMA请求 | 挂载总线 |
---|---|---|---|---|---|
高级 | TIM1,(TIM8) | 16 | 增 / 减 | 有 | APB2 |
通用 | TIM2,TIM5 | 32 | 增 / 减 | 有 | APB1 |
通用 | TIM3,TIM4 | 16 | 增 / 减 | 有 | APB1 |
通用 | TIM9,TIM10,TIM1 | 16 | 增 | 无 | APB2 |
通用 | (TIM12,TIM13,TIM14) | 16 | 增 | 无 | APB1 |
基本 | (TIM6,TIM7) | 16 | 增 | 有 | APB1 |
加括号的是作者使用的单片机没有的定时器
因为系统时钟的频率较高,使用定时器时需要的频率可能较低,因此可以预先分频 预分频的值为1-65535
通用定时器
基础介绍
时钟源
- 内部时钟
- 外部时钟1
- 外部时钟2
- 其他定时器的溢出
复位时默认为内部时钟,一般情况不需要使用内部时钟即可。
需要修改请参考 定时器时钟源选择
使用内部时钟时,默认时所有的定时器的输入时钟均为系统时钟,详情见
时钟系统介绍
计数模式
向上计数模式
从0开始计时,到自动加载值ARR(后文初始化时会介绍),归零,这时产生一次溢出
黑色箭头处产生溢出
向下计数模式
从自动加载值ARR(后文初始化时会介绍)开始计时,到0,回到ARR,这时产生一次溢出
黑色箭头处产生溢出
中央对齐模式
从0到自动加载值ARR-1(后文初始化时会介绍),此时产生一次溢出,后从自动加载值ARR(后文初始化时会介绍)开始计时,到1,此时产生一次溢出
黑色箭头处可以产生比较事件
初始化
本文使用内部时钟
初始化思路
使用内部时钟的初始化流程
- 打开定时器时钟(注意要打开对应的时钟)
- 配置定时器初始化结构体
- 初始化定时器
1.打开定时器时钟
使用这两个函数之一打开时钟
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
注意:不同的定时器挂载的总线不同
TIM2,TIM3,TIM4,TIM5,TIM12,TIM13,TIM14挂载在APB1
TIM9,TIM10,TIM11挂载在APB2
APB1总线下的名称
RCC_APB1Periph_TIM2
RCC_APB1Periph_TIM3
RCC_APB1Periph_TIM4
RCC_APB1Periph_TIM5
RCC_APB1Periph_TIM12
RCC_APB1Periph_TIM13
RCC_APB1Periph_TIM14
APB2总线下的名称
RCC_APB2Periph_TIM9
RCC_APB2Periph_TIM10
RCC_APB2Periph_TIM11
例子
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
打开TIM2的时钟
2. 配置定时器初始化结构体
typedef struct
{
uint16_t TIM_Prescaler; //预分频
uint16_t TIM_CounterMode; //计数模式
uint32_t TIM_Period; //定时器周期
uint16_t TIM_ClockDivision; //定时器时钟分频因子
uint8_t TIM_RepetitionCounter; //重载周期(高级定时器特有)
} TIM_TimeBaseInitTypeDef;
TIM_Prescaler
预分频器,设置这个可以选定定时器的时钟频率,将总线时钟分频后输入 其数值为1-65535
定时器时钟频率= 系统频率/(TIM_Prescaler+1)
TIM_CounterMode
这里计数模式有三种,上文介绍过
其取值为
TIM_CounterMode_Up //向上计数
TIM_CounterMode_Down //向下计数
TIM_CounterMode_CenterAligned1 //中央对齐模式1
TIM_CounterMode_CenterAligned2 //中央对齐模式2
TIM_CounterMode_CenterAligned3 //中央对齐模式3
向上和向下计数产生溢出事件,
中央对齐模式1在向下计数时产生比较事件
中央对齐模式2在向上计数时产生比较事件
中央对齐模式3在向上和向下计数时产生比较事件
中央对齐模式可以使用向上和向下计数来代替
TIM_Period
定时器周期,定时器随输入的时钟计数,一共计数 TIM_Period+1个
TIM_Period也就是能达到的最大值,即上文介绍计数模式时纵坐标的最大值(最小值为0)
16位范围为0-0xFFFF
32位范围为0-0xFFFFFFFF
TIM_ClockDivision
定时器时钟分频因子ClockDivision是决定数字滤波器采样频率的参数。
没怎么使用过,一般设置为 不分割(TIM_CKD_DIV1)即可
TIM_RepetitionCounter
高级定时器特有,通用定时器设置为0就行,
定时器的时间
使用内部时钟,默认时钟设置,向上或向下计数模式
溢出时间 = [ ( TIM_Period + 1 ) * ( TIM_Prescaler + 1 ) / ( SystemCoreClock ) ] ( s )
间隔时间= [ 两次计数值的差 * ( TIM_Prescaler + 1 ) ] / ( SystemCoreClock ) ] ( s )
3. 初始化定时器
使用这个函数初始化定时器
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
第一个输入是定时器号,取值可以是
TIM1 到 TIM14
第二个输入是定时器初始化结构体的地址,请使用取址符 ( & )
例子
TIM_TimeBaseInitTypeDef TIM_Init_Struct; //声明定时器初始化结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //打开时钟
TIM_Init_Struct.TIM_ClockDivision=TIM_CKD_DIV1; //滤波器不分频
TIM_Init_Struct.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
//每次中断触发时间=[(TIM_Period+1)*(TIM_Prescaler+1)/(SystemCoreClock)] (s)
//这里是5ms
TIM_Init_Struct.TIM_Period=839;
TIM_Init_Struct.TIM_Prescaler=499;
TIM_Init_Struct.TIM_RepetitionCounter=0; //高级定时器特有,这里写0就行
TIM_TimeBaseInit(TIM3,&TIM_Init_Struct); //调用函数初始化
定时器中断
思路
- 初始化定时器
- 配置NVIC
- 编写中断服务函数
- 打开定时器,使能中断
2. 配置NVIC
关于NVIC的内容在之前说过,详情 见中断和NVIC
与通用定时器相关的中断名称如下
有些通用定时器中断和高级定时器中断放在一个中断函数中了
TIM1_BRK_TIM9_IRQn;
TIM1_UP_TIM10_IRQn;
TIM1_TRG_COM_TIM11_IRQn;
TIM2_IRQn;
TIM3_IRQn;
TIM4_IRQn;
TIM5_IRQn;
(TIM8_BRK_TIM12_IRQn);
(TIM8_UP_TIM13_IRQn);
(TIM8_TRG_COM_TIM14_IRQn);
加括号的为作者使用的单片机没有的定时器
3. 编写中断服务函数
与通用定时器有关中断服务函数名如下
void TIM1_BRK_TIM9_IRQnHandler(void);
void TIM1_UP_TIM10_IRQHandler(void);
void TIM1_TRG_COM_TIM11_IRQHandler(void);
void TIM2_IRQHandler(void);
void TIM3_IRQHandler(void);
void TIM4_IRQHandler(void);
void TIM5_IRQHandler(void);
{void TIM8_BRK_TIM12_IRQnHandler(void);
void TIM8_UP_TIM13_IRQnHandler(void);
void TIM8_TRG_COM_TIM14_IRQnHandler(void);}
加大括号的为作者使用单片机的没有的定时器
4. 打开定时器,使能中断
使用这个函数设置中断使能
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
第一个输入是选择哪个定时器
第二个输入是选择那种中断
第三个输入是选择使能或失能
定时器的中断和外部中断不同,有不同种类的中断,可以是溢出,可以是捕获等等
一般常用的就是溢出
TIM_IT_Update
使用这个函数打开定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
第一个输入是选择哪个定时器
第二个输入是选择使能或失能
例子
初始化和NVIC配置
TIM_TimeBaseInitTypeDef TIM_Init_Struct; //声明定时器初始化结构体
NVIC_InitTypeDef NVIC_Init_Struct; //声明NVIC初始化结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //打开时钟
TIM_Init_Struct.TIM_ClockDivision=TIM_CKD_DIV1; //滤波器不分频
TIM_Init_Struct.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
//每次中断触发时间=[(TIM_Period+1)*(TIM_Prescaler+1)/(SystemCoreClock)] (s)
//这里是5ms
TIM_Init_Struct.TIM_Period=839;
TIM_Init_Struct.TIM_Prescaler=499;
TIM_Init_Struct.TIM_RepetitionCounter=0; //高级定时器特有,这里写0就行
TIM_TimeBaseInit(TIM3,&TIM_Init_Struct); //调用函数初始
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //启用溢出中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2
NVIC_Init_Struct.NVIC_IRQChannel=TIM3_IRQn; //中断名称
NVIC_Init_Struct.NVIC_IRQChannelCmd=ENABLE; //使能
NVIC_Init_Struct.NVIC_IRQChannelPreemptionPriority=1; //主优先级1
NVIC_Init_Struct.NVIC_IRQChannelSubPriority=1; //副优先级1
NVIC_Init(&NVIC_Init_Struct); //初始化NVIC
TIM_Cmd(TIM3,ENABLE); //打开定时器
中断服务函数
void TIM3_IRQHandler(void)
{
if( TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)
{
/*需要执行代码*/
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //将中断标志清除
}
}
与定时器有关的常用函数
中断标志相关
TIM_GetITStatus
原型
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT)
名称 | 描述 |
---|---|
输入1 | 定时器编号 |
输入2 | 标志类型 |
输出 | 设置或未设置 |
输出
RESET //没有触发
SET //已经触发
功能描述:判断选定定时器的选定中断标志的状态
TIM_ClearITPendingBit
原型
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
名称 | 描述 |
---|---|
输入1 | 定时器编号 |
输入2 | 标志类型 |
输出 | 无 |
功能描述:将选定定时器的选定中断标志清除(设置为RESET)
计数值相关
TIM_GetCounter
原型
uint32_t TIM_GetCounter(TIM_TypeDef* TIMx)
名称 | 描述 |
---|---|
输入 | 定时器的编号 |
输出 | 当前计数值 |
功能描述:获取选定的定时器当前的计数值
TIM_SetCounter
原型
void TIM_SetCounter(TIM_TypeDef* TIMx, uint32_t Counter)
名称 | 描述 |
---|---|
输入1 | 定时器编号 |
输入2 | 要设置的计数值 |
输出 | 无 |
功能描述:设置选定的定时器的计数值
杂
TIM_CounterModeConfig
原型
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode)
名称 | 描述 |
---|---|
输入1 | 定时器编号 |
输入2 | 计数模式 |
输出 | 无 |
功能描述:改变定时器的计数模式
TIM_Cmd
原型
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
名称 | 描述 |
---|---|
输入1 | 定时器编号 |
输入2 | 使能或失能 |
输出 | 无 |
输入2
ENABLE //打开
DISABLE //关闭
功能描述:打开或关闭选定的定时器
总结
定时器的用法较多,STM官方给了许许多多好用的功能,比如输入捕获,输出比较等等,这些可以使用外部中断和基本的定时器设置实现。
可以根据需要选择不同用法。
个人不习惯使用捕获和比较,他们的输入输出通道相对单一,可移植性不好。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)