1. 定时器基础

1.1 Counter Settings

Prescaler(16bit)分频值:将定时器之中频率分频。

Counter Period : 周期计数值,按照分频后的时间进行计数。

1.2 Channel Init

Mode :模式选择

1)Mode = Toggele on match(即定时器ARR寄存器周期计数值溢出就翻转电平)

Pulse : 电平跳变值。通过定时器计数,计数到Pulse时,定时电平产生反转。

CH Polarity : 初始电平

1.6 计算公式

cubemax PWM配置_cubemax PWM配置

 步进电机旋转频率:

cubemax PWM配置_回调函数_02

我们使用STM32F407,想要产生800Hz频率应当配置定时器如下:

cubemax PWM配置_IT_03

2. 定时器中断

2.1 应用场景

每到定时器计数值满进入定时器中断,执行相应的操作。

目的:让单片机输出一段可调频率的脉冲,用于驱动步进电机。
方案:1、使用定时器溢出中断,定时中断一次,在中断通过判断来翻转IO口。
优点:实现比较简单,对硬件要求不高。
缺点:不适合高速脉冲输出,而且脉冲分辨率也很低。

开启定时器中断:

HAL_TIM_Base_Start_IT(&htim8);//开启定时器中断

 在定时器中断回调函数中处理相关数据。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

就比如之前温控项目,利用PID进行计算,就是在定时器中断中进行计算。

cubemax PWM配置_cubemax PWM配置_04

3. 输出比较模式(可改变占空比)

输出比较模式其实和PWM生成模式差不多,也是设置定时周期,在这个定时计数周期内设置一个小于定时周期计数的一个值,当计数值累加到所设定的这个比较值时会发生电平的反转,进而实现一个周期内的占空比调整。

Output Compare模式产生PWM无法通过设置Pluse参数修改占空比,因为参数Pulse不起作用。

cubemax PWM配置_stm32_05

 在初始化函数中只需要进行如下操作即可产生波形。

HAL_TIM_Base_Start(&htim8);//开启定时器
  HAL_TIM_OC_Start(&htim8,TIM_CHANNEL_2);//开启比较输出通道

cubemax PWM配置_cubemax PWM配置_06

解释:由示波器看出生成的波形并不是计算出的800Hz,原因是Output Compare的Mode为Toggle on match。意思是定时器每溢出1次,就翻转电平1次。电平翻转2次才算是PWM波形,所以PWM波的频率 = TIM8溢出频率 / 2 = 800Hz / 2 = 400Hz。这个是因为定时器一直没有关闭,所以这个脉冲周期就是定时器溢出周期的两倍(为什么是两倍是因为比较输出模式的IO口翻转功能)

提问:那么既然Pluse没作用,又该如何使用它来改变输出频率呢?

这就需要采用下面介绍的方法了。

4. 输出比较模式+中断

使用定时器的输出比较模式,设置输出比较匹配时翻转IO口,并开启输出比较中断,
在中断中装载下一次比较值。
优点:可以输出高速脉冲,并且脉冲数量控制。
缺点:进入中断频繁,增加CPU负担。

接着上述步骤,只需要将中断打开。

cubemax PWM配置_回调函数_07

预分频设置为5,168/(5+1) = 28MHz定时器时钟,采用向上计数模式,定时器计数周期设置为0xFFFF,同时设置通道2的比较模式为反转模式。设置Pluse无意义。

cubemax PWM配置_寄存器_08

初始程序:

HAL_TIM_Base_Start(&htim8);//开启定时器
  HAL_TIM_OC_Start_IT(&htim8,TIM_CHANNEL_2);//开启比较中断回调函数
  TIM_CCxChannelCmd(TIM8,TIM_CHANNEL_2,TIM_CCx_DISABLE);//关闭PWM输出

开启可利用按键中断回调函数进行处理:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == KEY3_Pin)
    {
        HAL_Delay(20);
        if(KEY3 == 0)
        {
           TIM_CCxChannelCmd(TIM8,TIM_CHANNEL_2,TIM_CCx_ENABLE);
           LED1(1); 
           LED2(1);             
        }
    __HAL_GPIO_EXTI_CLEAR_IT(KEY3_Pin);//重新使能中断
    }    
}

4.1 应用场景

配置比较输出参数,设置脉冲变量pwmduty,定时器计数到pwmduty之后就反转电平。

HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)

上面的函数是定时器比较输出中断回调函数,初始化时首先使能定时器比较捕获中断,在定时器计数值与捕获比较寄存器值相等时,会调用该中断函数。

__HAL_TIM_GET_COUNTER(&htim8)

 获取当前计数器的值存放于count变量内。

tmp = 0xFFFF & (count + pwmduty); 
  __HAL_TIM_SET_COMPARE(&htim8,TIM_CHANNEL_2,tmp);//用于设置定时器通道输出比较值

那么如果想要生成周期为800Hz的波形,不防将范围设置为10000Hz ,我们反向推导168 000 000 / 10 000 / 65 536 =   2.5 所以设置为6分频完全不用担心实现不出800Hz周期的波形。那么我们就需要知道28MHz对应的时间,即计数多少能产生800Hz的波形。首先800Hz = 1 / 800 = 0.00125s ,1 / 28 000 000 = 0.000 000 035 7 ,0.00125 / 0.000 000 035 7 = 35014 所以,在这个计数频率下,想要生成800Hz的波形需要计数35014次,在65535之内。

cubemax PWM配置_回调函数_09

我们看到比预计的少一半,究其原因就是我们计算的周期值没有问题,但是比较反转模式是在设定的周期之后再反转电平,因此如果想得到周期是800Hz且有正反电平的波形,就需要将35014 / 2 = 17752。

cubemax PWM配置_stm32_10

 那么在实际应用中就可以通过改变pwmduty的值,改变脉冲周期。实现周期变化的脉冲。

4.2 如何生成一定数量的脉冲呢?

利用数组存放的阶变数据,将数组中的数据通过每次比较中断放入比较模式寄存器CCR2中。

第一个数为10000, 12000....以此累加值20000。

产生这样一个数组的函数怎么写呢?

5. 输出比较模式+中断+DMA

使用定时器的输出比较模式,设置输出比较匹配时翻转IO口,不开启输出比较中断,
开启DMA模式。
优点:可以输出高速脉冲,并且脉冲数量控制。
缺点:需要预装载脉冲频率的值,占用空间多。
结论:由于本次需要多个步进电机同时工作,并且步进电机需要加减速启动,电机转动的圈数不多。

6. PWM模式(可改变相位+占空比)

6.1 应用场景

可实现固定占空比的PWM波形。

使用PWM模式,通过改变ARR的值来改变脉冲周期,从而控制IO口反转。
优点:可以输出高速的脉冲。
缺点:一个定时器只能输出一路脉冲,脉冲数量不可设置。

6.2 CobeMX基本配置

cubemax PWM配置_stm32_11

在PWM Gernation模式下,由Counter Period与Pulse参数共同决定了PWM的频率与占空比。

cubemax PWM配置_stm32_12

6.3 程序配置 

需要进行初始化才可以

  HAL_TIM_Base_Start(&htim2); //开启定时器
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2); //开启引脚输出PWM信号波形

cubemax PWM配置_回调函数_13

7. PWM模式+中断

7.1 应用场景

可以在中断中实现周期和占空比可调。

7.2 CobeMX基本配置

其他设置保持不变,只需要开启定时器即可。

cubemax PWM配置_stm32_14

7.3 产生频率可变,占空比50%的波形。

想要启动这个函数,需在CubeMX上配置好定时器的PWM相关参数,并在NVIC中打开相应定时器的中断,然后在main函数中使用HAL_TIM_PWM_Start_IT开启PWM的初始化。

生成工程后需要初始化该功能。

  HAL_TIM_Base_Start_IT(&htim2);
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);

如果想关闭,利用下面的函数。

  HAL_TIM_Base_Stop_IT(&htim2);
  HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_2);

下面就需要进入定时器中断回调函数:

7.4 HAL_TIM_PWM_PulseFinishedCallback(PWM比较中断)与HAL_TIM_PeriodElapsedCallback(定时器中断)的区别

Pulsd Finish函数的触发是产生于计数器比较完成时,也就是PWM模式1(向上计数时,一旦周期计数器CNT<CCR比较值时通道1为有效电平,否则为无效电平)下,高电平结束的时候。

而Period Elapsed则是在整个周期结束时,也就是ARR计数溢出时触发。

我们不改变占空比值,因此选择定时器中断即可,在每个周期结束的时候对其进行赋值。

为了呈现有规律的周期变化,我们用数组查表的方式进行,在中断回调函数中对ARR赋值。

6.5 意外发现SGM42630的缺点

其实很简单,就是在定时器周期计数中断中重新赋予ARR和CCR寄存器的值。

为了加入按键,这里使用F407开发板产生PWM。

TIM8时钟为168MHZ,分频16,累加器13125,得到的111ms,8.9Hz的频率。但是计划要生产800Hz的频率,仔细查找发现原来外部晶振没有焊接,真是个傻逼的操作。

但是通过这个问题我们发现了一个现象,就是在此频率下的SGM42630芯片严重发烫。

cubemax PWM配置_寄存器_15

我们可以看到,焊接上晶振之后,得到的频率值虽然不是800Hz但也差不多了。 

我们重新在小板子上计算出8Hz的波形,验证是否发烫。

48 000 000/240/8=25000

事实证明在10Hz的情况下,全速模式芯片发烫严重。我们将其调整至细分模式。

我们调整至输出100Hz

48 000 000/240/100 = 2000

发现芯片发热依旧很严重。

这说明一个问题,就是在低频率下的SGM42630芯片发热严重。最好在500~1500Hz之间工作能够正常驱动57电机。

6.6 程序设计(按键改变周期)

6.6.1 按键设计

cubemax PWM配置_stm32_16

按键硬件电路如下,是检测低电平按下

6.6.2 按键中断输入

按键按下时会产生电平变化,即可作为中断输入触发源,比按键功能放在While循环里大大提高了程序的反应速度。

cubemax PWM配置_寄存器_17

cubemax PWM配置_stm32_18

cubemax PWM配置_IT_19

点击生成工程之后需要进行以下步骤。

外部中断下降沿检测模式。

cubemax PWM配置_stm32_20

配置中断优先级。

cubemax PWM配置_IT_21

同时中断服务函数也开启。

cubemax PWM配置_回调函数_22

按键进入中断之后就要开启外部中断回调函数——HAL_GPIO_EXTI_Callback 

cubemax PWM配置_IT_23

中断服务函数最终指向的就是这个回调处理虚函数。

cubemax PWM配置_寄存器_24

我们再这个回调函数里面编写按键处理程序:

cubemax PWM配置_回调函数_25

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == KEY1_Pin)
    {
        HAL_Delay(20);
        if(KEY1 == 0)
        {
           pwmduty += 500;
           LED1(0); 
           LED2(1); 
           timedutychange(&htim8, pwmduty); 
        }
    __HAL_GPIO_EXTI_CLEAR_IT(KEY1_Pin);//重新使能中断
    }
    else if(GPIO_Pin == KEY2_Pin)
    {
        HAL_Delay(20);
        if(KEY2 == 0)
        {
           pwmduty -= 500;
           LED1(1); 
           LED2(0);  
           timedutychange(&htim8, pwmduty);             
        }
    __HAL_GPIO_EXTI_CLEAR_IT(KEY2_Pin);//重新使能中断
    }
}

这里有一个占空比处理函数:

void timedutychange(TIM_HandleTypeDef *htim, uint16_t pwmsize)
{
        htim->Instance->ARR = pwmduty;
        htim->Instance->CCR2 = pwmduty/2;
}

最终实现的效果就是按键会改变电机的频率,单一直保持50%的占空比。

在这里定义的变量值,其实不用在这里定义,更不用在这改变。因为我们直接改变的时寄存器的值,通过直接写定时器TIM8的寄存器实现了频率的改变。

cubemax PWM配置_回调函数_26

cubemax PWM配置_回调函数_27

6.1 应用场景