目录
写在前面
先回顾下定时器的单路捕获PWM
多路捕获PWM的频率和占空比(状态机实现)
我的思路:
状态图
配置
给出示例代码
测试效果
先有了这篇文章实现了单定时器的多通道测量频率,以外部时钟的方式可测量任意频率的方波),奈何不能多路测试PWM波的频率,于是有了本文。
基于HAL库的STM32的单定时器的多路输入捕获测量脉冲频率(外部时钟实现)_昊月光华的博客-CSDN博客
对于定时器的单路捕获PWM的频率和脉冲,用cubemx配置:一个通道捕获上升沿,另一个通道捕获下降沿,Slave Mode 为Reset Mode .触发源为 TL1FP1 这可以很好地测量输入信号的周期和高电平时间,是使用定时器输入捕获的常用模式。(但仅限于定时器捕获单路PWM波)
在这种模式下:
1.上升沿到来时,触发中断,保存计数值到CCR1(假设通道1捕获上升沿的计数值),然后定时器的计数值清0(TIMx->CNT = 0)(这一点是关键)
2.下降沿到来时,保存计数值到CCR2(假设通道2捕获下降沿的计数值),定时器的计数值不会清0.
PWM一个周期下映射到定时器的计数值 = 上升沿的计数值.(CCRx)
PWM的频率 = 定时器的频率(1M) / (捕获上升沿的计数值 -0)
PWM的占空比 = (下降沿的计数值 / 上升沿的计数值)
配置(以通道1上升沿直接捕获,通道2下降沿间接捕获)
给出以上的实例代码
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
static u16 t = 0;
static u16 d = 0;
if(htim->Instance == TIM2)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
LEDDT[0]=1;
t = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1;
freq = 1000000 / t;
duty = (float)d/t*100;
}
else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
LEDDT[1]=2;
d = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2)+1;
}
}
}
配置定时器的两个通道都为上升沿捕获,开启定时器对于通道的输入捕获中断。
需要注意定时器的计数值有可能会溢出,所以要记录下溢出次数(在定时器的溢出更新中断中记录)
PWM频率= 定时器频率/ 两个上升沿之间的计数值
定时器频率 = 系统时钟 /预分频系数 = 1M
两个上升沿之间的计数值 = 第一次上升沿的计数值 +( 溢出次数 x 重装载值)- 第二次上升沿的计数值
PWM占空比 = 有效计数值 / 两个上升沿的计数值
有效计数值(假设以高电平为有效电平) = 下降沿的计数值 + ( 溢出次数 x 重装载值)-上升沿的计数值
设置定时器的两个通道(多通道)为上升沿捕获计数值,这意味着每次PWM波在上升沿都会进入中断,保留计数值到CCRx.
用cubemx配置的话,就是很简单的配置方式,系统时钟80m,预分配系数80-1, 定时器频率为1M,预装载为0xffff(65535)
TIM3的通道1和通道2
数据类型:
typedef struct mypwm{
u32 firstrisingcnt;
u32 secondrisingcnt;
u32 fallingcnt;
u32 validcnt; //有效计数值对应于脉宽
float freq;
float duty;
u16 updatetimes;
u8 state;
} pwms;
在溢出更新中断的回调函数中:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3)
{
mpwms[0].updatetimes++;
mpwms[1].updatetimes++;
//两路的更新计数值自加1,这里实际上可以用一个代替
}
}
输入捕获的中断回调函数
void Inputcapturehandle(pwms * cpwm,u32 cnt,TIM_HandleTypeDef *htim,u32 ch)
{
u32 temp = 0;
switch(cpwm->state)
{
case 0: //测量上升沿
{
//开启下次为下降沿采样
__HAL_TIM_SET_CAPTUREPOLARITY(htim, ch, TIM_INPUTCHANNELPOLARITY_FALLING);
//溢出计数值置为0
cpwm->updatetimes = 0;
//捕获第一次计数值
cpwm->firstrisingcnt =cnt;
//更新状态
cpwm->state = 1;
break;
}
case 1: //测量下降沿
{
//开启下一次为上升沿采样
__HAL_TIM_SET_CAPTUREPOLARITY(htim, ch, TIM_INPUTCHANNELPOLARITY_RISING);
//捕获下降沿的计数值
cpwm->fallingcnt = cnt;
//计算有效计数值(考虑溢出)
cpwm->validcnt = (cpwm->updatetimes * htim->Instance->ARR)+ cpwm->fallingcnt-cpwm->firstrisingcnt;
//溢出计数置为0
cpwm->updatetimes =0;
//更新下一状态
cpwm->state = 2;
break;
}
case 2: //再次测量上升沿
{
//捕获第二次上升沿的计数值
cpwm->secondrisingcnt = cnt;
//计算两个上升沿之间的计数值(考虑溢出)
temp = cpwm->secondrisingcnt + (cpwm->updatetimes * htim->Instance->ARR) - cpwm->firstrisingcnt;
//溢出计数值置为0
cpwm->updatetimes = 0;
//计算频率 = 定时器频率/一个PWM波的两个上升沿的计数值
cpwm->freq = 1e6/temp;
//计算占空比
cpwm->duty = cpwm->validcnt*1.0f / temp *100;
//更新状态
cpwm->state = 0;
}
break;
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
//进入到自己的中断回调函数中执行
Inputcapturehandle(&mpwms[0],TIM3->CCR1,htim,TIM_CHANNEL_1);
}
else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
//进入到自己的中断回调函数中执行
Inputcapturehandle(&mpwms[1],TIM3->CCR2,htim,TIM_CHANNEL_2);
}
}
}
通过电位器控制输出PWM波的频率和占空比:输出格式为 频率 -占空比