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 计算公式
步进电机旋转频率:
我们使用STM32F407,想要产生800Hz频率应当配置定时器如下:
2. 定时器中断
2.1 应用场景
每到定时器计数值满进入定时器中断,执行相应的操作。
目的:让单片机输出一段可调频率的脉冲,用于驱动步进电机。
方案:1、使用定时器溢出中断,定时中断一次,在中断通过判断来翻转IO口。
优点:实现比较简单,对硬件要求不高。
缺点:不适合高速脉冲输出,而且脉冲分辨率也很低。
开启定时器中断:
HAL_TIM_Base_Start_IT(&htim8);//开启定时器中断
在定时器中断回调函数中处理相关数据。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
就比如之前温控项目,利用PID进行计算,就是在定时器中断中进行计算。
3. 输出比较模式(可改变占空比)
输出比较模式其实和PWM生成模式差不多,也是设置定时周期,在这个定时计数周期内设置一个小于定时周期计数的一个值,当计数值累加到所设定的这个比较值时会发生电平的反转,进而实现一个周期内的占空比调整。
Output Compare模式产生PWM无法通过设置Pluse参数修改占空比,因为参数Pulse不起作用。
在初始化函数中只需要进行如下操作即可产生波形。
HAL_TIM_Base_Start(&htim8);//开启定时器
HAL_TIM_OC_Start(&htim8,TIM_CHANNEL_2);//开启比较输出通道
解释:由示波器看出生成的波形并不是计算出的800Hz,原因是Output Compare的Mode为Toggle on match。意思是定时器每溢出1次,就翻转电平1次。电平翻转2次才算是PWM波形,所以PWM波的频率 = TIM8溢出频率 / 2 = 800Hz / 2 = 400Hz。这个是因为定时器一直没有关闭,所以这个脉冲周期就是定时器溢出周期的两倍(为什么是两倍是因为比较输出模式的IO口翻转功能)
提问:那么既然Pluse没作用,又该如何使用它来改变输出频率呢?
这就需要采用下面介绍的方法了。
4. 输出比较模式+中断
使用定时器的输出比较模式,设置输出比较匹配时翻转IO口,并开启输出比较中断,
在中断中装载下一次比较值。
优点:可以输出高速脉冲,并且脉冲数量控制。
缺点:进入中断频繁,增加CPU负担。
接着上述步骤,只需要将中断打开。
预分频设置为5,168/(5+1) = 28MHz定时器时钟,采用向上计数模式,定时器计数周期设置为0xFFFF,同时设置通道2的比较模式为反转模式。设置Pluse无意义。
初始程序:
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之内。
我们看到比预计的少一半,究其原因就是我们计算的周期值没有问题,但是比较反转模式是在设定的周期之后再反转电平,因此如果想得到周期是800Hz且有正反电平的波形,就需要将35014 / 2 = 17752。
那么在实际应用中就可以通过改变pwmduty的值,改变脉冲周期。实现周期变化的脉冲。
4.2 如何生成一定数量的脉冲呢?
利用数组存放的阶变数据,将数组中的数据通过每次比较中断放入比较模式寄存器CCR2中。
第一个数为10000, 12000....以此累加值20000。
产生这样一个数组的函数怎么写呢?
5. 输出比较模式+中断+DMA
使用定时器的输出比较模式,设置输出比较匹配时翻转IO口,不开启输出比较中断,
开启DMA模式。
优点:可以输出高速脉冲,并且脉冲数量控制。
缺点:需要预装载脉冲频率的值,占用空间多。
结论:由于本次需要多个步进电机同时工作,并且步进电机需要加减速启动,电机转动的圈数不多。
6. PWM模式(可改变相位+占空比)
6.1 应用场景
可实现固定占空比的PWM波形。
使用PWM模式,通过改变ARR的值来改变脉冲周期,从而控制IO口反转。
优点:可以输出高速的脉冲。
缺点:一个定时器只能输出一路脉冲,脉冲数量不可设置。
6.2 CobeMX基本配置
在PWM Gernation模式下,由Counter Period与Pulse参数共同决定了PWM的频率与占空比。
6.3 程序配置
需要进行初始化才可以
HAL_TIM_Base_Start(&htim2); //开启定时器
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2); //开启引脚输出PWM信号波形
7. PWM模式+中断
7.1 应用场景
可以在中断中实现周期和占空比可调。
7.2 CobeMX基本配置
其他设置保持不变,只需要开启定时器即可。
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芯片严重发烫。
我们可以看到,焊接上晶振之后,得到的频率值虽然不是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 按键设计
按键硬件电路如下,是检测低电平按下
6.6.2 按键中断输入
按键按下时会产生电平变化,即可作为中断输入触发源,比按键功能放在While循环里大大提高了程序的反应速度。
点击生成工程之后需要进行以下步骤。
外部中断下降沿检测模式。
配置中断优先级。
同时中断服务函数也开启。
按键进入中断之后就要开启外部中断回调函数——HAL_GPIO_EXTI_Callback
中断服务函数最终指向的就是这个回调处理虚函数。
我们再这个回调函数里面编写按键处理程序:
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的寄存器实现了频率的改变。