马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
<p style="text-align:justify;text-justify:inter-ideograph"><span style="font-family:华文仿宋">来源:公众号【鱼鹰谈单片机】</span><br/> <span style="font-family:华文仿宋">作者:鱼鹰</span>Osprey<br/> ID <span style="font-family:华文仿宋">:</span>emOsprey<br/> <span style="font-family:华文仿宋">相信很多道友都有对输入</span>IO <span style="font-family:华文仿宋">口进行滤波的需求,比如按键输入、红外对管输入等。这里鱼鹰就以按键为例介绍如何进行较为高效的滤波。我们以为接入单片机引脚的按键按下后(并弹起)电平变化应该是这样的:</span></p><p style="text-align:justify;text-justify:inter-ideograph"><img src="/upload/article/20221214/1670982644374799.png" title="1670982644374799.png" alt="image.png"/></p><p style="text-align:justify;text-justify:inter-ideograph"><span style="font-family:华文仿宋">实际上却是这样的:</span></p><p style="text-align:justify;text-justify:inter-ideograph"><img src="/upload/article/20221214/1670982665192198.png" title="1670982665192198.png" alt="image.png"/></p><p> </p><p><span style="font-family:华文仿宋">首先思考一个问题,如果没有进行滤波,会有什么问题?一次按下过程可能被认为多次按下,因为按下后有抖动过程,这个过程电平并不稳定,导致单片机在很短的时间内多次检测到低电平状态。这样一来,本来只按下了一次,程序却认为按下了多次,这对按键功能会产生影响。如果将按键引脚设置为外部中断触发,那么在极短的时间内</span>CPU<span style="font-family:华文仿宋">将多次进入中断,影响中断的性能(所以对于非数字接口,即没有稳定的高低电平的接口,如果不需要非常高的实时性,那么鱼鹰不建议设置为外部中断触发方式)。那么我们该如何进行处理呢?很自然的,因为按下过程中有抖动期,我们就会想办法跳过抖动时间,然后再检测电平变化,所以,</span>V0.1 <span style="font-family:华文仿宋">版本就应运而生,这也是郭天祥老师告诉我们初学者最简单易懂的方式:</span></p><p>V0.1</p><p><span style="font-family:宋体">typedef enum {</span></p><p><span style="font-family:宋体">KEY_LEVEL_DOWN, // </span><span style="font-family: 华文仿宋">假设低电平为按下</span></p><p><span style="font-family:宋体">KEY_LEVEL_UP, }KeyLevelTypedef;<br/> KeyLevelTypedef get_key_level()</span></p><p><span style="font-family:宋体">{ return (KeyLevelTypedef)HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); </span></p><p><span style="font-family:宋体">}<br/> // V0.1</span></p><p><span style="font-family:宋体">void key_scan()</span></p><p><span style="font-family:宋体">{ // </span><span style="font-family:华文仿宋">欢迎关注:鱼鹰谈单片机</span></p><p><span style="font-family:宋体">if(get_key_level() == KEY_LEVEL_DOWN) </span></p><p><span style="font-family:宋体">{ HAL_Delay(20); // </span><span style="font-family:华文仿宋">假设抖动时间</span><span style="font-family:宋体"> 20 ms</span></p><p><span style="font-family:宋体"> if(get_key_level() == KEY_LEVEL_DOWN)</span></p><p><span style="font-family:宋体">{ key_flag = 1;// </span><span style="font-family: 华文仿宋">按键按下标志位</span></p><p><span style="font-family:宋体">}</span></p><p><span style="font-family:宋体">}</span></p><p><span style="font-family:宋体">}</span></p><p><span style="font-family:华文仿宋">对于初学者而言,这段代码简单易懂,但是对于工作多年的人来说,这种方式效率极其低下。有可能你会说,如果使用操作系统,当延时函数使用系统延时,那么这段时间就可以切换到其他任务进行处理,而不会浪费</span>CPU<span style="font-family:华文仿宋">使其空转了。但是如果这个任务本身功能比较复杂,那么这种处理会严重影响其他功能的执行,所以这种代码应该不会出现在工作多年的工程师手中。那么是否有更高效的方式呢?有,就是记录前后两次电平的变化,通过比较两次电平是否相等来确定电平是否稳定(这个方式在《</span><a href="http://mp.weixin.qq.com/s?__biz=MzU2MDgyNzgyMw==&mid=2247484066&idx=1&sn=6b916952101d45fad77fbb351057ffe6&chksm=fc035e62cb74d7742ce270e814fbd7645e0c5e0729818e0a9be8cdb3728aef2cc9e376e37243&scene=21#wechat_redirect" target="_blank"><span style="font-family:华文仿宋;color:#3D6BA7;text-underline:none">延时功能进化论</span><span style="color:#3D6BA7;text-underline:none">(</span><span style="font-family:华文仿宋;color:#3D6BA7;text-underline:none">合集</span><span style="color:#3D6BA7;text-underline:none">)</span></a><span style="font-family:华文仿宋">》有做简单介绍)。</span>V1.0<br/> <span style="font-family:宋体">typedef enum </span></p><p><span style="font-family:宋体">{ </span></p><p><span style="font-family:宋体">KEY_STATE_IDLE, // </span><span style="font-family:华文仿宋">按键空闲</span><span style="font-family:宋体"> </span></p><p><span style="font-family:宋体">KEY_STATE_DOWN, // </span><span style="font-family:华文仿宋">按键按下</span><span style="font-family:宋体"> </span></p><p><span style="font-family:宋体">KEY_STATE_FINISH, // </span><span style="font-family:华文仿宋">按键处理完成(由应用程序设置)</span><span style="font-family:宋体"> </span></p><p><span style="font-family:宋体">}KeyStateTypedef;<br/> KeyStateTypedef key_state;</span></p><p><span style="font-family:宋体">KeyLevelTypedef key_last_level; // </span><span style="font-family:华文仿宋">上次电平状态</span><span style="font-family:宋体"><br/> // V1.0</span></p><p><span style="font-family:宋体">// </span><span style="font-family:华文仿宋">函数调用周期</span><span style="font-family:宋体"> 20 ms</span><span style="font-family:华文仿宋">(如何实现应该不需要再说明了吧)</span></p><p><span style="font-family:宋体">void key_scan()</span></p><p><span style="font-family:宋体">{ // </span><span style="font-family:华文仿宋">欢迎关注:鱼鹰谈单片机</span><span style="font-family:宋体"> K</span></p><p><span style="font-family:宋体">eyLevelTypedef temp; // </span><span style="font-family:华文仿宋">可不可以不使用这个中间变量?</span><span style="font-family:宋体"> </span></p><p><span style="font-family:宋体">temp = get_key_level(); </span></p><p><span style="font-family:宋体">if(temp != key_last_level)</span></p><p><span style="font-family:宋体">{ key_last_level = temp; </span></p><p><span style="font-family:宋体">return; </span></p><p><span style="font-family:宋体">} </span></p><p><span style="font-family:宋体">// </span><span style="font-family:华文仿宋">当运行到这里,说明电平已经稳定下来了</span><span style="font-family:宋体"> </span></p><p><span style="font-family:宋体">if(temp == KEY_LEVEL_DOWN)</span></p><p><span style="font-family:宋体">{ if(key_state == KEY_STATE_IDLE)</span></p><p><span style="font-family:宋体">{ // </span><span style="font-family:华文仿宋">确保曾经释放过按键,这样可以保证在按下时不会不停设置该标志位</span><span style="font-family:宋体"> key_state = KEY_STATE_DOWN;// </span><span style="font-family:华文仿宋">按键按下标志位</span><span style="font-family: 宋体"> </span></p><p><span style="font-family:宋体">} </span></p><p><span style="font-family:宋体">} </span></p><p><span style="font-family:宋体">else{ </span></p><p><span style="font-family:宋体">if(key_state == KEY_STATE_FINISH)</span></p><p><span style="font-family:宋体">{ // </span><span style="font-family:华文仿宋">防止多线程情况下同时修改</span><span style="font-family:宋体"> </span></p><p><span style="font-family:宋体"> key_state = KEY_STATE_IDLE; // </span><span style="font-family:华文仿宋">释放按键</span><span style="font-family:宋体"> </span></p><p><span style="font-family:宋体"> } </span></p><p><span style="font-family:宋体">}</span></p><p><span style="font-family:宋体">}</span></p><p><span style="font-family:华文仿宋">在这里,使用了两个全局变量,一个是</span> key_state<span style="font-family:华文仿宋">,一个是</span> key_last_level<span style="font-family:华文仿宋">。前者共三种状态,这是为了防止按键扫描和按键处理程序不是顺序执行而设定的。当你按下按键后,保证按键处理程序必然可以得到按下状态,同时只有释放了按键之后才可以更改状态位,然后才能再次触发。这样可以保证按键扫描和按键处理得以顺序执行(这里面的关系需要考虑清楚,否则的就会写出有</span> BUG <span style="font-family:华文仿宋">的程序)。而后者只在本函数使用,所以不存在使用风险(前提是没有多个任务同时调用该函数,否则照样有风险)。可以看到该代码没有任何延时函数,简单、高效,当然这里有一个前提,那就是该函数的调用周期必须大于抖动时间,但是也别太长,否则实时性不好。假设抖动期时间为</span> 20 ms<span style="font-family:华文仿宋">,实现</span> 20 ms <span style="font-family:华文仿宋">的调用周期有很多种方法:</span>1<span style="font-family:华文仿宋">、中断定时器定时调用</span>2<span style="font-family:华文仿宋">、软件定时器调用(需操作系统)</span>3<span style="font-family:华文仿宋">、线程内周期执行该函数(需操作系统)</span>4<span style="font-family:华文仿宋">、使用鱼鹰介绍的方式(《</span><a href="http://mp.weixin.qq.com/s?__biz=MzU2MDgyNzgyMw==&mid=2247484046&idx=1&sn=95a3f52eca657b7a0c42e0dee9504e7e&chksm=fc035e4ecb74d758ae26dd65ac579752dd1969d0c92cbd92c59fc6af48a1549bf400a9f263da&scene=21#wechat_redirect" target="_blank"><span style="font-family:华文仿宋;color:#3D6BA7;text-underline:none">延时功能进化论之</span><span style="color:#3D6BA7;text-underline:none">V2.5~V2.7</span><span style="font-family:华文仿宋;color:#3D6BA7;text-underline:none">(鱼鹰强烈建议)</span></a><span style="font-family:华文仿宋">》)我们再次看这个图:</span></p><p><img src="/upload/article/20221214/1670982686742040.png" title="1670982686742040.png" alt="image.png"/></p><p><span style="font-family:华文仿宋">如果我们使用</span> V1.0 <span style="font-family:华文仿宋">的方式,我们就会发现,当程序运行在抖动期,因为函数调用的时间大于抖动时间,那么程序总是可以得到稳定后的状态。比如空闲状态下(</span>key_last_level<span style="font-family:华文仿宋">为高电平),突然按下按键,假设在抖动中期程序检测到高电平,那么</span>20 ms <span style="font-family:华文仿宋">后检测的是低电平,显然这是不相等的(</span>key_last_level<span style="font-family:华文仿宋">更新为低电平),那么程序就会执行下一次,下一次即</span>40 ms <span style="font-family:华文仿宋">后检测肯定是低电平(如果不是,说明电平不稳定),此时电平相等,即可认为电平稳定了。而如果在抖动中期程序检测到低电平,那么</span>20 ms <span style="font-family:华文仿宋">后检测的应该还是低电平,那么程序认为此时电平已经稳定了,那也没有问题,因为它已经跳过了抖动期。</span>V2.0<span style="font-family:华文仿宋">如果说,滤波只有按键这种抖动的话,那么上述方式应该算很不错了,但有时对</span>IO<span style="font-family:华文仿宋">滤波的需求比较复杂,那么上述方式只可参考,不可直接拿来对任何</span> IO <span style="font-family:华文仿宋">进行滤波。而且很多时候,程序需要定时检测多个</span> IO <span style="font-family:华文仿宋">的电平状态,当电平发生变化时,我们能及时通知应用层,而且只在电平发生变化时才进行通知。但与此同时我们需要在电平稳定之后才通知,而不是变化后马上进行通知,否则可能在电平抖动时多次通知。所以针对这种需求,我们需要设计一个更加通用一些的滤波函数,能应对所有数字</span> IO <span style="font-family:华文仿宋">的滤波(包括按键)。其实按键滤波已经包含了滤波思想,只是不够通用,需要进一步改进。</span></p><p>typedef enum</p><p>{</p><p>LEVEL_LOW, //</p><p>LEVEL_HIGH,</p><p>}LevelTypedef;<br/> typedef struct</p><p>{ uint32_t last_time; // <span style="font-family:华文仿宋">上次时间</span> </p><p> LevelTypedef last_level; // <span style="font-family:华文仿宋">上次电平状态</span></p><p>}FilterParaTypedef;</p><p><br/> // V2.0</p><p>// para <span style="font-family: 华文仿宋">滤波变量,</span>level <span style="font-family:华文仿宋">当前检测电平状态,</span> time <span style="font-family:华文仿宋">当前时间戳,单位</span> 1 ms, stable_time<span style="font-family:华文仿宋">希望电平稳定的时间</span></p><p>uint8_t filter(FilterParaTypedef *para, LevelTypedef level, uint32_t time, uint32_t stable_time)</p><p>{ // <span style="font-family:华文仿宋">欢迎关注:鱼鹰谈单片机</span></p><p> if(level != para->last_level)</p><p>{ para->last_level = level; // <span style="font-family:华文仿宋">更新当前电平状态</span></p><p>para->last_time = time; // <span style="font-family:华文仿宋">更新电平变化的时刻</span></p><p>return 0; // <span style="font-family: 华文仿宋">电平未稳定</span></p><p>}</p><p>if(time - para->last_time > stable_time)</p><p>{ // <span style="font-family:华文仿宋">这两个条件可以放在一起进行</span> && <span style="font-family:华文仿宋">判断吗?</span></p><p>return 1; // <span style="font-family: 华文仿宋">需要上报</span> </p><p> }return 0; // <span style="font-family:华文仿宋">电平稳定时间不够长</span></p><p>}</p><p><span style="font-family:华文仿宋">这个代码的思想就是,当电平不稳定时,更新当前时间戳,一旦电平不再变化,并且持续的时间够长(这个时间由用户决定),那么返回</span> 1 <span style="font-family:华文仿宋">表示电平已经稳定了(这个函数没有调用周期限制,调用周期不同,会产生一些影响,这个和滤波时间精度有关)。这个代码看起来挺简单的,也好像没啥问题,但实际上是存在问题的。看到那个稳定时间判断条件了吗?如果下次继续执行这个函数,那么程序依然返回</span> 1<span style="font-family:华文仿宋">,所以它总是会在稳定后不停的返回</span> 1<span style="font-family:华文仿宋">(判断条件总是成立),这样一来,这个函数并不能实现电平变化后才进行通知,也就是说调用者无法通过返回值直接决定下一步动作。可能你会说,如果在返回</span> 1 <span style="font-family:华文仿宋">之前先更新一下时间戳呢?看过鱼鹰之前笔记的应该知道,这种方式会周期性返回</span> 1<span style="font-family:华文仿宋">,即如果希望电平稳定时间为</span> 10 ms<span style="font-family:华文仿宋">,那么在电平稳定后,每隔</span> 10 ms <span style="font-family:华文仿宋">返回</span> 1<span style="font-family:华文仿宋">,这是我们不希望看到的。那么有没有什么解决办法呢?当然。因为我们只希望在变化之后再稳定时才返回</span>1<span style="font-family:华文仿宋">,即我们既希望短暂电平变化并不返回</span>1<span style="font-family:华文仿宋">,而那些长时间稳定的电平能在稳定时间阈值之后返回</span>1<span style="font-family:华文仿宋">,又希望在稳定之后只返回一次</span>1<span style="font-family:华文仿宋">,之后电平变化后如果再次稳定才返回</span>1<span style="font-family:华文仿宋">。有点绕口,看图好了:</span></p><p><img src="/upload/article/20221214/1670982709665027.png" title="1670982709665027.png" alt="image.png"/></p><p><span style="font-family:华文仿宋">因为目前判断条件总是返回</span>1<span style="font-family:华文仿宋">,所以我们需要增加限制条件,让它不总是返回</span>1<span style="font-family:华文仿宋">。简单的办法是,增加一个变量,用于记录上次的稳定后的电平,比如这样:</span></p><p><span style="font-family:宋体">typedef enum </span></p><p><span style="font-family:宋体">{ </span></p><p><span style="font-family:宋体">LEVEL_LOW, // </span></p><p><span style="font-family:宋体">LEVEL_HIGH, </span></p><p><span style="font-family:宋体">}LevelTypedef;<br/> typedef struct </span></p><p><span style="font-family:宋体">{ uint32_t last_time; // </span><span style="font-family:宋体">上次时间 </span></p><p><span style="font-family:宋体">LevelTypedef last_level; // </span><span style="font-family:宋体">上次电平状态 </span></p><p><span style="font-family:宋体">LevelTypedef last_stable_level; // </span><span style="font-family:宋体">上次稳定的电平状态 </span></p><p><span style="font-family:宋体">}FilterParaTypedef;</span></p><p><span style="font-family:宋体"><br/> // V2.0</span></p><p><span style="font-family:宋体">// para </span><span style="font-family:宋体">滤波变量,level 当前检测电平状态, time 当前时间戳,单位 1 ms, stable_time希望电平稳定的时间</span></p><p><span style="font-family:宋体">uint8_t filter(FilterParaTypedef *para, LevelTypedef level, uint32_t time, uint32_t stable_time)</span></p><p><span style="font-family:宋体">{ // </span><span style="font-family:宋体">欢迎关注:鱼鹰谈单片机 </span></p><p><span style="font-family:宋体">if(level != para->last_level)</span></p><p><span style="font-family:宋体">{ para->last_level = level; // </span><span style="font-family:宋体">更新当前电平状态 </span></p><p><span style="font-family:宋体"> para->last_time = time; // </span><span style="font-family:宋体">更新电平变化的时刻 </span></p><p><span style="font-family:宋体"> return 0; // </span><span style="font-family:宋体">电平未稳定 </span></p><p><span style="font-family:宋体">} </span></p><p><span style="font-family:宋体"> if(time - para->last_time > stable_time){ // </span><span style="font-family:宋体">这两个条件可以放在一起进行 && 判断吗? </span></p><p><span style="font-family:宋体">if(level != para->last_stable_level) </span></p><p><span style="font-family:宋体">{ // </span><span style="font-family:宋体">电平稳定时间够长且电平发生了变化 </span></p><p><span style="font-family:宋体">para->last_stable_level = level; </span></p><p><span style="font-family:宋体"> return 1; // </span><span style="font-family:宋体">需要上报 </span></p><p><span style="font-family:宋体">} </span></p><p><span style="font-family:宋体">} </span></p><p><span style="font-family:宋体"> return 0; // </span><span style="font-family:宋体">电平稳定时间不够长</span></p><p><span style="font-family:宋体">}</span></p><p><span style="font-family:华文仿宋">这样一来,下一次继续执行时,就不会再次返回</span>1<span style="font-family:华文仿宋">了。但是以上代码其实是有一个隐含问题的,那就是如果两次长时间电平之间有一个短时间的不同电平存在,那么也只会上报一次,即返回</span></p><p><span style="font-family:华文仿宋">一次</span>1<span style="font-family:华文仿宋">,即如下情况</span></p><p><img src="/upload/article/20221214/1670982723558289.png" title="1670982723558289.png" alt="image.png"/></p><p><span style="font-family:华文仿宋">如果说这是你想要的效果,那么恭喜你,你不用更改代码;但如果这不是你想要的结果,那这个代码就存在</span>BUG<span style="font-family:华文仿宋">,毕竟变化的时间虽然短,但还是变化了的嘛(这个问题稍后讨论)。还有一个问题,看过鱼鹰以前笔记的人都知道,这种计时方式是存在问题的,因为如果你的电平稳定时间很长,长到四字节计时器溢出了,那么就可能出问题。不过在这里,即使出现溢出,也没关系,结果是一样的,因为如果电平稳定时间很长了,那么肯定已经上报过一次了,后面肯定也不需要再次上报了。</span>V2.5V2.0<span style="font-family:华文仿宋">方式确实很高效,但是为了只在变化时上报一次,就要增加一个变量还是很不爽的,如果说鱼鹰没有找到好的方式,那么鱼鹰会采用的,但凑巧的是,鱼鹰想到了更好的方式,不需要增加这个变量也能达到效果。一个用于计时,一个用于记录上次电平,这两个变量肯定是不可或缺的。但是如果你仔细思考一下,就会发现,所谓的记录上次电平,其实是在变化时就被快速更改了的,它记录的是实时电平变化,而计时是在变化后更新时间戳,稳定时判断稳定时间,如果我们把计时顺序换一下,会如何呢?即,稳定时更新时间戳,变化时判断稳定时间,而记录电平的变量只记录已稳定的电平,会怎么样?</span></p><p><br/></p><p><span style="font-family:宋体">typedef struct { uint32_t last_time; // </span><span style="font-family:宋体">上次时间 LevelTypedef last_stable_level; // 上次稳定的电平状态 }FilterParaTypedef;<br/> // V2.5// para 滤波变量,level 当前检测电平状态, time 当前时间戳,单位 1 ms, stable_time希望电平稳定的时间uint8_t filter(FilterParaTypedef *para, LevelTypedef level, uint32_t time, uint32_t stable_time){ // 欢迎关注:鱼鹰谈单片机 if(level != para->last_stable_level){ if(time - para->last_time > stable_time) { para->last_stable_level = level; // 如果这次电平稳定时间足够长,那么记录这次稳定的电平return 1; // 上报} return 0; // 不上报,同时不更新时间戳(稳定时间不够)}<br/> para->last_time = time; // 不断更新电平稳定时间,保存电平稳定时的时间戳 return 0; // 不上报}</span></p><p><span style="font-family:华文仿宋">上面的代码比</span>V2.0<span style="font-family:华文仿宋">简单了许多,但也稍微难理解,但如果你仿真测试一番,其实也容易理解。测试代码(</span>rt_tick_get() <span style="font-family:华文仿宋">函数用于获取当前时间,单位</span> ms<span style="font-family:华文仿宋">):</span></p><p><span style="font-family:宋体">FilterParaTypedef FilterPara;<br/> void task(void *parameter)</span></p><p><span style="font-family:宋体">{ while(1)</span></p><p style="text-indent:16px"><span style="font-family:宋体"> { LevelTypedef temp = (LevelTypedef)HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); if(filter(&FilterPara, temp, rt_tick_get(), 100)) { rt_kprintf('stable level is %u\n',temp); } rt_thread_delay(5); } }</span></p><p><br/></p><p><span style="font-family: 华文仿宋;">在这个例子中,要求电平稳定时间</span>20 ms<span style="font-family: 华文仿宋;">,而线程的执行周期为</span> 5 ms<span style="font-family: 华文仿宋;">,即电平采样率为</span>5 ms<span style="font-family: 华文仿宋;">,当你的手速点击足够快时(如果不够快,可以加长</span> 20 ms<span style="font-family: 华文仿宋;">),那么应该不会有任何打印信息输出。需要注意的是,采样率比较关键,如果电平变化快,而采样率设置的不合适,那么不能完全反应外界引脚电平的变化,这个和</span>“<span style="font-family: 华文仿宋;">香农定理</span>”<span style="font-family: 华文仿宋;">有关,超出鱼鹰的范围,就不多说了。</span>V3.0<span style="font-family: 华文仿宋;">有的时候需求可能要求只需要稳定一个高电平或者低电平才上报,其他时候不上报,那么该如何修改</span>V2.5<span style="font-family: 华文仿宋;">的代码呢?上报时加入限制条件即可,如下所示:</span></p><p><span style="font-family:宋体">// V3.0// para </span><span style="font-family: 华文仿宋">滤波变量,</span><span style="font-family:宋体">level </span><span style="font-family:华文仿宋">当前检测电平状态,</span><span style="font-family:宋体"> time </span><span style="font-family:华文仿宋">当前时间戳,单位</span><span style="font-family:宋体"> 1 ms, stable_time</span><span style="font-family:华文仿宋">希望电平稳定的时间</span></p><p><span style="font-family:宋体">uint8_t filter(FilterParaTypedef *para, LevelTypedef level, uint32_t time, uint32_t stable_time)</span></p><p><span style="font-family:宋体">{ // </span><span style="font-family:华文仿宋">欢迎关注:鱼鹰谈单片机</span><span style="font-family:宋体"> </span></p><p><span style="font-family:宋体">if(level != para->last_stable_level)</span></p><p><span style="font-family:宋体">{ if(time - para->last_time > stable_time) </span></p><p><span style="font-family:宋体">{ para->last_stable_level = level; // </span><span style="font-family:华文仿宋">如果这次电平稳定时间足够长,那么记录这次稳定的电平</span></p><p><span style="font-family:宋体">if(level == LEVEL_HIGH) // LEVEL_HIGH </span><span style="font-family:华文仿宋">可以作为</span><span style="font-family:宋体"> para </span><span style="font-family:华文仿宋">的成员变量参数传入,方便适应其他电平</span><span style="font-family: 宋体"> </span></p><p><span style="font-family:宋体">{return 1; // </span><span style="font-family: 华文仿宋">上报</span><span style="font-family:宋体">} </span></p><p><span style="font-family:宋体">} </span></p><p><span style="font-family:宋体">return 0; // </span><span style="font-family: 华文仿宋">不上报</span><span style="font-family:宋体">,</span><span style="font-family:华文仿宋">同时不更新时间戳(稳定时间不够)</span><span style="font-family:宋体">}<br/> para->last_time = time; // </span><span style="font-family:华文仿宋">不断更新电平稳定时间,保存电平稳定时的时间戳</span><span style="font-family:宋体"> return 0; // </span><span style="font-family:华文仿宋">不上报</span><span style="font-family:宋体">}</span></p><p><span style="font-family:'inherit',serif"> </span></p><p><span style="font-family:华文仿宋">这样一来,只会在高电平稳定时才会进行上报,而低电平却不会上报。但是这种方式同样有一个隐藏限制,那就是低电平必须能稳定一段时间,否则下次高电平无法上报,照样有</span> V2.0 <span style="font-family:华文仿宋">的限制,如何打破这种限制呢?</span>V3.1<span style="font-family:华文仿宋">如果我们的需求是,变化后高电平稳定时上报一次,如果之后存在低电平,然后又变为高电平,并且稳定了,那么希望也能上报,那该如何处呢?</span></p><p><img src="/upload/article/20221214/1670982775709295.png" title="1670982775709295.png" alt="image.png"/></p><p><br/></p><p><br/></p> |