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 03.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 singlechip 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 opamp 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:
 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.
 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.
 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.
 Threshold: Find out the difference value HighLowDiff = High  Low, SampleHigh = High  HighLowDiff/3 (1/3 operation synchronization step 3), SampleLow = Low + HighLowDiff/3.
 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[j1]; } //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[j1]; } //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; }
 Specific algorithm: