STM32
直播中

张亮

7年用户 1313经验值
私信 关注
[问答]

STM32通用定时器TIM2~TIM5中断函数该怎样去编写呢

STM32通用定时器tiM2~TIM5主要有哪几个功能呢?
STM32通用定时器TIM2~TIM5中断函数该怎样去编写呢?

回帖(1)

张毕鹄

2021-11-24 09:38:09
  通用定时器TIM2~5
  引脚定义
  TIM2_CH1------PA0
  TIM2_CH2------PA1
  TIM2_CH3------PA2
  TIM2_CH4------PA3
  TIM3_CH1------PA6
  TIM3_CH2------PA7
  TIM3_CH3------PB0
  TIM3_CH4------PB1
  对于通用定时器主要有三个功能:
  基本的定时器功能,和基本定时器相同
  PWM脉冲输出
  测量输入脉冲的频率和脉冲宽度
  基本定时器功能
  TIM2~5的基本定时器功能与基本定时器TIM6和TIM7类似,不过不同的是,对于基本定时器中的计数器只能向上计数,而通用定时器的计数器可以配置计数模式向上或者向下。
  向上计数模式:从零开始向上计数,当达到TIMx_ARR中的值时,计数器清零,然后产生一个计数器溢出中断
  向下计数模式:从TIMx_ARR中的数值开始向下计数,当计数器中数为0时,自动将TIMx_ARR中的数重装入计数器,同时产生一个向下溢出事件。
  PWM脉冲输出
  对应于上面的GPIO口,将对应的GPIO配置为推挽复用输出。对于TIM的配置主要包括两个结构TIM_TimeBaseInitTypeDef和TIM_OCInitTypeDef,对于第一个结构体就是配置基本定时器时所使用的,对于第二结构体是输出捕获(Output Capture),只要用来配置输出PWM的占空比,高低电平等信息。
  对于TIM_TimeBaseInitTypeDef的配置
  类似于基本定时器的配置:
  .TIM_Period:定时的周期,该值存储在ARR中,计数器从0自增到该值,或者从该值自减到0
  .TIM_Prescaler:对TIMxCLK的预分频系数,分频后作为计数器的驱动时钟, 驱 动 时 钟 = T I M x C L K / ( 分 频 系 数 + 1 ) 驱动时钟=TIMxCLK/(分频系数+1) 驱动时钟=TIMxCLK/(分频系数+1)
  .TIM_ClockDivision:时钟分频因子,这个与上一个不同,这个也是对TIMxCLK进行分频,但分频后的时钟被输送到定时器的ETRP数字滤波器,会影响滤波器的采样频率。ETRP数字滤波器是对外部时钟TIMxETR进行滤波。一般使用内部时钟,该值没有任何影响。
  .TIM_CounterMode:配置计数器的技术模式,常用的是向上计数或者向下计数。
  具体的配置,如下代码:
  tim_s.TIM_ClockDivision = TIM_CKD_DIV1;
  tim_s.TIM_CounterMode = TIM_CounterMode_Up;
  tim_s.TIM_Period = 9999;
  tim_s.TIM_Prescaler = 0;
  TIM_TimeBaseInit(TIM2,&tim_s);
  对于TIM_OCInitTypeDef的配置
  对于TIM_OCInitTypeDef结构体在通用定时器中的用处来看,就是配置输出PWM的相关信息:
  .TIM_OCMode:配置PWM输出的模式,总共有两种模式PWM1和PWM2,。
  PWM1:向上计数时,当计数器中的数值小于CCR中的数值(也就是后面配置的TIM_Pulse的值)时输出为有效电平,当计数器中的数值大于CCR中的数值且小于ARR中的数值时,输出为无效电平。向下计数时,计数器中的数值大于CCR中的值输出为无效电平。
  PWM2:向上计数时,当计数器中的数值小于CCR中的数值(也就是后面配置的TIM_Pulse的值)时输出为无效电平,当计数器中的数值大于CCR中的数值且小于ARR中的数值时,输出为有效电平。向下计数时,计数器中的数值大于CCR中的值输出为有效电平。
  .TIM_OutputState:配置输出模式的状态,使能输出。
  .TIM_OCPolarity:(polarity n.极性)该配置有效电平的极性,可以将有效电平配置高电平,也可以配置为低电平。
  .TIM_Pulse:该参数记为CCR中的数值,由它来控制PWM的占空比。
  具体的配置代码如下:
  oc_s.TIM_OCMode = TIM_OCMode_PWM1;
  oc_s.TIM_OutputState = TIM_OutputState_Enable;
  oc_s.TIM_OCPolarity = TIM_OCPolarity_High;
  oc_s.TIM_Pulse = 1000;
  TIM_OC1Init(TIM2,&oc_s);
  对于同一个定时器的不同通道需要单独初始化TIM_OCxInit(TIMx,&oc_s)
  并且对于每个通道初始化之后,需要将CCR中的值存入寄存器TIM_OCxPreloadConfig(TIMx,TIM_OCPreload_Enable)
  在开启TIM之前需要将ARR的也存入寄存器TIM_ARRPreloadConfig(TIMx,ENABLE)
  对于PWM输出的TIM完整配置代码如下:
  void TIM2_Config(void)
  {
  TIM_TimeBaseInitTypeDef tim_s;
  TIM_OCInitTypeDef oc_s;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
  oc_s.TIM_OCMode = TIM_OCMode_PWM1;
  oc_s.TIM_OutputState = TIM_OutputState_Enable;
  oc_s.TIM_OCPolarity = TIM_OCPolarity_High;
  oc_s.TIM_Pulse = 1000;
  TIM_OC1Init(TIM2,&oc_s);
  TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable);
  oc_s.TIM_Pulse = 8000;
  TIM_OC2Init(TIM2,&oc_s);
  TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);
  TIM_ARRPreloadConfig(TIM2,ENABLE);
  TIM_Cmd(TIM2,ENABLE);
  }
  测量输入脉冲的频率和脉冲宽度
  对于测量输入脉冲的频率和脉冲宽度,需要配置对应通道的GPIO。将对应的GPIO配置为下拉输入。对于输入脉冲测量主要配置两个结构体和书写中断函数。具体的工作过程,假设使用通道1来进行脉冲测量,首先在配置中将捕获设为上升沿捕获,则当捕获到上升沿时,触发中断,在中断中将触发方式改为下降沿触发,并将计数器中的数清0。当下降沿到来,再次触发中断,计数器中的数自动存入到CCR中,则在中断中将触发方式再次更改为上升沿触发,且可以读出CCR中的值即为输入PWM的高电平时间。当上升沿再次到来,计数器中的数自动存入到CCR中,并触发中断,在中断中将CCR的值读出即为PWM的周期(以上的过程,默认为PWM的周期小于定时器溢出的时间)
  配置两个结构体
  在测量输入脉冲时需要配置TIM_TimeBaseInitTypeDef和TIM_ICInitTypeDef,对于TIM_TimeBaseInitTypeDef来说和之前的PWM配置相似,不同的是关于预分频系数和周期的配置,这个按照需要自己配置。
  TIM_ICInitTypeDef的配置
  .TIM_Channel:进行采用的通道,例TIM_Channel_3。
  .TIM_ICPolarity:触发中断的电平,可以设置为上升沿,也可设置成下降沿。
  .TIM_ICPrescaler:输入分频器,对输入的PWM可以进行相应的分频,例如2分频,就是两个周期采样一次。(个人理解)
  .TIM_ICFilter:输入的滤波器,没用,设为0x00
  .TIM_ICSeclection:设置是否直连如下图中对于TI1来说,后面有TIFP1和TIFP2,对于TIFP1是直接连,对于TIFP2是间接连接。
  #FormatImgID_0#
  具体的配置代码:
  ic_s.TIM_Channel = TIM_Channel_3;
  ic_s.TIM_ICPolarity = TIM_ICPolarity_Rising;
  ic_s.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  ic_s.TIM_ICFilter = 0x00;
  ic_s.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInit(TIM2,&ic_s);
  在结构配置完成以后,需要对中断控制器进行配置:
  void NVIC_Config(void)
  {
  NVIC_InitTypeDef nvic_s;
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  nvic_s.NVIC_IRQChannel = TIM2_IRQn;
  nvic_s.NVIC_IRQChannelPreemptionPriority = 0;
  nvic_s.NVIC_IRQChannelSubPriority = 2;
  nvic_s.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&nvic_s);
  }
  配置完成以后打开中断TIM_ITConfig(TIM2,TIM_IT_Update | TIM_IT_CC3|TIM_IT_CC4,ENABLE);
  TIM_IT_Update:计数器溢出中断
  TIM_IT_CC3:通道3的捕获中断
  TIM_IT_CC4:通道4的捕获中断
  中断函数的编写
  对于中断函数的编写流程如下(以上的过程,默认为PWM的周期小于定时器溢出的时间):
  首次上升沿捕获成功触发中断,进入中断,在中断中使用TIM_SetCounter(TIMx,0)将对应的定时器中计数器清0.然后将对应的通道设置为下降沿捕获TIM_OCnPolarityConfig(TIMx,TIM_ICPolarity_Falling);,同时需要标记上升沿第一次来。对应通道捕获到下降沿,自动的将计数器CNT中的数值赋值到CCR中,同时触发中断,在中断中可以将CCR的值读出,该值就是所测波形的高电平时间,然后再将该通道设置为上升沿捕获。再次捕获到上升沿,判断是否是第二次捕获上升沿,如果是就将CCR中的值读出,该值是第一个上升沿到第二个上升沿的时间,如果是周期性PWM的话,该值就是PWM的周期时间。如果不是第二次捕获,就重复之前第一捕获的流程。
  具体的代码为:
  void TIM2_IRQHandler(void)
  {
  if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)//计数器溢出中断判断
  {
  }
  if(TIM_GetITStatus(TIM2,TIM_IT_CC3) == SET)//定时器通道3捕获中断判断
  {
  if(C3_flag == 0)// 第一次上升沿到来
  {
  TIM_SetCounter(TIM2,0);
  TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Falling);
  C3_flag = 1;
  }
  else if(C3_flag == 1)//第一次下降沿到来
  {
  c3_high = (uint32_t)TIM_GetCapture3(TIM2);
  TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Rising);
  C3_flag = 2;
  }
  else if(C3_flag == 2)//第二次上升沿到来
  {
  c3_proide = (uint32_t)TIM_GetCapture3(TIM2);
  C3_flag = 0;
  }
  }
  TIM_ClearITPendingBit(TIM2,TIM_IT_Update | TIM_IT_CC3|TIM_IT_CC4);
  }
  注意到在上面提到两次PWM的周期小于定时器的溢出时间,是为中断函数的编写和便于把原理叙述清楚,如果没有这个假设,那就要考虑所有的情况
  所有的情况大致有以下几类
  PWM周期 = 定时器的溢出时间:此时对于上升沿的读取时间是没有问题的,对于周期的读取值会是0.
  PWM周期 》定时器的溢出时间:此时又分为两种情况:定时器溢出发生在上升沿和下降沿之间,或者发生在下降沿和上升沿之间。这种情况因为发生定时器溢出时,计数器会被清0,所以需要统计定时器溢出的次数,在最后将对应的时间加上(计数器溢出次数*计数器溢出时间。)
举报

更多回帖

发帖
×
20
完善资料,
赚取积分