如果第一次接触定时器,可以先看基本定时器。本篇内容较多,如果想直接动手操作,可以跳到后面的实验代码。
stm32标准库对定时器外设建立了4个初始化结构体,定时器分为基本定时器、通用定时器、高级定时器,针对不用的定时器要使用不同初始化结构体。下面是4个初始化结构体的适用分类:
TIM_TimeBaseInitTypeDef (基本定时器、通用定时器、高级定时器)
TIM_OCInitTypeDef (通用定时器、高级定时器)
TIM_ICInitTypeDef (通用定时器、高级定时器)
TIM_BDTRInitTypeDef (高级定时器)
使用哪一个定时器,就配置所有相应的初始化结构体,然后调用初始化函数。下面先理解每个结构体成员的含义和其取值范围。
定时器基本初始化结构体 >> 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输入信号的频率和占空比;信号的来源可以来自其他的定时器或者外部引脚;滤波器的作用是排除高频的干扰,采样的频率必须大于等于两倍的输入信号。
断路和死区初始化结构体 >>TIM_BDTRInitTypeDef
typedef struct
{
uint16_t TIM_OSSRState;/*运行模式下关闭状态选择。Value:
#define TIM_OSSRState_Enable ((uint16_t)0x0800)
#define TIM_OSSRState_Disable ((uint16_t)0x0000)*/
uint16_t TIM_OSSIState;/*空闲模式下关闭状态选择。Value:
#define TIM_OSSIState_Enable ((uint16_t)0x0400)
#define TIM_OSSIState_Disable ((uint16_t)0x0000)*/
uint16_t TIM_LOCKLevel;/*锁定配置。Value:
#define TIM_LOCKLevel_OFF ((uint16_t)0x0000)
#define TIM_LOCKLevel_1 ((uint16_t)0x0100)
#define TIM_LOCKLevel_2 ((uint16_t)0x0200)
#define TIM_LOCKLevel_3 ((uint16_t)0x0300)*/
uint16_t TIM_DeadTime;/*死区时间。Vlaue:0x0~0xFF*/
uint16_t TIM_Break;/*短路输入使能控制。Value:
#define TIM_Break_Enable ((uint16_t)0x1000)
#define TIM_Break_Disable ((uint16_t)0x0000)*/
uint16_t TIM_BreakPolarity;/*断路输出极性。Value:
#define TIM_BreakPolarity_Low ((uint16_t)0x0000)
#define TIM_BreakPolarity_High ((uint16_t)0x2000)*/
uint16_t TIM_AutomaticOutput;/*自动输出使能。Value:
#define TIM_AutomaticOutput_Enable ((uint16_t)0x4000)
#define TIM_AutomaticOutput_Disable ((uint16_t)0x0000)*/
} TIM_BDTRInitTypeDef;
备注:
上图的黑影部分是死区时间,死区时间是根据与输出信号相连接的器件及其特性来调整的,为了避免OCx和OCxN同时改变时,造成外部器来不及反应所造成的短路问题。
下面通过几个实例,进一步理解初始化结构体成员的含义,熟悉定时器的使用的流程。
双通道互补PWM,带死区和刹车功能。
周期:100ms
占空比:通道一为25%,通道二为60%
死区持续时间:12.8ms
采用TIM1的通道一和通道二。
PA8、PB13引脚是TIM1通道一的互补输出。PA9、PB14引脚是TIM1通道二的互补输出。为增加断路功能,需要用到TIM1_BKIN引脚,PB12。
- 定时器IO口配置。
- 配置时基结构体:TIM_TimeBaseInitTypeDef
- 配置输出比较结构体:TIM_OCInitTypeDef
- 配置断路和死区结构体:TIM_BDTRInitTypeDef
- 启动定时器,输出使能。
>>死区持续时间的计算:
TIM_DeadTime的就是配置寄存器DTG[7:0]的值,是定时器时钟,是死区发生器的时钟,DT是死区持续时间。
本实验定时器时钟配置为10kHz,死区持续时间需要配置成12.8ms,我这里选用第二条公式,具体选用那一条公式,由DTG[7:5]这几个bit位决定,如上图。
根据公式 DTG[7:5]=10x => DT=(64+DTG[5:0]) × Tdtg,Tdtg = 2 × TDTS
那么先计算 =2=2*(1/10kHz)=0.2ms
把参数代入公式 DT=(64+DTG[5:0]) × Tdtg,可算出DTG[5:0]=0,即DTG[7:0]=0x80.
创建TIM_test.h
#ifndef __TIM_TEST_H
#define __TIM_TEST_H
#include "stm32f10x.h"
void GPIO_Configuration(void);
void TIME_Configuration(void);
#endif
创建TIM_test.c
void GPIO_Configuration(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA| \
RCC_APB2Periph_GPIOC, ENABLE);//开启GPIOC的外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置引脚模式为通用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
GPIO_Init(GPIOB, &GPIO_InitStructure); //调用库函数,初始化GPIOC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_Init(GPIOA, &GPIO_InitStructure); //调用库函数,初始化GPIOC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_Init(GPIOB, &GPIO_InitStructure); //调用库函数,初始化GPIOC
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
#define ADVANCE_TIM TIM1
void TIME_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //开启TIM1时钟
//时基结构体配置
TIM_TimeBaseStructure.TIM_Period = 1000-1; //从0开始计数 一个周期1000次
TIM_TimeBaseStructure.TIM_Prescaler =(7200-1); //计数器频率为10kHz 定时器时钟:72MHz/7200=10kHz 周期:(1/10kHz)*1000=100ms
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //不需要分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数方式 向上计数
TIM_TimeBaseStructure.TIM_RepetitionCounter=0; //重复计数器
TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure); //调用库函数,初始化TIM1
//输出比较结构体配置
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//选择PWM1模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//信号输出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;//互补信号输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
//初始化TIM1的通道一
TIM_OCInitStructure.TIM_Pulse = 250-1; //占空比=250/1000=25%
TIM_OC1Init(ADVANCE_TIM,&TIM_OCInitStructure);
TIM_OC1PreloadConfig(ADVANCE_TIM,TIM_OCPreload_Enable);
//初始化TIM1通道二
TIM_OCInitStructure.TIM_Pulse = 600-1; //占空比=600/1000=60%
TIM_OC2Init(ADVANCE_TIM,&TIM_OCInitStructure);
TIM_OC2PreloadConfig(ADVANCE_TIM,TIM_OCPreload_Enable);
//死区和短路结构体配置
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
TIM_BDTRInitStructure.TIM_DeadTime = 0x80;
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;//低电平有效,如果引脚检测到高电平则会停止PWM的输出,不会产生任何波形
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(ADVANCE_TIM,&TIM_BDTRInitStructure);
//使能定时器,计数器开始计数
TIM_Cmd(ADVANCE_TIM, ENABLE);
//主动输出使能
TIM_CtrlPWMOutputs(ADVANCE_TIM,ENABLE);
}
创建main.c
#include "stm32f10x.h"
#include “TIM_test.h”
int main(void)
{
GPIO_Configuration(); //IO口配置
TIME_Configuration(); //定时器配置
while(1){
}
}
把程序编译烧录到开发板里面出,再将PA8,PB13引脚和PA9,PB14引脚接到示波器,并把短路输入引脚PB12拉低,示波器和开发板共地。也可以先在keil里面进行软件仿真。用keil4 模拟仿真显示引脚波形输出分析的步骤
软件仿真:
周期:100ms
死区持续时间:12.8ms
占空比(通道一):25%
占空比(通道二):60%
硬件调试:
信号1接的是PB13引脚,信号2接的是PA8引脚,调整示波器到合适的参数。
调试短路功能可以将PB12引脚处接高电平,那么定时器就会停止产生波形,以达到刹车的功能。高级定时器的PWM输出的功能特性天生就是用来控制电机的。
一个高级定时器可以输出多路PWM,但是他们的周期是一样的,占空比可以不一样,在运行过程中每个通道的占空比是可以通过调用函数来改变。通用定时器跟高级定时器的区别是前者没有死区和断路功能,其他都是一样。
水平有限,仅供参考,错误之处以及不足之处还望多多指教。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章