模拟PWM波的自适应取阈值算法
模拟PWM波的自适应取阈值算法
前言:
单片机开发中,我们常常接触到的波形就是PWM波,一般都是0~3.3V的数字PWM波,很少涉及模拟PWM波。两者有什么不同呢?模拟PWM波不只有高低电平,还有中间的一些变化过程,且波形可能也不是规整的矩形波,再者它的电压可能超过3.3v导致无法直接用单片机IO口识别,PWM无法直接捕获到上升下降沿。
如果你预算充足,开发的设备也不是非常的小型话,可以容纳额外的电路,那么你可以考虑从硬件上增加一些运放电路或整形电路,尽可能将模拟PWM变为数字PWM,如果不行就得考虑串联电阻来分压,然后代码中通过AD采样到数据后进行算法处理。
自适应算法
自适应取阈值算法思路:
这里的MAX,MIN,LOW电压值都是不确定的,受传输距离和干扰影响。- 具体算法:
- 求出PWM波采样值的MAX和MIN值:监测波形20ms(大于周期15.01ms),采集这段时间内的最大的20个采样值和最小的20个采样值(使用插入排序),舍弃20个数据的头尾各2个共4个数据,对十六个数据求平均值(求和后右移4位)。得出PWM的MAX和MIN值。
- 识别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,如果期间识别到上升沿则会提前结束,这段时间是不固定的。
- 识别PWM波的波峰值High和波谷值Low:PWM开始输出后进行1ms(理论PWM输出时间为1.28ms,监测时间需小于此)的监测,方法同步骤1,求出Low平均值, High平均值等于MAX平均值。
- 取阈值:求出差值HighLowDiff = High - Low,采样值取SampleHigh = High - HighLowDiff/3(1/3运算同步骤3), SampleLow = Low + HighLowDiff/3。
- 根据需要阈值也可选取为(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波的自适应取阈值算法相关推荐
- STM32之PWM波
说起PWM波,做过智能小车的人肯定都很清楚了,其实他就是一种脉宽调制.在智能小车上,我们一般用PWM波来控制小车的速度,通过控制方波中高低电平的比例,来达到控制小车转速的目的.而32的芯片他提供了专门 ...
- 使用Arduino ESP32 通过PWM波控制大疆GM6020以及3508无刷电机(更新)
使用Arduino控制大疆GM6020无刷电机: 话不多说直接上代码 #include <Servo.h> Servo myservo; //创建一个舵机控制对象// Arduino自带的 ...
- 【图像分割】利用粒子群算法与遗传算法实现图像的自适应多阈值的快速分割
文章目录 前言 一.自适应多阈值分割 1.最大类间差方法 2.最大熵方法 二.代码部分(以粒子群算法与遗传算法优化自适应双阈值分割为例) 1. 利用粒子群算法优化最大类间差双阈值分割 1.1 概述 1 ...
- OpenCV —— 阈值分割(直方图技术法,熵算法,Otsu,自适应阈值算法)
阈值分割 1. 全局阈值分割 直方图技术法 熵算法 Otsu算法 2. 局部阈值分割 自适应阈值 阈值的分割的核心就是如何选取阈值,选取正确的阈值时分割成功的关键.可以使用手动设置阈值,也可以采用直方 ...
- request[limit]取不到前台的值_基于uFUN开发板的心率计(二)动态阈值算法获取心率值...
前言 上一篇文章:基于uFUN开发板的心率计(一)DMA方式获取传感器数据,介绍了如何获取PulseSensor心率传感器的电压值,并对硬件电路进行了计算分析.心率计,重要的是要获取到心率值,本篇文章 ...
- 自适应阈值算法(大津阈值法)
最大类间方差法是由日本学者大津于1979年提出的,是一种自适应的阈值确定的方法,又叫大津法,简称OTSU.它是按图像的灰度特性,将图像分成背景和目标2部分.背景和目标之间的类间方差越大,说明构成图像的 ...
- 基于特征点匹配的自适应目标跟踪算法
基于特征点匹配的自适应目标跟踪算法 2016-01-29 13:11 摘 要:由于实际场景复杂多变,目标在运动过程中往往会出现形变.遮挡等问题,增加了跟踪的难度.为了解决上述问题,提出一种基于特征点匹 ...
- 波束赋形技术lms算法在matlab仿真,自适应波束成形算法LMS、RLS、VSSLMS分解
1.传统的通信系统中,基站天线通常是全向天线,此时,基站在向某一个用户发射或接收信号时,不仅会造成发射功率的浪费,还会对处于其他方位的用户产生干扰.然而,虽然阵列天线的方向图是全向的,但是通过一定技术 ...
- stm32定时器输出pwmIO口模拟pwm——呼吸灯
文章目录 前言 一.pwm(脉冲宽度调制) 1.基本原理 2.PWM的优点 3.PWM波的控制方法 二.定时器的相关介绍 1.stm32定时器 2.通用定时器计数模式 3.定时器的基本工作原理 三.定 ...
最新文章
- linux jdk安装_linux运维 - 用脚本快速安装jdk
- LightOJ 1401 No More Tic-tac-toe 博弈论SG打表
- C++中空指针调用类成员函数的原理
- 「递归」第3集 | 向善的信念,让技术自带光芒
- SSAS(分析服务)优化手册
- 第二篇 Python图片处理模块PIL(pillow)
- java翻转字符串中的单词
- 数组元素循环右移问题
- 把一个web项目改名后复制,再把改名后的web项目发布到tomcat,访问出现404错误的解决办法
- 使用jmeter快速生成测试报告
- 鼠标测试软件m,赛钛客CYBORG M.M.O.7鼠标
- Bee 事务注解 @Tran 使用实例
- Win11怎么删除微软输入法?
- 编码规则的发展历程(通俗版)
- 七代处理器装win7_雷神ST(Intel第七代CPU)怎么一键重装win7系统
- 第三章:Solr4.7以DIH的方式从数据库导数据
- word中的破折号中间有空格?
- PS流包格式之PS/SYS/PSM/PES头
- 销量不敌理想、小鹏,蔚来掉队了?
- git获取增量代码流程