提及到PWM的采集,我会想到K3里面的EMIOS模块,用起来较为方便,且节省资源,待完成K1XX系列芯片的介绍,后续会继续更新K3的介绍。
基于K144芯片的PWM采集,在此介绍一种基于PIT定时器中断的采集方法。
PWM采集思路
采集即需要采样,可以定时去采集输入的波形,只需要采集到一个PWM周期内的高低电平的持续时间,就可以得出周期,频率和正负占空比。关于PWM输入信号的采集,我们可以用到单片机的数字输入采集功能,就可以采集到高低电平,关于时间的计算,可以借助到定时器的计时功能,但是需要定时器的去采集,就需要定义采集的频率,采集的频率要尽可能的大,这样计算出来的采集的频率与占空比才会尽量的准确。
如果采样的频率较低,PWM的高低电平切换时,就会有较大误差,这样就会导致计算出来的值有较大误差
同时,也可能间隔一个甚至多个周期才会采集到高低跳变的情况,这也会导致计算有误
同时也有可能一直采集到高或者采集到低。
综上,总之采集频率要尽可能的大,采集频率越大,计算出的值越准确。
软件流程
1、使用PIT模块,最小的1个计数单位周期触发中断,实际根据具体情况拟定,会定义一个CNT进行自加,同时会去采集一次输入引脚的状态。
2、当前一个周期采集的电平状态与当前电平状态相反时,即不同时,就需要需要判定当前采集到的是高电平还是低电平,如果采集到的是高电平,则是上升沿,如果是低电平,则是下降沿
3、如果采集到上升沿,我们就可以知道低电平的持续时间,低电平的持续时间 = 采样周期 * 采样次数。
采样周期为定时器中断触发周期,采样次数为CNT的计数。当得到采样次数后,就需要把CNT清0,就下继续下一次计数采集了。
同理,如果采集到下降沿,就可以计算出高电平的持续时间。
4、在定义低电平时间变量 和高电平时间变量 时,需要初始化为0。当高低电平的时间变量都被CNT赋值了,即高低电平的持续时间都不为0,代表已经采样了一个周期,我们就可以知道PWM周期、频率和周期。
周期 = (低电平时间 + 高电平时间)采样周期 * 采样次数
频率 = 1/周期
正占空比 = 高电平时间 / (低电平时间 + 高电平时间)
负占空比 = 低电平时间 / (低电平时间 + 高电平时间)
5、计算完成之后,需要把高电平时间和低电平时间清0*,将继续下一个周期的采集
代码
以下是代码部分
#include "sdk_project_config.h"
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include"CLOCK_S32K1xx.h"
#include "interrupt_manager.h"
#include"lpit_driver.h"
extern void LPIT0_Ch0_IRQHandler(void);
static int Cnt_125ns= 0;
unsigned int old_sta = 0;
unsigned int new_sta = 0;
unsigned int up_time = 0;
unsigned int down_time = 0;
double period = 0;
double fre= 0;
double Pos_duty= 0;
double Neg_duty= 0;
double cali_period = 0 ;
int main(void)
{
double temp = (double)lpit1_ChnConfig0.period; //计算采样时间周期
cali_period =temp/ 8000000.0; //周期 单位S
CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT, g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT);
CLOCK_SYS_UpdateConfiguration(0U, 1); // 时钟初始化
PINS_DRV_Init(NUM_OF_CONFIGURED_PINS0, g_pin_mux_InitConfigArr0); //GPIO初始化
LPIT_DRV_Init(0,&lpit1_InitConfig); //LPIT初始化
LPIT_DRV_InitChannel(0,0,&lpit1_ChnConfig0); //配置计数模式,定时时间
LPIT_DRV_StartTimerChannels(0, (1 << 0)); //启动计数
LPIT_DRV_EnableTimerChannelInterrupt(0,1<<0); //允许定时器生成中断
while(1)
{
}
}
void LPIT0_Ch0_IRQHandler(void)
{
LPIT0->MSR = 1 ; /* 也可以用 LPIT_DRV_ClearInterruptFlagTimerChannels(0, (1 << 0)); */ /* 清除中断响应标志位 */
Cnt_125ns++; //计数单位自加,
old_sta = new_sta; //获取上一个采集周期的电平状态
new_sta = (PTC->PDIR >> 14) & 0X01; //获取当前采集的电平状态
if(old_sta != new_sta) //当上一个采样周期的电平状态与当前的采集电平状态不符合,则有跳变沿产生
{
if(new_sta == 1) //如果采集到高电平,则此处为上升沿
{
down_time = Cnt_125ns; //计算低电平时间
Cnt_125ns = 0; //低电平时间已经得到,计数清0 ,下一个周期继续计数
}
else //如果采集到低电平,则此处为下升沿
{
up_time = Cnt_125ns; //计算高电平时间
Cnt_125ns = 0; //高电平时间已经得到,计数清0 ,下一个周期继续计数
}
}
if(up_time > 0 && down_time > 0) //当高低电平的时间都已经得到,就可以计算了
{
Pos_duty = (double)up_time / ((double)up_time + (double)down_time) * 100; //正占空比
Neg_duty= (double)down_time / ((double)up_time + (double)down_time) * 100; //负占空比
period =(((double)up_time + (double)down_time) ) * cali_period;
fre = (double)1.0/period;
up_time = 0; //高电平时间清0
down_time = 0; //低电平时间清0
}
if(Cnt_125ns > 8000000/lpit1_ChnConfig0.period) //超过1S钟未检测到跳变沿,PWM频率小于1HZ,则一直为高电平或者低电平
{
period = 0;
fre = 0;
if(new_sta == 1 )
{
Pos_duty = 100;
Neg_duty = 0.0000;
}
else
{
Pos_duty = 0.0000;
Neg_duty = 100;
}
}
}
测试结果
采用外部设备输出PWM信号,采样出来的值与预期符合。