前段时间,由于项目原因需要驱动四线风扇(电脑CPU用的散热风扇大多就是四线风扇),打算做一块风扇的驱动板。风扇通过输入PWM波来控制电机输出,而风扇内置了霍尔传感器,可以输出PWM信号,通过捕获波形信号可以得到信号的占空比、周期、频率等信息,换算后可以得到风扇的转速,实现对风扇的闭环控制。
过程主要使用STM32的定时器实现PWM输出与PWM捕获,项目完成后就把这两个知识整理后写成这篇博客。
STM32定时器可以分为:基本定时器
、通用定时器
、高级定时器
,以STM32F1为例:
定时器类型 | 定时器型号 | 定时器总线 | 定时器功能 |
---|---|---|---|
基本定时器 | TIM6 TIM7 |
APB1 | 1. 拥有定时中断。 2. 主模式触发DAC的功能。 |
通用定时器 | TIM2 TIM3 TIM4 TIM5 |
APB1 | 1. 拥有基本定时器全部功能。 2. 具有内外时钟源选择。 3. 输入捕获。 3. 输出比较。 4. 编码器接口。 5. 主从触发模式等功能。 |
高级定时器 | TIM1 TIM8 |
APB2 | 1. 拥有通用定时器全部功能。 2. 具有重复计数器。 3. 死区生成。 4. 互补输出。 5. 刹车输入等功能。 |
它们之间的关系是:通用定时器
具备基本定时器的所有功能并额外增加功能,高级定时器
具备通用定时器所有功能并额外增加功能。
基本定时器
TIM6和TIM7各包含一个16位自动装载计数器,由各自的可编程预分频器驱动。其主要功能包括:
● 16位自动重装载累加计数器 。
● 16位可编程(可实时修改)预分频器,用于对输入的时钟按系数为1~65536之间的任意数值分频 。
● 触发DAC的同步电路 。
● 在更新事件(计数器溢出)时产生中断/DMA请求 。
以TIM6为例,在STM32CubeMX中通过以下参数设置基本定时器:
通用定时器
主要功能包括:
● 16位向上、向下、向上/向下自动装载计数器。
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值。
● 4个独立通道:
- 输入捕获
- 输出比较
- PWM生成(边缘或中间对齐模式)
- 单脉冲模式输出
● 使用外部信号控制定时器和定时器互连的同步电路。
● 如下事件发生时产生中断/DMA:
- 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
- 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
- 输入捕获
- 输出比较
● 支持针对定位的增量(正交)编码器和霍尔传感器电路。
● 触发输入作为外部时钟或者按周期的电流管理。
高级定时器
在涵盖通用定时器所有功能的同时,多出如下特性:
● 死区时间可编程的互补输出。
● 允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器。
● 刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态。
● 刹车信号输入事件发生时产生中断/DMA。
笔者用得不多,在此不作介绍。
开发环境: Keil 、CubeMX
软件版本: 5.31 、6.2.1
使用芯片: STM32F103C8T6
开发语言: C
PWM的输入输出需要使用通用定时器或者高级定时器。STM32F103C8T6有4个定时器,其中TIM1
为高级定时器,TIM2、TIM3、TIM4
为通用定时器,没有基本定时器,因此随便选择即可。
这里选择定时器TIM2,打开Channel1 选择功能PWM Generation CH1
:
注意:
PWM频率计算公式:
PWM周期计算公式:
若有效电平为High,采用PWM模式一时,PWM占空比计算公式:
若有效电平为High,采用PWM模式二时,PWM占空比计算公式:
若有效电平为Low,则上述(3)、(4)公式变为1-Duty,计数方式(向上向下计数)并不影响占空比的计算公式 。
其中TimerClock
为对应定时器的时钟频率;Prescaler
为预分频系数;CounterPeriod
为重装载值;CCRx
为比较寄存器的值,在代码工程中可以使用函数__HAL_TIM_SetCompare()修改CCRx的值从而修改PWM的占空比。
PWM模式一: 在PWM1模式下,无论是向上计数还是向下计数,TIMx_CNT
PWM模式二: 在PWM2模式下,无论是向上计数还是向下计数,TIMx_CNT>TIMx_CCR1时通道1为有效电平。
初始脉冲宽度(Pulse): 脉冲宽度即高电平持续的时间,Pluse与CounterPeriod+1的比值即为PWM的初始占空比。
自动预重装载(auto-reload preload): 当使能自动预重装载时,自动重装载寄存器写入新值后,计数器立即产生计数溢出,然后开始新的计数周期;当不使能自动预重装载时,自动重装载寄存器写入新值后,计数器完成当前旧的计数后,再开始新的计数周期。
假设采用如上配置,配置TIM2通道一作为PWM输出,只需在代码工程中调用如下两条函数便可以实现PWM的控制。
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);//初始化,开启PWM
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,1000);//设置比较寄存器CCRx的值,占空比=CCRx/ARRx 此处 CCRx=1000,ARRx=65536
定时器的PWM输入捕获的框图如下,由通用定时器的框图剪裁得到(去掉了一些与输入捕获无关的功能):
PWM输入捕获流程如下: |
---|
1. PWM信号由TIMx_CH1进入,依据是上升沿还是是下降沿从而触发TI1FP1或TI1FP2。 |
2. 当捕捉到第一次上升沿时,触发TI1FP1,计数器复位,计数值CNT清零。 |
3. 当捕捉到下降沿时,触发TI1FP2,CNT计数值在TIMx_CH2中被记录到TIMx_CCR2寄存器中。 |
4. 当再次捕捉到上升沿时,触发TI1FP1,CNT计数值在TIMx_CH1中被记录到TIMx_CCR1寄存器中,同时计数器复位,计数值清零。 |
5. 依据TIMx_CCR1、TIMx_CCR2的值便可以计算PWM的周期、频率、占空比。TIMx_CCR1的值就是周期, T I M x C C R 2 T I M x C C R 1 \frac{TIMxCCR2}{TIMxCCR1} TIMxCCR1TIMxCCR2的值就是占空比。 |
关于PWM的输入捕获,有两种CubeMX的配置方式,虽然配置方式不一样但是效果和原理都是完全一致的。
方法一:
这是网上大多数帖子的推荐配置,非常麻烦,这里不推荐使用。
方法二(推荐使用):
方法二与方法一原理与效果是一样的,只不过ST官方考虑到PWM输入捕获较为常用,可以一键配置。只需在Combined Channels中选择PWM Input on CH1同时打开定时器中断即可。
配置完成后在代码中完成定时器的中断回调函数,便可计算PWM的占空比、频率、周期。
/**
* @brief 定时器中断回调函数
* @note htim 定时器句柄
*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim2)
{
if(htim2.Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
/* 记录TIM2_CCR1的值 */
PWM_RisingCount = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);
if(PWM_RisingCount != 0 && PWM_FallingCount!= 0)
{
PWM_Duty = (float)(PWM_FallingCount / PWM_RisingCount);//占空比
PWM_Period = PWM_RisingCount*0.000001f; //周期
PWM_Frequency = 1/PWM_Period; //频率
}
}
else if(htim2.Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
/* 记录TIM2_CCR2的值 */
PWM_FallingCount = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2);
}
}
}
前言所说的风速控制板最终效果如下,非常简单,使用TIM1四路PWM输出用于控制四个LED灯,通过呼吸灯的方式指示风扇转速;使用TIM3一路PWM用于控制风扇输出(PWM频率为25KHz)。使用TIM4用于PWM捕获,测出PWM频率后转化风扇转速。