你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32开发项目:硬件定时器(Timer)的配置与使用

[复制链接]
STMCU小助手 发布时间:2022-4-13 17:00
简要介绍
以STM32F103为例,对定时器外设做一个简单的介绍。STM32F103内部共有 8 个定时器,分为基本定时器,通用定时器和高级定时器。基本定时器 TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。通用定时器 TIM2/3/4/5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。高级定时器 TIM1/8是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。

2020092716260459.png

常用配置
设置更新中断

使用Timer产生更新中断时,应当注意通用定时器与高级定时器的几点区别:
1.时钟配置上存在区别(通用定时器、基本定时器与高级定时器的总线不同);
2.基础配置结构体的部分成员仅对高级定时器与通用定时器有效(例如TIM_RepetitionCounter, TIM_ClockDivision, TIM_CounterMode);
3.高级定时器的中断服务函数名(void TIM1_UP_IRQHandler())与通用定时器、基本定时器的中断服务函数名(void TIM2_IRQHandler())不同;
通用定时器(以Timer2为例)
外设配置
  1. #define TIM2_Prescaler                                 7200                                                //10KHz,100us
  2. #define TIM2_PWM_FREQUENCY                        2
  3. #define TIM2_OC2_PWM_DutyCycle                 50                                                        //50%
  4. #define TIM2_Period                                 (72000000.0/TIM2_Prescaler/TIM2_PWM_FREQUENCY)
  5. #define TIM2_OC2_Pulse                                (TIM2_OC2_PWM_DutyCycle*TIM2_Period/100)

  6. void Timer_Config()
  7. {
  8.         TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  9.         
  10.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  11.         TIM_DeInit(TIM2);
  12.         
  13.         TIM_TimeBaseInitStructure.TIM_Prescaler = TIM2_Prescaler - 1;
  14.         TIM_TimeBaseInitStructure.TIM_Period = TIM2_Period - 1;
  15.         TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  16.         TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  17.         TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
  18.         
  19.         TIM_ClearFlag(TIM2, TIM_FLAG_Update);
  20.         TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
  21.         TIM_ARRPreloadConfig(TIM2, ENABLE);
  22. }
复制代码

设置中断优先级
  1. void NVIC_Config()
  2. {
  3.         NVIC_InitTypeDef NVIC_InitStructure;
  4.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

  5.         NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  6.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  7.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  8.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  9.         NVIC_Init(&NVIC_InitStructure);
  10. }
复制代码

中断服务函数
  1. void TIM2_IRQHandler(void)
  2. {
  3.         if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
  4.         {
  5.                 Timer2Updated();
  6.                
  7.                 TIM_ClearFlag(TIM2,TIM_FLAG_Update);
  8.                 TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
  9.         }
  10. }
复制代码

开启定时器
  1.         TIM_Cmd(TIM2, ENABLE);
复制代码

高级定时器(以Timer1为例)
外设配置。
  1. #define TIM1_Prescaler                                 7200                                                //10KHz,100us
  2. #define TIM1_PWM_FREQUENCY                        2
  3. #define TIM1_OC1_PWM_DutyCycle                 50                                                        //50%
  4. #define TIM1_Period                                 (72000000.0/TIM1_Prescaler/TIM1_PWM_FREQUENCY)
  5. #define TIM1_OC1_Pulse                                (TIM1_OC1_PWM_DutyCycle*TIM1_Period/100)

  6. void Timer_Config()
  7. {
  8.         TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  9.         
  10.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
  11.         TIM_DeInit(TIM1);
  12.         
  13.         TIM_TimeBaseInitStructure.TIM_Prescaler = TIM1_Prescaler - 1;
  14.         TIM_TimeBaseInitStructure.TIM_Period = TIM1_Period - 1;
  15.         TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  16.         TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  17.         TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
  18.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
  19.         
  20.         TIM_ClearFlag(TIM1, TIM_FLAG_Update);
  21.         TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
  22.         TIM_ARRPreloadConfig(TIM1, ENABLE);
  23. }
复制代码

设置中断优先级
  1. void NVIC_Config()
  2. {
  3.         NVIC_InitTypeDef NVIC_InitStructure;
  4.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

  5.         NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
  6.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  7.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  8.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  9.         NVIC_Init(&NVIC_InitStructure);
  10. }
复制代码

中断服务函数。注意高级定时器服务函数名称与通用定时器服务函数的区别。
  1. void TIM1_UP_IRQHandler(void)
  2. {
  3.         if(TIM_GetITStatus(TIM1,TIM_IT_Update)!=RESET)
  4.         {
  5.                 Timer1Updated();

  6.                 TIM_ClearFlag(TIM1,TIM_FLAG_Update);
  7.                 TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
  8.         }
  9. }
复制代码

开启定时器
  1.         TIM_Cmd(TIM1, ENABLE);
复制代码

基本定时器(以Timer6为例)
外设配置
  1. #define TIM6_Prescaler                                 7200                                                //10KHz,100us
  2. #define TIM6_PWM_FREQUENCY                        2
  3. #define TIM6_OC1_PWM_DutyCycle                 50                                                        //50%
  4. #define TIM6_Period                                 (72000000.0/TIM6_Prescaler/TIM6_PWM_FREQUENCY)
  5. #define TIM6_OC1_Pulse                                (TIM6_OC1_PWM_DutyCycle*TIM6_Period/100)

  6. void Timer_Config()
  7. {
  8.         TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  9.         
  10.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
  11.         TIM_DeInit(TIM6);
  12.         
  13.         TIM_TimeBaseInitStructure.TIM_Prescaler = TIM6_Prescaler - 1;
  14.         TIM_TimeBaseInitStructure.TIM_Period = TIM6_Period - 1;
  15.         TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStructure);
  16.         
  17.         TIM_ClearFlag(TIM6, TIM_FLAG_Update);
  18.         TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
  19.         TIM_ARRPreloadConfig(TIM6, ENABLE);
  20. }
复制代码

设置中断优先级
  1. void NVIC_Config()
  2. {
  3.         NVIC_InitTypeDef NVIC_InitStructure;
  4.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

  5.         NVIC_InitStructure.NVIC_IRQChannel = TIM6_UP_IRQn;
  6.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  7.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  8.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  9.         NVIC_Init(&NVIC_InitStructure);
  10. }
复制代码

中断服务函数。注意高级定时器服务函数名称与通用定时器服务函数的区别。
  1. void TIM6_IRQHandler(void)
  2. {
  3.         if(TIM_GetITStatus(TIM6,TIM_IT_Update)!=RESET)
  4.         {
  5.                 Timer6Updated();
  6.                
  7.                 TIM_ClearFlag(TIM6,TIM_FLAG_Update);
  8.                 TIM_ClearITPendingBit(TIM6,TIM_IT_Update);
  9.         }
  10. }
复制代码

开启定时器
  1.         TIM_Cmd(TIM6, ENABLE);
复制代码

输出PWM
使用Timer输出PWM时,应当注意通用定时器与高级定时器的几点区别:

1.时钟配置上存在区别(通用定时器与高级定时器的总线不同)
2.基础配置结构体的部分成员仅对高级定时器有效(例如TIM_RepetitionCounter)
3.开启/关闭方波的方法不一样(高级定时器:TIM_CtrlPWMOutputs(TIM1, ENABLE), 通用定时器: TIM_CCxCmd(TIM2, TIM_Channel_1, TIM_CCx_Enable))
4.使用Timer输出PWM时,无需打开中断。当然也可以同时打开定时器的PWM输出与更新中断。

通用定时器(以Timer2为例)
外设配置
  1. #define TIM2_Prescaler                                 720
  2. #define TIM2_PWM_FREQUENCY                        1000
  3. #define TIM2_OC1_PWM_DutyCycle                 50                                                        //50%
  4. #define TIM2_Period                                 (72000000.0/TIM2_Prescaler/TIM2_PWM_FREQUENCY)
  5. #define TIM2_OC1_Pulse                                (TIM2_OC1_PWM_DutyCycle*TIM2_Period/100)

  6. void Timer_Config()
  7. {
  8.         TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  9.         TIM_OCInitTypeDef TIM_OCInitStructure;
  10.         
  11.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  12.         TIM_DeInit(TIM2);
  13.         
  14.         TIM_TimeBaseInitStructure.TIM_Prescaler = TIM2_Prescaler - 1;
  15.         TIM_TimeBaseInitStructure.TIM_Period = TIM2_Period - 1;
  16.         TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  17.         TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  18.         TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
  19.         
  20.         TIM_ARRPreloadConfig(TIM2, ENABLE);
  21.         
  22.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  23.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  24.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  25.         TIM_OCInitStructure.TIM_Pulse = TIM2_OC1_Pulse;
  26.         TIM_OC1Init(TIM2, &TIM_OCInitStructure);
  27.         TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
  28. }
复制代码

方波端口配置
  1. void GPIO_Config()
  2. {
  3.         GPIO_InitTypeDef GPIO_InitStructure;
  4.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  6.         
  7.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  8.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  9.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  10.         GPIO_Init(GPIOE, &GPIO_InitStructure);
  11. }
复制代码

PWM常用操作
  1.         //开启定时器
  2.         TIM_Cmd(TIM2, ENABLE);
  3.         //开启PWM输出
  4.         TIM_CCxCmd(TIM2, TIM_Channel_1, TIM_CCx_Enable);
  5.         //设置占空比(dutyCycle: 0~1)
  6.         TIM_SetCompare1(TIM2, dutyCycle*(TIM2->ARR + 1));
复制代码

高级定时器(以Timer1为例)
外设配置
  1. #define TIM1_Prescaler                                 720
  2. #define TIM1_PWM_FREQUENCY                        1000
  3. #define TIM1_OC1_PWM_DutyCycle                 50                                                        //50%
  4. #define TIM1_Period                                 (72000000.0/TIM1_Prescaler/TIM1_PWM_FREQUENCY)
  5. #define TIM1_OC1_Pulse                                (TIM1_OC1_PWM_DutyCycle*TIM1_Period/100)

  6. void Timer_Config()
  7. {
  8.         TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  9.         TIM_OCInitTypeDef TIM_OCInitStructure;
  10.         
  11.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
  12.         TIM_DeInit(TIM1);
  13.         
  14.         TIM_TimeBaseInitStructure.TIM_Prescaler = TIM1_Prescaler - 1;
  15.         TIM_TimeBaseInitStructure.TIM_Period = TIM1_Period - 1;
  16.         TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  17.         TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  18.         TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
  19.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
  20.         
  21.         TIM_ARRPreloadConfig(TIM1, ENABLE);
  22.         
  23.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  24.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  25.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  26.         TIM_OCInitStructure.TIM_Pulse = TIM1_OC1_Pulse;
  27.         TIM_OC1Init(TIM1, &TIM_OCInitStructure);
  28.         TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
  29. }
复制代码

方波端口配置
  1. void GPIO_Config()
  2. {
  3.         GPIO_InitTypeDef GPIO_InitStructure;
  4.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  6.         
  7.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  8.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  9.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  10.         GPIO_Init(GPIOE, &GPIO_InitStructure);
  11. }
复制代码

PWM常用操作
  1.         //开启定时器
  2.         TIM_Cmd(TIM1, ENABLE);
  3.         //开启PWM输出
  4.         TIM_CtrlPWMOutputs(TIM1, ENABLE);
  5.         //设置占空比(dutyCycle: 0~1)
  6.         TIM_SetCompare1(TIM1, dutyCycle*(TIM1->ARR + 1));
复制代码

输入捕获
通用定时器

通用定时器与高级定时器的输入捕获配置流程基本相同,它们的区别在于:高级定时器的CC中断与Update中断是不同的中断服务函数名(TIM1_UP_IRQHandler, TIM1_CC_IRQHandler),而通用定时器的CC中断与Update中断共享一个中断服务函数名,需要在中断服务函数中进行中断类型的判断。

  1.         if(TIM_GetITStatus(TIM5, TIM_IT_Update) == SET) //捕获到 更新 中断
  2.         {
  3.                
  4.         }
  5.         
  6.         if(TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET)//捕获 1 发生捕获事件
  7.         {
  8.                
  9.         }
复制代码

高级定时器

外设配置
解释一下滤波器的作用:
数字滤波器由一个事件计数器组成,它记录到N个事件后会产生一个输出的跳变。也就是说连续N次采样,如果都是高电平,则说明这是一个有效的触发,就会进入输入捕捉中断(如果设置了的话)。这样就可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤波的作用。
  1. void Timer_Config()
  2. {
  3.         TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  4.         TIM_ICInitTypeDef TIM_ICInitStructure;

  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
  6.         TIM_DeInit(TIM1);
  7.         
  8.         TIM_TimeBaseInitStructure.TIM_Prescaler = TIM1_Prescaler - 1;
  9.         TIM_TimeBaseInitStructure.TIM_Period = TIM1_Period - 1;
  10.         TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  11.         TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  12.         TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
  13.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
  14. //        TIM_ARRPreloadConfig(TIM1, ENABLE);

  15.         TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;
  16.         TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  17.         TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  18.         TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  19.         TIM_ICInitStructure.TIM_ICFilter = 0x08;
  20.         TIM_ICInit(TIM1, &TIM_ICInitStructure);
  21.         
  22.         TIM_ClearFlag(TIM1, TIM_FLAG_CC4 | TIM_IT_Update);
  23.         TIM_ITConfig(TIM1, TIM_IT_CC4| TIM_IT_Update, ENABLE);
  24. }
复制代码

端口配置
以Timer1 Ch4部分重映射到PE14为例,GPIO应当配置为输入模式:
  1.         //Timer1部分重映射  TIM1_CH3->PE13    TIM1_CH4->PE14
  2.         GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE);
  3.         GPIO_ConfigPort('E', 14, GPIO_Mode_IPU, 0);
复制代码

中断配置
配置NVIC的中断优先级:
  1. void NVIC_Config()
  2. {
  3.         NVIC_InitTypeDef NVIC_InitStructure;
  4.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

  5.         NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
  6.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
  7.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  8.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  9.         NVIC_Init(&NVIC_InitStructure);

  10.         NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
  11.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
  12.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  13.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  14.         NVIC_Init(&NVIC_InitStructure);
  15. }
复制代码

编写中断服务函数:

  1. void TIM1_UP_IRQHandler(void)
  2. {
  3.         if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
  4.         {
  5.                 /*Something to do when update IRQ*/
  6.                
  7.                 //TIM_ClearFlag(TIM1,TIM_FLAG_Update);
  8.                 TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
  9.         }
  10. }



  11. void TIM1_CC_IRQHandler(void)
  12. {
  13.         if(TIM_GetITStatus(TIM1, TIM_IT_CC4) != RESET)
  14.         {
  15.                 /*Something to do when CC IRQ*/
  16.                 ///Use function: uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx) to get CC value.

  17.                 //TIM_ClearFlag(TIM1,TIM_FLAG_CC4);
  18.                 TIM_ClearITPendingBit(TIM1,TIM_IT_CC4);
  19.         }
  20. }
复制代码

输出带死区控制的互补PWM
高级定时器

外设配置
  1. #define TIM1_Prescaler                                 720
  2. #define TIM1_PWM_FREQUENCY                        1000
  3. #define TIM1_OC1_PWM_DutyCycle                 50                                                        //50%
  4. #define TIM1_Period                                 (72000000.0/TIM1_Prescaler/TIM1_PWM_FREQUENCY)
  5. #define TIM1_OC1_Pulse                                (TIM1_OC1_PWM_DutyCycle*TIM1_Period/100)

  6. void Timer_Config()
  7. {
  8.         TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  9.         TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
  10.         TIM_OCInitTypeDef TIM_OCInitStructure;

  11.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
  12.         TIM_DeInit(TIM1);
  13.         
  14.         TIM_TimeBaseInitStructure.TIM_Prescaler = TIM1_Prescaler - 1;
  15.         TIM_TimeBaseInitStructure.TIM_Period = TIM1_Period - 1;
  16.         TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  17.         TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  18.         TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
  19.         TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);

  20.         TIM_ARRPreloadConfig(TIM1, ENABLE);                                                                                                                //启用ARR的影子寄存器(直到产生更新事件才更改设置)

  21.         /* Automatic Output enable, Break, dead time and lock configuration*/
  22.         TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;                                //运行模式下输出
  23.         TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;                                //空闲模式下输出选择
  24.         TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;                                //锁定设置,锁定级别1
  25. //        TIM_BDTRInitStructure.TIM_DeadTime = 0xAC;                                                                                        //死区时间3us
  26.         TIM_BDTRInitStructure.TIM_DeadTime = 72;                                                                                                        //死区时间1us
  27.         TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;                                                        //刹车功能使能
  28.         TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;                //刹车输入极性,即刹车控制引脚接GND时,PWM停止
  29.         TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable;        //自动输出使能
  30.         TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);

  31.         /*        刹车控制引脚为TIM1_BKIN pin(PB.12),将PB12接GND,channel和其互补通道,都变为刹车后的电平,具体为0还是1,要看如下两个设置:
  32.          .TIM_OCIdleState = TIM_OCIdleState_Reset;        //刹车之后,PWM通道变为0
  33.          .TIM_OCNIdleState = TIM_OCNIdleState_Reset;        //刹车之后,PWM互补通道变为0

  34.          注意:如果没必要,还是不要开启刹车功能,因为会对PWM产生影响,特别是当PB12悬空时,波形将会有很大的波动。
  35.          这里不打开刹车功能,即.TIM_Break = TIM_Break_Disable;
  36.          */

  37.         /*
  38.          正确的deadtime的计算方法(经理论与示波器测试成功)
  39.          TIM_BDTRInitStructure.TIM_DeadTime=255 //这句设定的就是寄存器TIMx_BDTR的后8位,即DTG[7:0],所以最大值为255
  40.          从下面的代码中的“第五步”中,实际上就相当于TIM1->BDTR=0x71FF;

  41.          查看"STM32中文参考手册2009.pdf"的TIMx_BDTR(第248页),列寄存器TIMx_BDTR的后8位如下:
  42.          位7:0        UTG[7:0]: 死区发生器设置 (Dead-time generator setup)
  43.          这些位定义了插入互补输出之间的死区持续时间。假设DT表示其持续时间:
  44.          DTG[7:5]=0xx => DT=DTG[7:0] × Tdtg,                Tdtg = Tdts;
  45.          DTG[7:5]=10x => DT=(64+DTG[5:0]) × Tdtg,        Tdtg = 2 × Tdts;
  46.          DTG[7:5]=110 => DT=(32+DTG[4:0]) × Tdtg,        Tdtg = 8 × Tdts;
  47.          DTG[7:5]=111 => DT=(32+DTG[4:0]) × Tdtg,        Tdtg = 16× Tdts;

  48.          Tdts为系统时钟周期时长,Tdtg为死区时间计算步长。主要思想就是把DTG的八位,掰成两半用。一半决定步长,另一半是与步长相乘的乘数,乘数可以自行设定,步长*乘数=死区时间。

  49.          在72M的定时器时钟下,TDTS = 1/72M = 13.89ns:
  50.          项目                                                 情况1                         情况2                         情况3                         情况4

  51.          步长位置                                         DTG[7]                         DTG[7:6]                 DTG[7:5]                 DTG[7:5]
  52.          步长值(二进制)                         0xx                         10x                         110                         111

  53.          步长是周期几倍                                 1                                2                                 8                                 16
  54.          乘数位置                                         DTG[6:0]                 DTG[5:0]                 DTG[4:0]                 DTG[4:0]
  55.          乘数最大值                                         127                         64+63                         63+31                         32+31
  56.          乘数范围                                         0~127                         64~127                         32~63                         34~63
  57.          等价几倍周期                                 0~127                         128~254                 256~504                         512~1008

  58.          周期13.89ns时,死区范围ns         0~1764                 1778~3528         3556~7000         7112~14001


  59.          示例:
  60.          需要3us的死区时间,那么属于情况2,DTG[7:6] = 0b10,步长=27.78,
  61.          需要的乘数 = 3000÷27.78-64=108-64=44=0b101100,DTG[7:0]=0b10101100=0xAC
  62.          */

  63.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                                       //CH2 PWM2模式

  64.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;                                    //比较输出使能
  65.         TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;                     //比较互补输出使能

  66.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;                                                //输出极性
  67.         TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;                                         //互补输出极性

  68.         TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;                                        //指定空闲状态下的TIM输出比较的引脚状态。
  69.         TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;                                        //指定空闲状态下的TIM互补输出比较的引脚状态。

  70.         TIM_OCInitStructure.TIM_Pulse = TIM1_OC1_Pulse;

  71.         TIM_OC1Init(TIM1, &TIM_OCInitStructure);                                                                                                          //根据指定的参数初始化外设TIMx
  72.         TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);                                                                        //启用CCR1寄存器的影子寄存器(直到产生更新事件才更改设置)

  73.         //OCx输出信号与参考信号相同,只是它的上升沿相对参考信号的上升沿有一个延迟
  74.         //OCxN输出信号与参考信号相同,只是它的上升沿相对参考信号的下降沿有一个延迟
  75. }
复制代码

方波端口配置
  1. void GPIO_Config()
  2. {
  3.         GPIO_InitTypeDef GPIO_InitStructure;
  4.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  6.         
  7.         //Timer1 Ch1 - PA8
  8.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  9.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  10.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  11.         GPIO_Init(GPIOA, &GPIO_InitStructure);

  12.         //Timer1 Ch1N - PB13
  13.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
  14.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  15.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  16.         GPIO_Init(GPIOB, &GPIO_InitStructure);
  17. }
复制代码

PWM常用操作
  1.         //开启定时器
  2.         TIM_Cmd(TIM1, ENABLE);
  3.         //开启PWM输出
  4.         TIM_CtrlPWMOutputs(TIM1, ENABLE);
  5.         //设置占空比(dutyCycle: 0~1)
  6.         TIM_SetCompare1(TIM1, dutyCycle*(TIM1->ARR + 1));
复制代码

使用指南
实用Timer设置函数(以Timer时钟72M为例)
设置PWM频率

  1. /**
  2. * @brief Set the frequency of PWM
  3. *                 需要特别注意的是,考虑到PWM占空比设置精度,本函数实现的PWM调节最大频率为1MHz。
  4. *                 对于固定频率的PWM,可以参考本函数的实现方法,
  5. *                 选择合适的分频系数手动设置分频系数,以保证最大的PWM占空比调整精度。
  6. *
  7. *                 注意1: 在STM32F407中,高级定时器的时钟频率与通用定时器的时钟频率是不一样的,
  8. *                 需要修改本函数(增加对定时器类型的判断)才能正确的设置PWM频率。
  9. *
  10. *                 注意2: 只有高级定时器与通用定时器才能产生PWM。
  11. * @param TIMx: 高级定时器与通用定时器TIM 1, 2, 3, 4, 5 and 8(在STM32F103中,定时器的时钟频率可以都设置为72MHz)
  12. * @param freq: 0.2~1MHz
  13. */
  14. void User_PWM_SetFrequency(TIM_TypeDef *TIMx, float freq)
  15. {
  16.         //duty cycle of 4 channels
  17.         float dutyCycle1;
  18.         float dutyCycle2;
  19.         float dutyCycle3;
  20.         float dutyCycle4;
  21.         
  22.         //Range of frequency is 0.2Hz to 1MHz.
  23.         //参数不正确,直接返回
  24.         if (freq > 1000000)
  25.                 return;
  26.         if (freq < 0.2)
  27.                 return;

  28.         //根据频率设置Timer的分频系数
  29.         //0.2 <= frequency < 100, psc = 7200, f = 10KHz
  30.         //占空比设置精度为1/50000~1/100
  31.         if(freq < 100)
  32.         {
  33.                 TIMx->PSC = 7200 - 1;
  34.         }
  35.         //100 <= frequency < 1k, psc = 720, f = 100KHz
  36.         //占空比设置精度为1/1000~1/100
  37.         else if(freq < 1000)
  38.         {
  39.                 TIMx->PSC = 720 - 1;
  40.         }
  41.         //1K <= frequency < 10K, psc = 72, f = 1MHz
  42.         //占空比设置精度为1/1000~1/100
  43.         else if(freq < 10000)
  44.         {
  45.                 TIMx->PSC = 72 - 1;
  46.         }
  47.         //10K <= frequency < 100K, psc = 6, f = 12MHz
  48.         //占空比设置精度为1/1200~1/120
  49.         else if(freq < 100000)
  50.         {
  51.                 TIMx->PSC = 6 - 1;
  52.         }
  53.         //100K <= frequency <= 1M, psc = 1, f = 72MHz
  54.         //占空比设置精度为1/720~1/72
  55.         else if(freq <= 1000000)
  56.         {
  57.                 TIMx->PSC = 1 - 1;
  58.         }
  59.         else
  60.         {

  61.         }

  62.         //Get the duty cycle of the pwm before changing the frequency.
  63.         dutyCycle1 = (float) TIMx->CCR1 / (TIMx->ARR + 1);
  64.         dutyCycle2 = (float) TIMx->CCR2 / (TIMx->ARR + 1);
  65.         dutyCycle3 = (float) TIMx->CCR3 / (TIMx->ARR + 1);
  66.         dutyCycle4 = (float) TIMx->CCR4 / (TIMx->ARR + 1);

  67.         //Set the update frequency of the timer.
  68.         TIMx->ARR = 72000000.0 / (TIMx->PSC + 1) / freq - 1;

  69.         //Set the duty cycle of the timer.
  70.         TIMx->CCR1 = dutyCycle1 * (TIMx->ARR + 1);
  71.         TIMx->CCR2 = dutyCycle2 * (TIMx->ARR + 1);
  72.         TIMx->CCR3 = dutyCycle3 * (TIMx->ARR + 1);
  73.         TIMx->CCR4 = dutyCycle4 * (TIMx->ARR + 1);
  74. }
复制代码

设置PWM占空比
  1. /**
  2. * @brief Set the duty cycle of PWM
  3. * @param TIMx: 高级定时器与通用定时器TIM 1, 2, 3, 4, 5 and 8
  4. * @param channel: 0,1,2,3, 高级定时器与通用定时器有4个PWM通道
  5. * @param dutyCycle: 0~1 (需要注意PWM频率不同时,可以设置的占空比精度是不一样的)
  6. */
  7. void User_PWM_SetDutyCycle(TIM_TypeDef *TIMx, uint8_t channel, float dutyCycle)
  8. {
  9.         //Range of duty cycle is 0 to 1.
  10.         //参数不正确,直接返回
  11.         if (dutyCycle > 1)
  12.                 return;
  13.         if (dutyCycle < 0)
  14.                 return;

  15.         //Set the duty cycle of the PWM.
  16.         switch (channel)
  17.         {
  18.         case 0:
  19.                 //TIM_SetCompare1(TIMx, dutycycle * (TIMx->ARR + 1));
  20.                 TIMx->CCR1 = dutyCycle * (TIMx->ARR + 1);
  21.                 break;
  22.         case 1:
  23.                 //TIM_SetCompare2(TIMx, dutycycle * (TIMx->ARR + 1));
  24.                 TIMx->CCR2 = dutyCycle * (TIMx->ARR + 1);
  25.                 break;
  26.         case 2:
  27.                 //TIM_SetCompare3(TIMx, dutycycle * (TIMx->ARR + 1));
  28.                 TIMx->CCR3 = dutyCycle * (TIMx->ARR + 1);
  29.                 break;
  30.         case 3:
  31.                 //TIM_SetCompare4(TIMx, dutycycle * (TIMx->ARR + 1));
  32.                 TIMx->CCR4 = dutyCycle * (TIMx->ARR + 1);
  33.                 break;
  34.         default:
  35.                 break;
  36.         }
  37. }
复制代码

更新中断频率
设置Timer的更新中断与设置PWM频率是类似的,因为PWM的频率即为更新中断的频率,它们的区别在于:
1.设置更新中断频率时无需设置CCR1~4(PWM的占空比)的值
2.基本定时器可以设置更新中断频率,但是不能设置PWM的频率

  1. /**
  2. * @brief Set the update frequency of timer
  3. *                 本函数实现的最大更新中断频率为1MHz。
  4. *
  5. *                 注意1: 在STM32F407中,高级定时器的时钟频率与通用定时器的时钟频率是不一样的,
  6. *                 需要修改本函数(增加对定时器类型的判断)才能正确的设置更新中断的频率。
  7. *
  8. *                 注意2: 基本定时器、通用定时器、高级定时器都可以设置更新中断的频率。
  9. * @param TIMx: 基本定时器、通用定时器、高级定时器TIM 1~8(在STM32F103中,定时器的时钟频率可以都设置为72MHz)
  10. * @param freq: 0.2~1MHz
  11. */
  12. void User_Timer_SetUpdateFrequency(TIM_TypeDef *TIMx, float freq)
  13. {
  14.         //Range of frequency is 0.2Hz to 1MHz.
  15.         //参数不正确,直接返回
  16.         if (freq > 1000000)
  17.                 return;
  18.         if (freq < 0.2)
  19.                 return;

  20.         //根据频率设置Timer的分频系数
  21.         //0.2 <= frequency < 100, psc = 7200, f = 10KHz
  22.         //占空比设置精度为1/50000~1/100
  23.         if(freq < 100)
  24.         {
  25.                 TIMx->PSC = 7200 - 1;
  26.         }
  27.         //100 <= frequency < 1k, psc = 720, f = 100KHz
  28.         //占空比设置精度为1/1000~1/100
  29.         else if(freq < 1000)
  30.         {
  31.                 TIMx->PSC = 720 - 1;
  32.         }
  33.         //1K <= frequency < 10K, psc = 72, f = 1MHz
  34.         //占空比设置精度为1/1000~1/100
  35.         else if(freq < 10000)
  36.         {
  37.                 TIMx->PSC = 72 - 1;
  38.         }
  39.         //10K <= frequency < 100K, psc = 6, f = 12MHz
  40.         //占空比设置精度为1/1200~1/120
  41.         else if(freq < 100000)
  42.         {
  43.                 TIMx->PSC = 6 - 1;
  44.         }
  45.         //100K <= frequency <= 1M, psc = 1, f = 72MHz
  46.         //占空比设置精度为1/720~1/72
  47.         else if(freq <= 1000000)
  48.         {
  49.                 TIMx->PSC = 1 - 1;
  50.         }
  51.         else
  52.         {

  53.         }

  54.         //Set the update frequency of the timer.
  55.         TIMx->ARR = 72000000.0 / (TIMx->PSC + 1) / freq - 1;
  56. }
复制代码

设置死区时间
  1. /**
  2. * @brief Set the dead time of PWM
  3. * @param deatTime: 0~14 us
  4. */
  5. void User_PWM_SetDeadTime(uint8_t deadTime)
  6. {
  7.         /*
  8.          正确的deadtime的计算方法(经理论与示波器测试成功)
  9.          TIM_BDTRInitStructure.TIM_DeadTime=255 //这句设定的就是寄存器TIMx_BDTR的后8位,即DTG[7:0],所以最大值为255
  10.          从下面的代码中的“第五步”中,实际上就相当于TIM1->BDTR=0x71FF;

  11.          查看"STM32中文参考手册2009.pdf"的TIMx_BDTR(第248页),列寄存器TIMx_BDTR的后8位如下:
  12.          位7:0        UTG[7:0]: 死区发生器设置 (Dead-time generator setup)
  13.          这些位定义了插入互补输出之间的死区持续时间。假设DT表示其持续时间:
  14.          DTG[7:5]=0xx => DT=DTG[7:0] × Tdtg,                Tdtg = Tdts;
  15.          DTG[7:5]=10x => DT=(64+DTG[5:0]) × Tdtg,        Tdtg = 2 × Tdts;
  16.          DTG[7:5]=110 => DT=(32+DTG[4:0]) × Tdtg,        Tdtg = 8 × Tdts;
  17.          DTG[7:5]=111 => DT=(32+DTG[4:0]) × Tdtg,        Tdtg = 16× Tdts;

  18.          Tdts为系统时钟周期时长,Tdtg为死区时间计算步长。主要思想就是把DTG的八位,掰成两半用。一半决定步长,另一半是与步长相乘的乘数,乘数可以自行设定,步长*乘数=死区时间。

  19.          在72M的定时器时钟下,TDTS = 1/72M = 13.89ns:
  20.          项目                                                         情况1                         情况2                         情况3                         情况4

  21.          步长位置                                                 DTG[7]                         DTG[7:6]                 DTG[7:5]                 DTG[7:5]
  22.          步长值(二进制)                                 0xx                         10x                         110                         111

  23.          步长是周期几倍                                         1                                 2                                 8                                 16

  24.          Step Value (Tdts = 13.89ns)        1/72M                        1/36M                        1/9M                        2/9M
  25.          Step Value (Tdts = 13.89ns)        13.89ns                        27.78ns                        111.11ns                222.22ns

  26.          乘数位置                                                 DTG[6:0]                 DTG[5:0]                 DTG[4:0]                 DTG[4:0]
  27.          乘数最大值                                                 0+127                         64+63                         32+31                         32+31
  28.          乘数范围                                                 0~127                         64~127                         32~63                         32~63
  29.          等价几倍周期                                         0~127                         128~254                 256~504                         512~1008

  30.          周期13.89ns时,死区范围ns                 0~1764                         1778~3528                 3556~7000                 7112~14001


  31.          示例:
  32.          需要3us的死区时间,那么属于情况2,DTG[7:6] = 0b10,步长=27.78,
  33.          需要的乘数 = 3000÷27.78-64=108-64=44=0b101100,DTG[7:0]=0b10101100=0xAC
  34.          */
  35.         uint16_t _deadTime = 0;

  36.         if (deadTime > 14)
  37.                 return;
  38.         //7<deadTime<=14, deadTime = 8, 9, 10, 11, 12, 13, 14
  39.         else if (deadTime > 7)
  40.         {
  41.                 _deadTime = 0xE0 | (uint8_t) (deadTime * 100000 / 22222 - 32);
  42.         }
  43.         //3<deadTime<=7, deadTime = 4, 5, 6, 7
  44.         else if (deadTime > 3)
  45.         {
  46.                 _deadTime = 0xC0 | (uint8_t) (deadTime * 100000 / 11111 - 32);
  47.         }
  48.         //1<deadTime<=3, deadTime = 2, 3
  49.         else if (deadTime > 1)
  50.         {
  51.                 _deadTime = 0x80 | (uint8_t) (deadTime * 100000 / 2778 - 64);
  52.         }
  53.         //deadTime = 0, 1
  54.         else
  55.         {
  56.                 _deadTime = 0x00 | (uint8_t) (deadTime * 100000 / 1389 - 0);
  57.         }

  58.         TIM1->BDTR = (0x0C00 | _deadTime);

  59.         //Dead time setting is validated after restart the PWM.
  60.         //        User_PWM_Open(0);
  61.         //        User_PWM_Open(1);
  62. }
复制代码

项目实例
BLDC电机速度闭环控制

笔者参与的BLDC电机速度闭环控制开发项目中使用了定时器的输入捕获功能,通过测量霍尔传感器产生的脉冲宽度计算电机转速。定时器等相关外设的配置上文有介绍,本章节重点介绍中断服务函数的实现。

声明用于记录脉冲计数的全局变量
  1. uint32_t BLDCMotor_PulseCount = 0;
复制代码

在定时器的更新中断中将此全局变量复位
其作用相当于超出测试量程后将速度计算值复位为0。

  1. void TIM1_UP_IRQHandler(void)
  2. {
  3.         if(TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
  4.         {
  5.                 BLDCMotor_PulseCount = 0;

  6.                 TIM_ClearFlag(TIM1,TIM_FLAG_Update);
  7.                 TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
  8.         }
  9. }
复制代码

在定时器的输入捕获中断服务函数中计算脉冲宽度
为了提高输入捕获的精度,应该尽可能减小服务函数的执行时间。多数情况下应该是调用函数TIM_GetCaptureX获取对应通道的计数值,然后将连续两次调用的计数值相减得到脉冲计数。为了提高效率,本项目中的服务函数直接将计数值清零,下次获取的计数值将直接等于脉冲计数。

  1. void TIM1_CC_IRQHandler(void)
  2. {
  3.         if(TIM_GetITStatus(TIM1, TIM_IT_CC4) != RESET)
  4.         {
  5.                 /**
  6.                  * Pulse capture has strict requirements on time,
  7.                  * so it needs to operate the register directly.
  8.                  */
  9.                 BLDCMotor_PulseCount = Filter_MovingAverage_VariantSize(TIM1->CCR4, MOSP_FMA_INDEX, 5);

  10.                 TIM1->CNT = 0;
  11.                 TIM1->SR = (uint16_t)~TIM_FLAG_CC4;

  12.                 //                Timer4_IT_CC1_Triggered();
  13.                 //                TIM_ClearFlag(TIM1,TIM_FLAG_CC4);
  14.                 //                TIM_ClearITPendingBit(TIM1,TIM_IT_CC4);
  15.         }
  16. }
复制代码

脉冲长度与电机转速的转化
霍尔传感器的脉冲宽度以定时器的时钟计数量表示,首先要将它转换为时间/频率的单位。脉冲频率与电机转速的比例是一个受直流无刷电机磁极子个数影响的固定参数,同时考虑到可能存在的减速机构改变了传动比,因此设置了两个可以调整的参数用于计算最终的电机转速。

  1. float BLDC_PulseToSpeed(uint32_t pulse)        //unit: rpm
  2. {
  3.         float frequency;

  4.         if (pulse == 0)
  5.                 return 0;

  6.         //验证计算参数是否有效
  7.         if (HoldingReg_GetData(3) == 0 || HoldingReg_GetData(4) == 0)
  8.         {
  9.                 return -1;
  10.         }

  11.         frequency = (float) SystemCoreClock / (TIM1_Prescaler * pulse);

  12.         return frequency * 60 / HoldingReg_GetData(3) / HoldingReg_GetData(4);        //unit: rpm
  13. }
复制代码




收藏 评论0 发布时间:2022-4-13 17:00

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版