定时器除了用于最基本的定时器计时中断以外,还可以用于输出PWM(Pulse Width Modulation)波,即脉冲宽度调制波形,也就是频率与占空比均可改变的矩形波。下面我们就使用PA1端口生成PWM波。
在Cube中,首先需要将PA1设置成定时器的通道,在这里我们选择TIM2的CH2(CHANNEL2)(注:CH后带N的意思是输出与不带N的波形反相):
接着,我们要到定时器中进行设置:
将定时器的时钟频率设置为80MHz后,这样设置得到的定时器频率(计算公式见第三节)为80,000,000/(100*200)=4kHz,而所得PWM波的频率与定时器频率一致,为4kHz。因此,我们可以通过改变时钟频率、Prescaler(分频系数)、Counter Period(计数周期,也叫AutoReload重装载值)来改变PWM波的频率。而PWM波的占空比为Pulse(也叫Compare比较值)/(Counter Period+1)(AutoReload重装载值),故通过改变比较值或重装载值就可以改变PWM波的占空比。为方便计算,我们通常将重装载值设置为100-1(或200-1),这样比较值(或Pulse)的值(或除以2)即为PWM波的占空比。
完成在Cube中的设置之后,在程序的初始化部分只需要开启PWM波的输出:
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); //开启TIM2的CH2输出PWM
这样就可以在PA1通道输出频率为4kHz、占空比为10%的PWM波啦!
HAL_TIM_PWM_Start函数的声明如下:
/**
* @brief Starts the PWM signal generation.
* @param htim TIM handle
* @param Channel TIM Channels to be enabled
* This parameter can be one of the following values:
* @arg TIM_CHANNEL_1: TIM Channel 1 selected
* @arg TIM_CHANNEL_2: TIM Channel 2 selected
* @arg TIM_CHANNEL_3: TIM Channel 3 selected
* @arg TIM_CHANNEL_4: TIM Channel 4 selected
* @arg TIM_CHANNEL_5: TIM Channel 5 selected
* @arg TIM_CHANNEL_6: TIM Channel 6 selected
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
若想要在后续改变输出PWM波的频率与占空比,需要用到这两个宏定义:
pa1_autoreload = 100-1;
__HAL_TIM_SetAutoreload(&htim2, pa1_autoreload);
//修改重装载值为100-1,此时频率为80,000,000/(100*100)=8kHz
__HAL_TIM_SetCompare(&htim2, (pa1_autoreload+1)*0.1);
//因为重装载值改变,若要保持占空比不变,需要同步修改比较值,此时占空比仍为10%
这样就能得到频率为8kHz,占空比为10%的PWM波了。
下面我们以第十四届省赛题为例,总结本节所讲内容:
把要求简化为固定占空比为50%,根据第五节:交互系统的内容,当在数据界面按下B2时,进行高低频模式的切换。考虑到题目要求在5s内均匀升高或降低频率,而我们是通过改变重装载值的方式来改变频率的,因此频率与我们需要改变的量成反比关系,要求频率均匀变化,重装载值必然不能均匀变化。为尽可能增加精度,预分频系数应该设置得尽量小,使得在频率相同时,重装载值能够尽量大,这样在改变频率时能够改变的重装载值就能分得越精细。因此我们将预分频系数设为1,通过计算可得当频率为4kHz时,重装载值为20000;当频率为8kHz时,重装载值为10000。
为满足题目要求在5s内切换到目标频率,步进值小于200Hz的要求,我们选择每100ms改变100Hz:
int pa1_frq = 4000;
double pa1_duty = 0.5;
int pa1_autoreload = 20000;
void change_frq(void)
{
if (pa1_frq == 4000)
{
while (pa1_frq < 8000)
{
pa1_frq += 100;
pa1_autoreload = 80000000/pa1_frq; //公式计算重装载值
__HAL_TIM_SetAutoreload(&htim2, pa1_autoreload); //改变频率
__HAL_TIM_SetCompare(&htim2, int(pa1_autoreload*pa1_duty)); //保持占空比不变
LED_Toggle(LD2);
HAL_Delay(100); //每100ms改变一次
}
}
else if (pa1_frq == 8000)
{
while (pa1_frq > 4000)
{
pa1_frq -= 100;
pa1_autoreload = 80000000/pa1_frq; //公式计算重装载值
__HAL_TIM_SetAutoreload(&htim2, pa1_autoreload); //改变频率
__HAL_TIM_SetCompare(&htim2, int(pa1_autoreload*pa1_duty)); //保持占空比不变
LED_Toggle(LD2);
HAL_Delay(100); //每100ms改变一次
}
}
}
将函数change_frq封装到task库中,并在上一节内容中已经写好的key_pro函数中调用此函数,即可完成按键改变高低频模式的要求。