模拟PWM波的自适应取阈值算法

介绍一种针对模拟PWM波的自适应阈值算法,适用于单片机开发中信号处理,通过监测波形并计算平均值来确定阈值,以识别波峰和波谷,适用于非标准和变化的PWM信号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模拟PWM波的自适应取阈值算法

前言:
  单片机开发中,我们常常接触到的波形就是PWM波,一般都是0~3.3V的数字PWM波,很少涉及模拟PWM波。两者有什么不同呢?模拟PWM波不只有高低电平,还有中间的一些变化过程,且波形可能也不是规整的矩形波,再者它的电压可能超过3.3v导致无法直接用单片机IO口识别,PWM无法直接捕获到上升下降沿。
  如果你预算充足,开发的设备也不是非常的小型话,可以容纳额外的电路,那么你可以考虑从硬件上增加一些运放电路或整形电路,尽可能将模拟PWM变为数字PWM,如果不行就得考虑串联电阻来分压,然后代码中通过AD采样到数据后进行算法处理。

自适应算法
  • 自适应取阈值算法思路:
    [外链图片转存失败(img-FkAsB5Cv-1565606949215)(.\png\自适应算法思路.png "自适应算法思路")]
    这里的MAX,MIN,LOW电压值都是不确定的,受传输距离和干扰影响。

    • 具体算法:
      1. 求出PWM波采样值的MAX和MIN值:监测波形20ms(大于周期15.01ms),采集这段时间内的最大的20个采样值和最小的20个采样值(使用插入排序),舍弃20个数据的头尾各2个共4个数据,对十六个数据求平均值(求和后右移4位)。得出PWM的MAX和MIN值。
      2. 识别PWM波开始输出的第一个上升沿:差值MAXMINDiff = MAX - MIN, 监测波形20ms,寻找采样值从小于MIN + MAXMINDiff/3(避免1/3除法运算,1/3 = 5/15 约等于5/16 = 4/16 + 1/16 = 1/4 + 1/16 = >>2 + >>4)跳变到大于MAX - MAXMINDiff/3(且小于MIN + MAXMINDiff/3的持续时间需大于一个25kHz的周期,PWM输出期间半个周期就会有一个上升沿,若持续一个周期都未出现认为当前处于无PWM输出阶段)的上升沿。最大采集20ms,如果期间识别到上升沿则会提前结束,这段时间是不固定的。
      3. 识别PWM波的波峰值High和波谷值Low:PWM开始输出后进行1ms(理论PWM输出时间为1.28ms,监测时间需小于此)的监测,方法同步骤1,求出Low平均值, High平均值等于MAX平均值。
      4. 取阈值:求出差值HighLowDiff = High - Low,采样值取SampleHigh = High - HighLowDiff/3(1/3运算同步骤3), SampleLow = Low + HighLowDiff/3。
      5. 根据需要阈值也可选取为(SampleHigh + SampleLow)/2。

    c代码实现(这段代码我在一个自动测试程序中一直在使用,暂未发现问题):

      //自适应求阈值函数,用于区分模拟PWM的上升下降沿。此函数工作在32MHz晶振下
      en_result_t GetThreshold_SelfAdaption(uint16_t* pu16AdcRegHighThd, uint16_t* pu16AdcRegLowThd)
      {
          uint32_t u32MaxMinSampleTime = 640000;    //取出20ms(至少要大于一个周期时间)时间内SELF_ADAPTION_NUM个最小的采样值和SELF_ADAPTION_NUM个最大的采样值
          uint32_t u32LowSampleTime = 32000;        //取出1ms(不能大于1.28ms的PWM输出时间)时间内SELF_ADAPTION_NUM个最小的采样值,用于确定u16AvgVLow
          uint16_t u16Vmax[SELF_ADAPTION_NUM] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,            //0:小  19:大
                                                 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u};
          uint16_t u16Vmin[SELF_ADAPTION_NUM] = {65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u,            //0:大  19:小
                                                 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u};
          uint16_t u16VLow[SELF_ADAPTION_NUM] = {65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u,            //0:大  19:小
                                                 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u};
          uint16_t u16AvgVmax = 0u;               //最大采样值平均值
          uint16_t u16AvgVmin = 0u;               //最小采样值平均值
          uint16_t u16AvgVHigh = 0u;              //模拟PWM波峰采样值平均值
          uint16_t u16AvgVLow = 0u;               //模拟PWM波谷采样值平均值
          uint32_t u32Sum = 0u;                   //采样值之和
          uint32_t u32TIMCnt = 0u;                //定时器计数值
          uint16_t u16AdcResult = 0u;             //AD采样值
          uint8_t u8StateFlg = TRUE;              //用于标识当前采样值处于波峰还是波谷,False:波谷  TRUE:波峰
          uint8_t u8StatePreFlg = TRUE;           //u8StateFlg的历史值
          uint8_t u8MaxToMinFlg = FALSE;          //检测波谷跳变到波峰的标志,TRUE:检测到,FALSE:未检测到
          int8_t i = 0;
          int8_t j = 0;
          uint16_t u16DiffValue = 0u;             //u16VLow和u16VHign的差值
          uint16_t u16DiffValue_5_16 = 0u;        //差值的5/16,接近1/3
          uint32_t u32LatestMaxTime = 0u;         //最新采样值处于u16AvgVmax附近的时间
          uint32_t u32KeepMinTime = 0u;           //采样电压值持续保持在u16AvgVmin附近的时间
          uint32_t u32SpaceDiffCntJuge = 1280u;   //间隔计数差值判断标准, 1/25000 * 32000000 = 1280,32M下25KHz一个周期计数值为1280,大于此间隔则认为是长间隔。
          en_result_t enResult = Ok;
    
          //取一段时间内的采样值最小值和最大值(分别取最小/最大的SELF_ADAPTION_NUM个)
          u16AdcResult = 0u;
          Bt_Run(TIM1);                                           //BT TIM1定时器开始计时
          while(u32TIMCnt < u32MaxMinSampleTime)
          {
              Adc_Start();                                        //ADC转换开始
              Adc_GetResult(&u16AdcResult);                       //获取ADC转换值
    
              if(u16AdcResult > u16Vmax[0])                       //大于u16Vmax中最小的则新增
              {
                  //插入排序,小到大排
                  for(i = SELF_ADAPTION_NUM - 1; i >= 0; i--)     //从数组右往左找
                  {
                      if(u16Vmax[i] < u16AdcResult)               //找到第一个比采样值小的
                      {
                          //往左移动,0号最小的被移出
                          for(j = 0; j < i; j++)
                          {
                              u16Vmax[j] = u16Vmax[j+1];
                          }
    
                          //插入采样值
                          u16Vmax[i] = u16AdcResult;
                          break;
                      }
                  }
              }
    
              if(u16AdcResult < u16Vmin[19])                      //小于u16Vmin中最大的则新增
              {
                  //插入排序,小到大排
                  for(i = 0; i < SELF_ADAPTION_NUM; i++)          //从数组左往右找
                  {
                      if(u16Vmin[i] > u16AdcResult)               //找到第一个比采样值大的
                      {
                          //往右移动,19号最大的被移出
                          for(j = SELF_ADAPTION_NUM - 1; j > i; j--)
                          {
                              u16Vmin[j] = u16Vmin[j-1];
                          }
    
                          //插入采样值
                          u16Vmin[i] = u16AdcResult;
                          break;
                      }
                  }
              }
    
              u32TIMCnt = Bt_Cnt32Get(TIM1);      //获取计时数
          }
    
          //获取采样值最大最小平均值
          //采样值最大值平均值
          for(i = 2, u32Sum = 0; i < SELF_ADAPTION_NUM - 2; i++)  //舍弃首位各两个,做16个的平均值,用移位避免除法
          {
              u32Sum = u32Sum + u16Vmax[i];
          }
          u16AvgVmax = u32Sum >> 4;
          
          //采样值最小值平均值
          for(i = 2, u32Sum = 0; i < SELF_ADAPTION_NUM - 2; i++)  //舍弃首位各两个,做16个的平均值,用移位避免除法
          {
              u32Sum = u32Sum + u16Vmin[i];
          }
          u16AvgVmin = u32Sum >> 4;
    
          if(u16AvgVmax > u16AvgVmin)                             //异常检查
          {
              //求模拟PWM波峰波谷采样值平均值
              //波峰采样值平均值
              u16AvgVHigh = u16AvgVmax;
    
              //波谷采样值平均值
              //找到从u16AvgVmin跳变到u16AvgVmax的上升沿,此后开始一段时间的最小采样值求取
              Bt_Cnt32Set(TIM1, 0u);                              //计数值清零
              u32TIMCnt = 0u;
              u8MaxToMinFlg = FALSE;
              u16AdcResult = 0u;
              u16DiffValue = u16AvgVmax - u16AvgVmin;
              u16DiffValue_5_16 = (u16DiffValue >> 2) + (u16DiffValue >> 4);        //1/4 + 1/16 = 5/16 接近1/3
              while(u32TIMCnt < u32MaxMinSampleTime)
              {
                  Adc_Start();                                    //ADC转换开始
                  Adc_GetResult(&u16AdcResult);                   //获取ADC转换值
    
                  if(u16AdcResult < u16AvgVmin + u16DiffValue_5_16)           //认为处于最小值范围
                  {
                      u8StateFlg = FALSE;
                      u32KeepMinTime = u32TIMCnt - u32LatestMaxTime;
                  }
                  else if(u16AdcResult > u16AvgVmax - u16DiffValue_5_16)      //认为处于最大值范围
                  {
                      u8StateFlg = TRUE;
                      u32LatestMaxTime = u32TIMCnt;
                  }
    
                  if((u8StateFlg == TRUE) && (u8StatePreFlg == FALSE) && (u32KeepMinTime >= u32SpaceDiffCntJuge ))        //由波谷变到波峰
                  {
                      u8MaxToMinFlg = TRUE;                                   //标记从波谷跳到波峰
                      break;
                  }
    
                  u8StatePreFlg = u8StateFlg;
    
                  u32TIMCnt = Bt_Cnt32Get(TIM1);                              //获取计时数
              }
    
              if(u8MaxToMinFlg == TRUE)                                       //识别到跳变
              {
                  Bt_Cnt32Set(TIM1, 0u);                                      //计数值清零
                  u32TIMCnt = 0u;
                  u16AdcResult = 0u;
                  while(u32TIMCnt < u32LowSampleTime)
                  {
                      Adc_Start();                                            //ADC转换开始
                      Adc_GetResult(&u16AdcResult);                           //获取ADC转换值
    
                      if(u16AdcResult < u16VLow[SELF_ADAPTION_NUM - 1])                      //小于u16Vmin中最大的则新增
                      {
                          //插入排序,小到大排
                          for(i = 0; i < SELF_ADAPTION_NUM; i++)          //从数组左往右找
                          {
                              if(u16VLow[i] > u16AdcResult)               //找到第一个比采样值大的
                              {
                                  //往右移动,19号最大的被移出
                                  for(j = SELF_ADAPTION_NUM - 1; j > i; j--)
                                  {
                                      u16VLow[j] = u16VLow[j-1];
                                  }
    
                                  //插入采样值
                                  u16VLow[i] = u16AdcResult;
                                  break;
                              }
                          }
                      }
    
                      u32TIMCnt = Bt_Cnt32Get(TIM1);                          //获取计时数
                  }
    
                  //PWM波部分采样值最大值平均值
                  for(i = 2, u32Sum = 0; i < SELF_ADAPTION_NUM - 2; i++)  //舍弃首位各两个,做16个的平均值,用移位避免除法
                  {
                      u32Sum = u32Sum + u16VLow[i];
                  }
                  u16AvgVLow = u32Sum >> 4;
    
                  if(u16AvgVHigh > u16AvgVLow)                                //异常检查
                  {
                      u16DiffValue = u16AvgVHigh - u16AvgVLow;
                      u16DiffValue_5_16 = (u16DiffValue >> 2) + (u16DiffValue >> 4);        //1/4 + 1/16 = 5/16 接近1/3
                      *pu16AdcRegHighThd = u16AvgVHigh - u16DiffValue_5_16;
                      *pu16AdcRegLowThd = u16AvgVLow + u16DiffValue_5_16;
                  }
                  else
                  {
                      enResult = Error;
                  }
              }
              else                                    //检测时间内未识别到波谷到波峰的跳变
              {
                  enResult = Error;
              }
          }
          else
          {
              enResult = Error;
          }
    
          Bt_Stop(TIM1);                               //定时器停止
          Bt_Cnt32Set(TIM1, 0u);                       //计数值清零
    
          return enResult;
      }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值