互动
最近评论
文档阅读者
stonewu
static uint32_t next_tick = 0; 原: if(HAL_GetTick() >= next_tick) { next_tick = HAL_GetTick() + 100; HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); }//未处理边界条件,溢出导致逻辑非预期。 改动: if(HAL_GetTick()-next_tick <= UINT32_MAX/2 ) { next_tick = HAL_GetTick() + 100; HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); } ***代码分析:以上代码的作用是每隔一段时间翻转IO口。其巧妙之处,在于利用减法配合溢出将原本因为溢出而需要 考虑的【边界判断省去】了。 ***通用处理: static uint32_t next_tick = 0; #define INTERVAL 100 if(HAL_GetTick()-next_tick <= (UINT32_MAX+1-INTERVAL) ) { next_tick = HAL_GetTick() + INTERVAL; HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); } ***【边界判断省去】原理分析: 原式HAL_GetTick()≥next_tick, 移项变形得到HAL_GetTick()-next_tick≥0, *当HAL_GetTick()等于或者略大于next_tick的时候上式无问题,但是当next_tick增加, *HAL_GetTick()小于next_tick时,由于均是uint32_t,不等式左边的值应是恒≥0的,就比原式错得更多了(原式好歹是边界条件时出错,这直接一开始就出错了),但是仔细观察,next_tick+100后大于HAL_GetTick(),那么HAL_GetTick()-next_tick的值是大于2^32-100的, *那么只需要继续变形为HAL_GetTick()-next_tick<=(2^32-100)是不是就可以了呢? 以上不考虑边界条件,考虑边界条件,则分为两种情况: ①next_tick先溢出, next_tick变得很小,而HAL_GetTick()很大,HAL_GetTick()-next_tick则是一个比较大的值,记其值最小情况为UPLIMIT,为了排除此情况,HAL_GetTick()-next_tick应该<或者≤UPLIMIT。 ②HAL_GetTick()、next_tick均溢出, 此情况就同不考虑边界条件时的分析,HAL_GetTick()-next_tick<=(2^32-100)就可以了。 那么最后,HAL_GetTick()-next_tick是应该<还是≤ UINT32_MAX/2,UPLIMIT,(2^32-100)呢?或者说UPLIMIT应该是多少呢? UINT32_MAX/2是怎么来的,不做考究,当INTERVAL的值大于UINT32_MAX/2则HAL_GetTick()-next_tick <= UINT32_MAX/2 会出错; 当INTERVAL取值100,HAL_GetTick()-next_tick应该和(2^32-100)做比较, 即UPLIMIT应取值(2^32-INTERVAL),<或者≤就自己思考吧。 ***温馨提示or思考:以上代码中两次HAL_GetTick()的值可能是不一样的,也就是翻转IO的周期可能不是简单的INTERVAL*2;还有当INTERVAL取值特别大时,代码可能失效,例如HAL_GetTick()-next_tick<=0或者 <=1时,由于时序问题,调用HAL_GetTick()的时机可能很难满足式子成立。 记HAL_GetTick()为A,记next_tick为B,记INTERVAL为c, 当A=A0=B0>=2^32-c时,B1=B0+c-2^32, B=B1、A>A0→A-B=A-B1=A-(B0+c-2^32)>A0-(B0+c-2^32)=2^32-c; 当A=A2=B2<2^32-c时,B3=B2+c, B=B3、B3>A>A2→[A-B]=[A-B3]=[A-(B2+c)]=A-(B2+c)+2^32>A2-(B2+c)+2^32=2^32-c;
文章72
2024
嵌入式GUI专题
嵌入式GUI专题
rs485接入rt-thread的控制台finSH
rs485接入rt-thread的控制台finSH
openharmony笔记
openharmony笔记
你好啊!我是
蒙蒙plus的个人博客
最近发布
结构体赋值操作
结构体赋值操作
rtthread启动流程
rtthread启动流程
RT-Thread仓库目录介绍
RT-Thread仓库目录介绍
最新评论
头像
static uint32_t next_tick = 0; 原: if(HAL_GetTick() >= next_tick) { next_tick = HAL_GetTick() + 100; HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); }//未处理边界条件,溢出导致逻辑非预期。 改动: if(HAL_GetTick()-next_tick <= UINT32_MAX/2 ) { next_tick = HAL_GetTick() + 100; HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); } ***代码分析:以上代码的作用是每隔一段时间翻转IO口。其巧妙之处,在于利用减法配合溢出将原本因为溢出而需要 考虑的【边界判断省去】了。 ***通用处理: static uint32_t next_tick = 0; #define INTERVAL 100 if(HAL_GetTick()-next_tick <= (UINT32_MAX+1-INTERVAL) ) { next_tick = HAL_GetTick() + INTERVAL; HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); } ***【边界判断省去】原理分析: 原式HAL_GetTick()≥next_tick, 移项变形得到HAL_GetTick()-next_tick≥0, *当HAL_GetTick()等于或者略大于next_tick的时候上式无问题,但是当next_tick增加, *HAL_GetTick()小于next_tick时,由于均是uint32_t,不等式左边的值应是恒≥0的,就比原式错得更多了(原式好歹是边界条件时出错,这直接一开始就出错了),但是仔细观察,next_tick+100后大于HAL_GetTick(),那么HAL_GetTick()-next_tick的值是大于2^32-100的, *那么只需要继续变形为HAL_GetTick()-next_tick<=(2^32-100)是不是就可以了呢? 以上不考虑边界条件,考虑边界条件,则分为两种情况: ①next_tick先溢出, next_tick变得很小,而HAL_GetTick()很大,HAL_GetTick()-next_tick则是一个比较大的值,记其值最小情况为UPLIMIT,为了排除此情况,HAL_GetTick()-next_tick应该<或者≤UPLIMIT。 ②HAL_GetTick()、next_tick均溢出, 此情况就同不考虑边界条件时的分析,HAL_GetTick()-next_tick<=(2^32-100)就可以了。 那么最后,HAL_GetTick()-next_tick是应该<还是≤ UINT32_MAX/2,UPLIMIT,(2^32-100)呢?或者说UPLIMIT应该是多少呢? UINT32_MAX/2是怎么来的,不做考究,当INTERVAL的值大于UINT32_MAX/2则HAL_GetTick()-next_tick <= UINT32_MAX/2 会出错; 当INTERVAL取值100,HAL_GetTick()-next_tick应该和(2^32-100)做比较, 即UPLIMIT应取值(2^32-INTERVAL),<或者≤就自己思考吧。 ***温馨提示or思考:以上代码中两次HAL_GetTick()的值可能是不一样的,也就是翻转IO的周期可能不是简单的INTERVAL*2;还有当INTERVAL取值特别大时,代码可能失效,例如HAL_GetTick()-next_tick<=0或者 <=1时,由于时序问题,调用HAL_GetTick()的时机可能很难满足式子成立。 记HAL_GetTick()为A,记next_tick为B,记INTERVAL为c, 当A=A0=B0>=2^32-c时,B1=B0+c-2^32, B=B1、A>A0→A-B=A-B1=A-(B0+c-2^32)>A0-(B0+c-2^32)=2^32-c; 当A=A2=B2<2^32-c时,B3=B2+c, B=B3、B3>A>A2→[A-B]=[A-B3]=[A-(B2+c)]=A-(B2+c)+2^32>A2-(B2+c)+2^32=2^32-c;
文档阅读者 /
广告
引用到评论