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

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

自适应算法
  • 自适应取阈值算法思路:

    这里的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个最小的采样值,用于确定u16AvgVLowuint16_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/3uint32_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/3while(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;}
    

模拟PWM波的自适应取阈值算法相关推荐

  1. STM32之PWM波

    说起PWM波,做过智能小车的人肯定都很清楚了,其实他就是一种脉宽调制.在智能小车上,我们一般用PWM波来控制小车的速度,通过控制方波中高低电平的比例,来达到控制小车转速的目的.而32的芯片他提供了专门 ...

  2. 使用Arduino ESP32 通过PWM波控制大疆GM6020以及3508无刷电机(更新)

    使用Arduino控制大疆GM6020无刷电机: 话不多说直接上代码 #include <Servo.h> Servo myservo; //创建一个舵机控制对象// Arduino自带的 ...

  3. 【图像分割】利用粒子群算法与遗传算法实现图像的自适应多阈值的快速分割

    文章目录 前言 一.自适应多阈值分割 1.最大类间差方法 2.最大熵方法 二.代码部分(以粒子群算法与遗传算法优化自适应双阈值分割为例) 1. 利用粒子群算法优化最大类间差双阈值分割 1.1 概述 1 ...

  4. OpenCV —— 阈值分割(直方图技术法,熵算法,Otsu,自适应阈值算法)

    阈值分割 1. 全局阈值分割 直方图技术法 熵算法 Otsu算法 2. 局部阈值分割 自适应阈值 阈值的分割的核心就是如何选取阈值,选取正确的阈值时分割成功的关键.可以使用手动设置阈值,也可以采用直方 ...

  5. request[limit]取不到前台的值_基于uFUN开发板的心率计(二)动态阈值算法获取心率值...

    前言 上一篇文章:基于uFUN开发板的心率计(一)DMA方式获取传感器数据,介绍了如何获取PulseSensor心率传感器的电压值,并对硬件电路进行了计算分析.心率计,重要的是要获取到心率值,本篇文章 ...

  6. 自适应阈值算法(大津阈值法)

    最大类间方差法是由日本学者大津于1979年提出的,是一种自适应的阈值确定的方法,又叫大津法,简称OTSU.它是按图像的灰度特性,将图像分成背景和目标2部分.背景和目标之间的类间方差越大,说明构成图像的 ...

  7. 基于特征点匹配的自适应目标跟踪算法

    基于特征点匹配的自适应目标跟踪算法 2016-01-29 13:11 摘 要:由于实际场景复杂多变,目标在运动过程中往往会出现形变.遮挡等问题,增加了跟踪的难度.为了解决上述问题,提出一种基于特征点匹 ...

  8. 波束赋形技术lms算法在matlab仿真,自适应波束成形算法LMS、RLS、VSSLMS分解

    1.传统的通信系统中,基站天线通常是全向天线,此时,基站在向某一个用户发射或接收信号时,不仅会造成发射功率的浪费,还会对处于其他方位的用户产生干扰.然而,虽然阵列天线的方向图是全向的,但是通过一定技术 ...

  9. stm32定时器输出pwmIO口模拟pwm——呼吸灯

    文章目录 前言 一.pwm(脉冲宽度调制) 1.基本原理 2.PWM的优点 3.PWM波的控制方法 二.定时器的相关介绍 1.stm32定时器 2.通用定时器计数模式 3.定时器的基本工作原理 三.定 ...

最新文章

  1. linux jdk安装_linux运维 - 用脚本快速安装jdk
  2. LightOJ 1401 No More Tic-tac-toe 博弈论SG打表
  3. C++中空指针调用类成员函数的原理
  4. 「递归」第3集 | 向善的信念,让技术自带光芒
  5. SSAS(分析服务)优化手册
  6. 第二篇 Python图片处理模块PIL(pillow)
  7. java翻转字符串中的单词
  8. 数组元素循环右移问题
  9. 把一个web项目改名后复制,再把改名后的web项目发布到tomcat,访问出现404错误的解决办法
  10. 使用jmeter快速生成测试报告
  11. 鼠标测试软件m,赛钛客CYBORG M.M.O.7鼠标
  12. Bee 事务注解 @Tran 使用实例
  13. Win11怎么删除微软输入法?
  14. 编码规则的发展历程(通俗版)
  15. 七代处理器装win7_雷神ST(Intel第七代CPU)怎么一键重装win7系统
  16. 第三章:Solr4.7以DIH的方式从数据库导数据
  17. word中的破折号中间有空格?
  18. PS流包格式之PS/SYS/PSM/PES头
  19. 销量不敌理想、小鹏,蔚来掉队了?
  20. git获取增量代码流程

热门文章

  1. Python 中堪称神仙的6个内置函数
  2. CUMTOJ算法实验四
  3. 写魔兽改键时遇到的问题
  4. android+汉王手写引擎,汉王推键盘输入引擎 从手写专家到输入法专家
  5. 苏轼不知月亮上是何年何月 | Python计算出来了
  6. 泰克示波器DPO3054自校准SPC失败维修
  7. Kotlin学习——了解Kotlin
  8. EVA(新世纪福音战士)经典语句
  9. 深圳Java培训未来5-10年的前景怎么样?
  10. 我的扫地机器人让我了解机器人过程自动化(RPA)