Adaptive Threshold Selection Algorithms for Simulating PWM Waves

Adaptive Threshold Selection Algorithms for Simulating PWM Waves

Preface:
In the development of MCU, the waveform we often come into contact with is PWM wave, which is usually 0-3.3V digital PWM wave, rarely involving analog PWM wave. What's the difference between the two? Simulated PWM wave is not only high and low level, but also some intermediate process, and the waveform may not be a regular rectangular wave. Moreover, its voltage may exceed 3.3v, which makes it impossible to identify directly with single-chip IO port, and PWM can not directly capture the rising and falling edges.
If your budget is adequate and the equipment developed is not very small, it can accommodate additional circuits, then you can consider adding some op-amp circuits or shaping circuits from the hardware to convert analog PWM into digital PWM as far as possible. If not, you have to consider series resistance to divide the voltage, and then the code is sampled by AD. Data are processed by algorithm.

adaptive algorithm
  • The idea of adaptive thresholding algorithm:

    The voltage values of MAX, MIN and LOW are uncertain, which are affected by transmission distance and interference.

    • Specific algorithm:
      1. The MAX and MIN values of the sampling values of PWM waves are obtained: the monitoring waveform is 20 ms (greater than 15.01 ms), the maximum 20 sampling values and the minimum 20 sampling values (using insertion sort) are collected during this period, and the average values of 16 data are obtained by discarding 2 data at the head and the tail of 20 data, respectively (4 digits after summation). The MAX and MIN values of PWM are obtained.
      2. Identify the first rising edge of PWM wave starting output: difference MAXMINDiff = MAX - MIN, monitoring waveform 20ms, looking for sampling value from less than MIN + MAXMINDiff/3 (avoid 1/3 division operation, 1/3 = 5/15 approximately equal to 5/16 = 4/16 = 1/4 + 1/16 = > 2 + > 4) to larger than MAX - MAXMINDiff/3 (and less than MIN + MAXMINDiff/3). If the duration is longer than a period of 25 kHz, there will be a rising edge in half of the period of PWM output. If the period lasts for one period, there will be no rising edge which is considered to be in the phase of no PWM output at present. The maximum acquisition time is 20 ms. If the rising edge is identified during the acquisition time, it will end ahead of time, which is not fixed.
      3. The peak and trough values of PWM waves are identified and then monitored for 1 ms (theoretical PWM output time is 1.28 ms, monitoring time needs to be less than this). The method synchronizes step 1 and calculates the average value of Low. The average value of High is equal to the average value of MAX.
      4. Threshold: Find out the difference value HighLowDiff = High - Low, SampleHigh = High - HighLowDiff/3 (1/3 operation synchronization step 3), SampleLow = Low + HighLowDiff/3.
      5. The threshold can also be selected as (SampleHigh + SampleLow)/2 according to the need.

    c code implementation (I've been using this code in an automated test program and haven't found any problems yet):

      //The adaptive threshold function is used to distinguish the rising and falling edges of analog PWM. This function operates under 32 MHz crystal oscillation.
      en_result_t GetThreshold_SelfAdaption(uint16_t* pu16AdcRegHighThd, uint16_t* pu16AdcRegLowThd)
      {
          uint32_t u32MaxMinSampleTime = 640000;    //Take out the minimum sampling value of SELF_ADAPTION_NUM and the maximum sampling value of SELF_ADAPTION_NUM in 20 ms (at least more than one cycle time)
          uint32_t u32LowSampleTime = 32000;        //The minimum sampling values of SELF_ADAPTION_NUM in 1ms (not more than 1.28ms PWM output time) are taken out to determine u16AvgVLow.
          uint16_t u16Vmax[SELF_ADAPTION_NUM] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,            //0:Little 19:Big
                                                 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:big 19:small
                                                 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:big 19:small
                                                 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u, 65535u};
          uint16_t u16AvgVmax = 0u;               //Average Maximum Sampling Value
          uint16_t u16AvgVmin = 0u;               //Mean Value of Minimum Sampling Value
          uint16_t u16AvgVHigh = 0u;              //Average Sampling Value of Simulated PWM Wave Peak
          uint16_t u16AvgVLow = 0u;               //Average Sampling Value of Simulated PWM Valley
          uint32_t u32Sum = 0u;                   //Sum of Sampled Values
          uint32_t u32TIMCnt = 0u;                //Timer count value
          uint16_t u16AdcResult = 0u;             //AD Sampling Value
          uint8_t u8StateFlg = TRUE;              //Used to identify whether the current sampling value is at a peak or a trough, False: trough TRUE: peak
          uint8_t u8StatePreFlg = TRUE;           //Historical value of u8StateFlg
          uint8_t u8MaxToMinFlg = FALSE;          //Signs for detecting trough jump to peak, TRUE: detected, FALSE: undetected
          int8_t i = 0;
          int8_t j = 0;
          uint16_t u16DiffValue = 0u;             //The difference between u16VLow and u16VHign
          uint16_t u16DiffValue_5_16 = 0u;        //5/16 of the difference, close to 1/3
          uint32_t u32LatestMaxTime = 0u;         //Time when the latest sampling value is near u16AvgVmax
          uint32_t u32KeepMinTime = 0u;           //Sampling Voltage Value Holding Near u16AvgVmin Time
          uint32_t u32SpaceDiffCntJuge = 1280u;   //The interval counting difference criterion is 1/25000 * 32000000 = 1280, and a cycle counting value of 25KHz under 32M is 1280. If the interval is greater than this, it is considered as a long interval.
          en_result_t enResult = Ok;
    
          //Take the minimum and maximum sampling values over a period of time (SELF_ADAPTION_NUM minimum/maximum, respectively)
          u16AdcResult = 0u;
          Bt_Run(TIM1);                                           //BT TIM1 Timer Begins Timing
          while(u32TIMCnt < u32MaxMinSampleTime)
          {
              Adc_Start();                                        //ADC Conversion Start
              Adc_GetResult(&u16AdcResult);                       //Get the ADC conversion value
    
              if(u16AdcResult > u16Vmax[0])                       //The smallest is added when it is larger than u16Vmax
              {
                  //Insert sort, small to large row
                  for(i = SELF_ADAPTION_NUM - 1; i >= 0; i--)     //Look right to left from the array
                  {
                      if(u16Vmax[i] < u16AdcResult)               //Find the first one smaller than the sample value.
                      {
                          //Move left, the smallest of zeros is removed
                          for(j = 0; j < i; j++)
                          {
                              u16Vmax[j] = u16Vmax[j+1];
                          }
    
                          //Insert Sample Value
                          u16Vmax[i] = u16AdcResult;
                          break;
                      }
                  }
              }
    
              if(u16AdcResult < u16Vmin[19])                      //The largest is added in less than u16Vmin
              {
                  //Insert sort, small to large row
                  for(i = 0; i < SELF_ADAPTION_NUM; i++)          //Look left to right from the array
                  {
                      if(u16Vmin[i] > u16AdcResult)               //Find the first one larger than the sample value.
                      {
                          //Move to the right, the largest of 19 is removed.
                          for(j = SELF_ADAPTION_NUM - 1; j > i; j--)
                          {
                              u16Vmin[j] = u16Vmin[j-1];
                          }
    
                          //Insert Sample Value
                          u16Vmin[i] = u16AdcResult;
                          break;
                      }
                  }
              }
    
              u32TIMCnt = Bt_Cnt32Get(TIM1);      //Get Timing Number
          }
    
          //Getting the Maximum and Minimum Mean Value of Sampling Value
          //Average Mean of Maximum Sampling Value
          for(i = 2, u32Sum = 0; i < SELF_ADAPTION_NUM - 2; i++)  //Abandon the first two, do the average of 16, use shift to avoid Division
          {
              u32Sum = u32Sum + u16Vmax[i];
          }
          u16AvgVmax = u32Sum >> 4;
          
          //Average minimum sampling value
          for(i = 2, u32Sum = 0; i < SELF_ADAPTION_NUM - 2; i++)  //Abandon the first two, do the average of 16, use shift to avoid Division
          {
              u32Sum = u32Sum + u16Vmin[i];
          }
          u16AvgVmin = u32Sum >> 4;
    
          if(u16AvgVmax > u16AvgVmin)                             //Abnormal examination
          {
              //Finding the Mean Sampling Value of Simulated PWM Peak and Valley
              //Average Peak Sampling Value
              u16AvgVHigh = u16AvgVmax;
    
              //Average Valley Sampling Value
              //Find the rising edge from u16AvgVmin jump to u16AvgVmax, and then start to calculate the minimum sampling value for a period of time.
              Bt_Cnt32Set(TIM1, 0u);                              //Number zero
              u32TIMCnt = 0u;
              u8MaxToMinFlg = FALSE;
              u16AdcResult = 0u;
              u16DiffValue = u16AvgVmax - u16AvgVmin;
              u16DiffValue_5_16 = (u16DiffValue >> 2) + (u16DiffValue >> 4);        //1/4 + 1/16 = 5/16 is close to 1/3
              while(u32TIMCnt < u32MaxMinSampleTime)
              {
                  Adc_Start();                                    //ADC Conversion Start
                  Adc_GetResult(&u16AdcResult);                   //Get the ADC conversion value
    
                  if(u16AdcResult < u16AvgVmin + u16DiffValue_5_16)           //It is considered to be in the minimum range.
                  {
                      u8StateFlg = FALSE;
                      u32KeepMinTime = u32TIMCnt - u32LatestMaxTime;
                  }
                  else if(u16AdcResult > u16AvgVmax - u16DiffValue_5_16)      //Consider to be in the maximum range
                  {
                      u8StateFlg = TRUE;
                      u32LatestMaxTime = u32TIMCnt;
                  }
    
                  if((u8StateFlg == TRUE) && (u8StatePreFlg == FALSE) && (u32KeepMinTime >= u32SpaceDiffCntJuge ))        //From trough to crest
                  {
                      u8MaxToMinFlg = TRUE;                                   //Markers jump from trough to crest
                      break;
                  }
    
                  u8StatePreFlg = u8StateFlg;
    
                  u32TIMCnt = Bt_Cnt32Get(TIM1);                              //Get Timing Number
              }
    
              if(u8MaxToMinFlg == TRUE)                                       //Recognition to jump
              {
                  Bt_Cnt32Set(TIM1, 0u);                                      //Number zero
                  u32TIMCnt = 0u;
                  u16AdcResult = 0u;
                  while(u32TIMCnt < u32LowSampleTime)
                  {
                      Adc_Start();                                            //ADC Conversion Start
                      Adc_GetResult(&u16AdcResult);                           //Get the ADC conversion value
    
                      if(u16AdcResult < u16VLow[SELF_ADAPTION_NUM - 1])                      //The largest is added in less than u16Vmin
                      {
                          //Insert sort, small to large row
                          for(i = 0; i < SELF_ADAPTION_NUM; i++)          //Look left to right from the array
                          {
                              if(u16VLow[i] > u16AdcResult)               //Find the first one larger than the sample value.
                              {
                                  //Move to the right, the largest of 19 is removed.
                                  for(j = SELF_ADAPTION_NUM - 1; j > i; j--)
                                  {
                                      u16VLow[j] = u16VLow[j-1];
                                  }
    
                                  //Insert Sample Value
                                  u16VLow[i] = u16AdcResult;
                                  break;
                              }
                          }
                      }
    
                      u32TIMCnt = Bt_Cnt32Get(TIM1);                          //Get Timing Number
                  }
    
                  //Average Value of Maximum of Partial Sampling Value of PWM Wave
                  for(i = 2, u32Sum = 0; i < SELF_ADAPTION_NUM - 2; i++)  //Abandon the first two, do the average of 16, use shift to avoid Division
                  {
                      u32Sum = u32Sum + u16VLow[i];
                  }
                  u16AvgVLow = u32Sum >> 4;
    
                  if(u16AvgVHigh > u16AvgVLow)                                //Abnormal examination
                  {
                      u16DiffValue = u16AvgVHigh - u16AvgVLow;
                      u16DiffValue_5_16 = (u16DiffValue >> 2) + (u16DiffValue >> 4);        //1/4 + 1/16 = 5/16 is close to 1/3
                      *pu16AdcRegHighThd = u16AvgVHigh - u16DiffValue_5_16;
                      *pu16AdcRegLowThd = u16AvgVLow + u16DiffValue_5_16;
                  }
                  else
                  {
                      enResult = Error;
                  }
              }
              else                                    //The jump from trough to peak is not recognized in detection time
              {
                  enResult = Error;
              }
          }
          else
          {
              enResult = Error;
          }
    
          Bt_Stop(TIM1);                               //Timer stop
          Bt_Cnt32Set(TIM1, 0u);                       //Number zero
    
          return enResult;
      }
    
           enResult = Error;
          }
    
          Bt_Stop(TIM1);                               //Timer stop
          Bt_Cnt32Set(TIM1, 0u);                       //Number zero
    
          return enResult;
      }
    

Tags: less

Posted on Mon, 12 Aug 2019 03:54:51 -0700 by nonexistentera