通用定时器基本原理
通用定时器功能特点描述
STM3 的通用 TIMx (TIM2、TIM3、TIM4 和 TIM5)定时器功能特点包括:
- 位于低速的APB1总线上(APB1)
- 16 位向上、向下、向上/向下(中心对齐)计数模式,自动装载计数器(TIMx_CNT)。
- 16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数 为 1~65535 之间的任意数值。
- 4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
- 输入捕获
- 输出比较
- PWM 生成(边缘或中间对齐模式)
- 单脉冲模式输出
- 可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。
- 如下事件发生时产生中断/DMA(6个独立的IRQ/DMA请求生成器):
- 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
- 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
- 输入捕获
- 输出比较
- 支持针对定位的增量(正交)编码器和霍尔传感器电路
- 触发输入作为外部时钟或者按周期的电流管理
- STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。
- 使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。
计数器模式
通用定时器可以向上计数、向下计数、向上向下双向计数模式。
- 向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
- 向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
- 中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
时钟来源
- 内部时钟(CK_INT)默认
- 外部时钟模式1:外部输入脚(TIx)
- 外部时钟模式2:外部触发输入(ETR)
- 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。
实际单元:经过CK_PSC预分频得到计数器最终的时钟,技术结束后会重装载或产生相应的触发事件溢出事件
输入捕获部分:捕获通道引脚上的电平,常用于得到脉冲长度计算eg:上升沿
输出比较:每个通道要么用于输入比较要么用于输出比较
定时器中断
定时器中断实验相关寄存器
计数器当前值寄存器CNT
预分频寄存器TIMx_PSC
自动重装载寄存器(TIMx_ARR)
控制寄存器1(TIMx_CR1)
DMA中断使能寄存器(TIMx_DIER)
库函数
定时器参数初始化:
void TIM_TimeBaseInit(TIM_TypeDef* TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
typedef struct
{
uint16_t TIM_Prescaler; //预分频系数
uint16_t TIM_CounterMode; //计数模式
uint16_t TIM_Period; //自动装载值
uint16_t TIM_ClockDivision; //不重要
uint8_t TIM_RepetitionCounter; //不重要
} TIM_TimeBaseInitTypeDef;
TIM_TimeBaseStructure.TIM_Period = 4999;
TIM_TimeBaseStructure.TIM_Prescaler =7199;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
定时器使能函数:
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
定时器中断使能函数:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
状态标志位获取和清除
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();
- 初始化定时器,配置ARR,PSC。
TIM_TimeBaseInit();
- 开启定时器中断,配置NVIC。
void TIM_ITConfig(); //使能响应中断
NVIC_Init(); //中断优先级设置
- 使能定时器。
TIM_Cmd(); //使能定时器
- 编写中断服务函数。
TIMx_IRQHandler();
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否,TIM_IT_Update更新中断
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志
//执行内容
}
}
程序实操
程序要求
通过定时器中断配置,每500ms中断一次,然后中断服务函数中控制LED实现LED1状态取反(闪烁)。
Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk
其中(PSC+1)/Tclk相当于周期的时间长度
Tclk为时钟频率(72MHz),系统初始化函数初始化APB1
PSC+1预分频系数加一,设为7199,则定时器的时钟为72M/7200=10k,T=0.1ms
timer.c
#include "timer.h"
#include "led.h"
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
//定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断,中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx
}
//定时器3中断服务程序
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志
LED1=!LED1;
}
}
main.c
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
LED_Init(); //LED端口初始化
TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms
while(1)
{
}
}
PWM处处实验
STM32 PWM工作过程
ARR确定周期
CCRx确定占空比
- CCR1:捕获比较(值)寄存器(x=1,2,3,4):设置比较值。
- CCMR1: OC1M[2:0]位:
对于PWM方式下,用于设置PWM模式1【110】或者PWM模式2【111】 - CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平有效。
- CCER:CC1E位:输入/捕获1输出使能。0:关闭,1:打开。
PWM模式1 & PWM模式2
PWM模式1:CNT< CCRx时为有效电平
高电平有效就是输出为有效电平时输出高电平
STM32 PWM工作过程
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
自动重载的预装载寄存器
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
//简单的说,更改ARR后,ARPE=1,ARR立即生效。。。APRE=0,ARR下个比较周期生效。
STM32 定时器3输出通道引脚
PWM库函数
初始化
void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
typedef struct
{
uint16_t TIM_OCMode; //PWM模式1或者模式2
uint16_t TIM_OutputState; //输出使能 OR失能
uint16_t TIM_OutputNState;//没用
uint16_t TIM_Pulse; //比较值,写CCRx
uint16_t TIM_OCPolarity; //比较输出极性
uint16_t TIM_OCNPolarity; //没用
uint16_t TIM_OCIdleState; //没用
uint16_t TIM_OCNIdleState; //没用
} TIM_OCInitTypeDef;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //PWM模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure. TIM_Pulse=100; //初始化设置,后面可以不断改变
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
设置比较值函数:
void TIM_SetCompareX(TIM_TypeDef* TIMx, uint16_t Compare2);
使能输出比较预装载:
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
改变CCER寄存器相关位——极性
void TIM_OCxPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
使能自动重装载的预装载寄存器允许位:
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
PWM输出实验
使用定时器3的PWM功能,输出占空比可变的PWM波,用来驱动LED灯,从而达到LED[PB5]亮度由暗变亮,又从亮变暗,如此循环。
PWM输出配置步骤:
- 使能定时器3和相关IO口时钟。
- 使能定时器3时钟:
RCC_APB1PeriphClockCmd();
- 使能GPIOB时钟:
RCC_APB2PeriphClockCmd();
- 初始化IO口为复用功能输出。函数:
GPIO_Init(); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- 这里我们是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,所以需要开启AFIO时钟。同时设置重映射。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
- 初始化定时器:ARR,PSC等:
TIM_TimeBaseInit();
- 初始化输出比较参数:
TIM_OC2Init();
- 使能预装载寄存器:
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
- 使能定时器。
TIM_Cmd();
- 不断改变比较值CCRx,达到不同的占空比效果:
TIM_SetCompare2();
程序实操
- 在头文件中声明void TIM3_PWM_Init(u16 arr,u16 psc)
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM3 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
- 主函数中
TIM3_PWM_Init(899,0); //不分频。PWM频率=72000000/900=80Khz