1.通用定时器PWM模式初始化流程
<1>建立GPIO、时基、输出比较结构体
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
<2>使能定时器以及相关IO口时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
<3>配置GPIO、时基、输出比较结构体并初始化
//初始化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 Channel 1 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_OCPolarity_Low
TIM_OCInitStructure.TIM_Pulse = 1;
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //定时器通道一的初始化
<4>使能预装载寄存器
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器,这一步在大部分操作中可以省略。
<5>使能定时器
TIM_Cmd(TIM3, ENABLE);
2.通用定时器结构体
- 时基初始化结构体 >> TIM_TimeBaseInitTypeDef
typedef struct
{
uint16_t TIM_Prescaler; /*定时器预分频设置。 value:(0~0xFFFF)*/
uint16_t TIM_CounterMode;/*选择了计数器模式。Value:
#define TIM_CounterMode_Up ((uint16_t)0x0000) //TIM 向上计数模式
#define TIM_CounterMode_Down ((uint16_t)0x0010) //TIM 向下计数模式
#define TIM_CounterMode_CenterAligned1 ((uint16_t)0x0020) //TIM 中央对齐模式 1 计数模式
#define TIM_CounterMode_CenterAligned2 ((uint16_t)0x0040) //TIM 中央对齐模式 2 计数模式
uint16_t TIM_Period; /*设置了在下一个更新事件装入活动的自动重装载寄存器周期的值。value:0x0000~0xFFFF*/
uint16_t TIM_ClockDivision; /*设置定时器时钟CK_INT频率与死区发生器以及数字滤波器采样时钟频率分频化。Value:
#define TIM_CKD_DIV1 ((uint16_t)0x0000)
#define TIM_CKD_DIV2 ((uint16_t)0x0100)
#define TIM_CKD_DIV4 ((uint16_t)0x0200)*/
uint8_t TIM_RepetitionCounter; /*是否使用重复定时器,当该值不为0的时候,计数器计数值达到周期数时,该值减1,计数器重新计数,当该值减到0的时候才会产生事件。*/
} TIM_TimeBaseInitTypeDef;
备注:
若是主频72MHz、TIM_Prescaler的值设置为(72-1),则定时器时钟频率=72MHz/72=1MHz。知道频率就可以算周期了,用1/1MHz乘以TIM_Period的值就是定时的时间(周期),如果加入了重复定时器,那么还要乘以TIM_RepetitionCounter的值才是定时的时间(周期)。
- 定时器比较输出初始化结构体 >> TIM_OCInitTypeDef
typedef struct
{
uint16_t TIM_OCMode;/*比较输出模式选择,共8种。value:
#define TIM_OCMode_Timing ((uint16_t)0x0000) // TIM 输出比较时间模式
#define TIM_OCMode_Active ((uint16_t)0x0010) //TIM 输出比较主动模式
#define TIM_OCMode_Inactive ((uint16_t)0x0020) //TIM 输出比较非主动模式
#define TIM_OCMode_Toggle ((uint16_t)0x0030) //TIM 输出比较触发模式
#define TIM_OCMode_PWM1 ((uint16_t)0x0060) //TIM 脉冲宽度调制模式 1
#define TIM_OCMode_PWM2 ((uint16_t)0x0070) //TIM 脉冲宽度调制模式 2*/
uint16_t TIM_OutputState;/*比较输出使能,决定信号是否通过外部引脚输出。value:0(Disable)、1(Enable)。 */
uint16_t TIM_OutputNState; /*比较互补输出使能,决定互补信号是否通过外部引脚输出。value:0(Disable)、1(Enable)。*/
uint16_t TIM_Pulse; /*比较输出的脉冲宽度,设置占空比。Value:0x0000~0xFFFF*/
uint16_t TIM_OCPolarity; /*比较输出极性,决定定时器通道有效电平的极性。Value:
#define TIM_OCPolarity_High ((uint16_t)0x0000)
#define TIM_OCPolarity_Low ((uint16_t)0x0002)*/
uint16_t TIM_OCNPolarity; /*比较互补输出极性,可选高电平有效、低电平有效。Value:
#define TIM_OCNPolarity_High ((uint16_t)0x0000)
#define TIM_OCNPolarity_Low ((uint16_t)0x0008)*/
uint16_t TIM_OCIdleState; /*空闲状态时通道输出电平设置,可选高电平、低电平。Value:
#define TIM_OCNPolarity_High ((uint16_t)0x0000)
#define TIM_OCNPolarity_Low ((uint16_t)0x0008)*/
uint16_t TIM_OCNIdleState; /*空闲状态时互补通道输出电平设置,可选高电平、低电平,设定值必须跟TIM_OCIdleState相反。Value:
#define TIM_OCNIdleState_Set ((uint16_t)0x0200)
#define TIM_OCNIdleState_Reset ((uint16_t)0x0000)*/
} TIM_OCInitTypeDef;
备注:设置TIM_Pulse的值就可以改变输出波形的占空比了,如果不使用重复定时器,那么占空比=(TIM_Pulse+1)/(TIM_Period+1)x100%。
- 定时器输入捕获初始化结构体 >> TIM_ICInitTypeDef
typedef struct
{
uint16_t TIM_Channel; /*输入通道选择,共4个通道。Value:
#define TIM_Channel_1 ((uint16_t)0x0000) //使用 TIM 通道 1
#define TIM_Channel_2 ((uint16_t)0x0004) //使用 TIM 通道 2
#define TIM_Channel_3 ((uint16_t)0x0008) //使用 TIM 通道 3
#define TIM_Channel_4 ((uint16_t)0x000C) //使用 TIM 通道 4*/
uint16_t TIM_ICPolarity; /*输入捕获边沿触发选择,可选上升沿触发、下降沿触发。Value:
#define TIM_ICPolarity_Rising ((uint16_t)0x0000) //TIM 输入捕获上升沿
#define TIM_ICPolarity_Falling ((uint16_t)0x0002) //TIM 输入捕获下降沿*/
uint16_t TIM_ICSelection; /*输入通道选择,共3个通道。Value:
#define TIM_ICSelection_DirectTI ((uint16_t)0x0001) //TIM 输入 2,3 或 4 选择对应地与 IC1 或 IC2 或IC3 或 IC4 相连
#define TIM_ICSelection_IndirectTI ((uint16_t)0x0002) //TIM 输入 2,3 或 4 选择对应地与 IC2 或 IC1 或IC4 或 IC3 相连
#define TIM_ICSelection_TRC ((uint16_t)0x0003) //TIM 输入 2,3 或 4 选择与 TRC 相连*/
uint16_t TIM_ICPrescaler; /*输入捕获通道预分频,共(1、2、4、8)种。Value:
#define TIM_ICPSC_DIV1 ((uint16_t)0x0000) //TIM 捕获在捕获输入上每探测到一个边沿执行一次
#define TIM_ICPSC_DIV2 ((uint16_t)0x0004) // TIM 捕获每 2 个事件执行一次
#define TIM_ICPSC_DIV4 ((uint16_t)0x0008) //TIM 捕获每 4 个事件执行一次
#define TIM_ICPSC_DIV8 ((uint16_t)0x000C) //TIM 捕获每 8 个事件执行一次*/
uint16_t TIM_ICFilter; /*输入捕获滤波器设置,value:0x0~0x0F。一般不用,设置为0*/
} TIM_ICInitTypeDef;
备注:定时器捕获信号,可以测量输入信号的脉宽和测量PWM输入信号的频率和占空比;信号的来源可以来自其他的定时器或者外部引脚;滤波器的作用是排除高频的干扰,采样的频率必须大于等于两倍的输入信号。
3.通用定时器PWM输出例程
#include "tim1.h"
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_GPIOA, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
//GPIO口初始化
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //IO复用推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_6); //输出低
//初始化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 Channel 1 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_OCPolarity_Low
TIM_OCInitStructure.TIM_Pulse = 1000; //比较值
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //定时器通道一的初始化
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能输出比较预装载
TIM_ARRPreloadConfig(TIM2, ENABLE);//使能自动重装载的预装载寄存器允许位
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
通用定时器配置为PWM输出模式时,只需要配置TIM_OCInitStructure.TIM_OCMode、TIM_OCInitStructure.TIM_OutputState、TIM_OCInitStructure.TIM_OCPolarity、TIM_OCInitStructure.TIM_Pulse这4个成员变量。其他的都不用配置。
4.TIM2定时器4路PWM输出例程
uint16_t TIM2_CCR1_Val;
uint16_t TIM2_CCR2_Val;
uint16_t TIM2_CCR3_Val;
uint16_t TIM2_CCR4_Val;
void pwm_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseInitStructure.TIM_Period = 1999;//10KHz
TIM_TimeBaseInitStructure.TIM_Prescaler = 359;
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, & TIM_TimeBaseInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_Pulse = TIM2_CCR1_Val; //设置比较值
TIM_OC1Init(TIM2, &TIM_OCInitStructure); //通道1输出使能
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能预装载器
TIM_OCInitStructure.TIM_Pulse = TIM2_CCR2_Val;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OCInitStructure.TIM_Pulse = TIM2_CCR3_Val;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OCInitStructure.TIM_Pulse = TIM2_CCR4_Val;
TIM_OC4Init(TIM2, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能输出比较预装载
TIM_ARRPreloadConfig(TIM2, ENABLE);//使能自动重装载的预装载寄存器允许位
TIM_Cmd(TIM2, ENABLE);
}
/*调用此函数完成四路占空比实时变化的PWM波*/
void set(uint16_t TIM2_CCR1_Val,uint16_t TIM2_CCR2_Val,uint16_t TIM2_CCR3_Val,uint16_t TIM2_CCR4_Val)
{
TIM_SetCompare2(TIM3,TIM2_CCR1_Val);
TIM_SetCompare2(TIM3,TIM2_CCR2_Val);
TIM_SetCompare2(TIM3,TIM2_CCR3_Val);
TIM_SetCompare2(TIM3,TIM2_CCR4_Val);
}
5.通用定时器输入捕获
void TIM5_Cap_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_ICInitTypeDef TIM5_ICInitStructure; //定时器5通道1输入捕获配置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //使能TIM5时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 清除之前设置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0); //PA0 下拉
//初始化定时器5 TIM5
TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM5输入捕获参数
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 选择输入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
//中断分组初始化
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级2级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级0级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断
TIM_Cmd(TIM5,ENABLE ); //使能定时器5
}
u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态
u16 TIM5CH1_CAPTURE_VAL; //输入捕获值
//定时器5中断服务程序
void TIM5_IRQHandler(void)
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM5CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM5CH1_CAPTURE_VAL=0XFFFF;
}else TIM5CH1_CAPTURE_STA++;
}
}
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
{
if(TIM5CH1_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM_SetCounter(TIM5,0);
TIM5CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获
}
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
}
输入捕获的一般步骤
- 1.初始化定时器和通道对应IO的时钟; 初始化IO口,模式为输入。
- 2.调用函数:GPIO_Init();
- 3.初始化定时器ARR,PSC。调用函数:TIM_TimeBaseInit();
- 4.初始化输入捕获通道。调用函数:TIM_ICInit();
- 5.如果要开启捕获中断。调用函数:TIM_ITConfig();NVIC_Init();
- 6.使能定时器。调用函数:TIM_Cmd();
- 7.编写中断服务函数。调用函数:TIMx_IRQHandler()。
6.通用定时器库函数
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
/*
@arg TIM_IT_Update: TIM update Interrupt source
* @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
* @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
* @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
* @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
* @arg TIM_IT_COM: TIM Commutation Interrupt source
* @arg TIM_IT_Trigger: TIM Trigger Interrupt source
* @arg TIM_IT_Break: TIM Break Interrupt source
*/
7.通用定时器补充
-
1.通用定时器基本介绍
- 通用定时器包括TIM2、TIM3、TIM4和TIM5
- STM32通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。
- 每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作。
- 定时器可以进行定时器基本定时,输出4路PWM,脉冲输入捕获
-
2.输入捕获 (捕获边沿信号,上升沿和下降沿)
- 首先我们需要以一定的频率检测电平的跳变,然后对部分跳变(也就是部分输入的波形)进行过滤------ 这就是定时器里面的滤波器的任务
- 指定输入滤波器时钟频率,首先是系统时钟分给定时器72Mhz,我们首先初始化定时器的时候指定了TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 没有分频,输入给滤波器的时钟频率还是72MHz,TIM_ClockDivision也可以指定为2分频或者4分频
- 波形过滤(TIM_ICFilter),这里有一个指定过滤器的参数(参考芯片手册),例如我们设置参数为0101(二进制),采样频率(fsampling)为 滤波器频率/2 = 36Mhz,N=8.当检测到一个上升沿的时候,再以fsampling频率连续8次检测到高电平才确认是一个有效的上升沿,这样可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤高频波的效果。
- 配置输入分频(TIM_ICPrescaler),如果我们设置不分频,一个边沿(上升沿或者下降沿)就触发一次捕获,二分频就是两次边沿触发捕获,这里这个分频可以为1,2,4,8。