stm32之TIM-高级定时器应用实例一(详细)
阅读原文时间:2021年04月22日阅读:1
  • 硬件:stm32f103c8t6
  • 开发工具:Keil uVision4
  • 下载调试工具:ARM仿真器

        如果第一次接触定时器,可以先看基本定时器。本篇内容较多,如果想直接动手操作,可以跳到后面的实验代码。

        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互补输出实验(带死区和断路功能)

一. 设计要求:

双通道互补PWM,带死区和刹车功能。
周期:100ms
占空比:通道一为25%,通道二为60%
死区持续时间:12.8ms  

二. 硬件设计:

 采用TIM1的通道一和通道二。

        PA8、PB13引脚是TIM1通道一的互补输出。PA9、PB14引脚是TIM1通道二的互补输出。为增加断路功能,需要用到TIM1_BKIN引脚,PB12。

三. 设计步骤:

  1. 定时器IO口配置。
  2. 配置时基结构体:TIM_TimeBaseInitTypeDef
  3. 配置输出比较结构体:TIM_OCInitTypeDef
  4. 配置断路和死区结构体:TIM_BDTRInitTypeDef
  5. 启动定时器,输出使能。

>>死区持续时间的计算:

        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,但是他们的周期是一样的,占空比可以不一样,在运行过程中每个通道的占空比是可以通过调用函数来改变。通用定时器跟高级定时器的区别是前者没有死区和断路功能,其他都是一样。

水平有限,仅供参考,错误之处以及不足之处还望多多指教。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章