超时溢出有问题
_timer_ticks 是一个无符号数, 假如是一个无符号8位,当_timer_ticks为254,我超时为2m, target->timeout此时为0吧,这时候判断感觉出现问题了,会立马导致超时事件发生
可以通过增加溢出标识来处理,具体如下: 1.增加 static uint8_t _timer_overflow_flag = 0; 2.Timer结构体:
typedef struct Timer {
uint32_t timeout;
uint32_t repeat;
uint8_t overflow_flag;
void (*timeout_cb)(void);
struct Timer* next;
}Timer;
- timer_ticks函数改为:
void timer_ticks()
{
_timer_ticks++;
if (_timer_ticks == 0) {
_timer_overflow_flag = 1;
}
}
4.timer_loop函数改为:
void timer_loop()
{
struct Timer* target;
uint8_t flag = _timer_overflow_flag;
_timer_overflow_flag = 0;
for(target=head_handle; target; target=target->next) {
if((flag == target->overflow_flag && _timer_ticks >= target->timeout) || flag > target->overflow_flag) {
target->overflow_flag = 0;
if(target->repeat == 0) {
timer_stop(target);
} else {
target->timeout = _timer_ticks + target->repeat;
if (target->timeout < _timer_ticks) {
target->overflow_flag = 1;
}
}
target->timeout_cb();
}
}
}
5.最后timer_init函数也要加上初始化的标识: handle->overflow_flag = _timer_overflow_flag; 有可能有考虑不周到的情况,可以帮忙检查下这样是否有漏洞。
//fix bug when ticks overflow
if((int)((uint32_t)(target->timeout -_timer_ticks)) <= 0) {
}
这样修改即可,修改后暂时没有再测到溢出时出现我所述问题
//fix bug when ticks overflow if((int)((uint32_t)(target->timeout -_timer_ticks)) <= 0) { }这样修改即可,修改后暂时没有再测到溢出时出现我所述问题
其实还有一种更优雅的解决方法~采用双链表去维护,溢出时切换链表
if( (_time_ticks - target->time) < (UINT32_MAX)/2)
结构体多增加了一个target->time,用于在启动定时器时缓存,target->time = _time_ticks+ target->timeout;
target->timeout 值不能超过(UINT32_MAX)/2
//fix bug when ticks overflow if((int)((uint32_t)(target->timeout -_timer_ticks)) <= 0) { }这样修改即可,修改后暂时没有再测到溢出时出现我所述问题
赞同,这种方式是最简单而且容易理解的: 以一个字节的长度为例,当_timer_ticks=254时,定时3ms,即target->timeout=1时发生超时 根据target->timeout -_timer_ticks <= 0 条件 当_timer_ticks=254时,1-254=3,大于0,未超时 当_timer_ticks=255时,1-255=2,大于0,未超时 当_timer_ticks=0时,1-0=1,大于0,未超时 当_timer_ticks=1时,1-1=0,等于0,超时 考虑到超时判断是在主循环进行的,可能会出现判断的时候错过了timer_ticks=1的情况 当_timer_ticks=2时,1-2=-1,小于0,超时 当_timer_ticks=3时,1-3=-2,小于0,超时
是不是可以换个思路,解决溢出的原因是以为溢出后会导致计时器不准。 可以将_timer_ticks定义为64位的,这样即使1ms累加一次,也要上亿年,设备也运行不到那个时候。
这个方案是否可行:timeout参数采用倒计时。 (1)在timer_init函数中timeout参数采用倒计时的方式,不需要算上当前的时间戳; (2)在HAL_IncTick中每次减去tick,减到0时,增加一个标志位 (3)在timer_loop中检查标志位。
typedef struct SortTimer { uint32_t timestart; uint32_t interval; uint32_t repeat; void (timeout_cb)(); struct SortTimer next; } SortTimer; 结构体timeout修改为timestart,增加一个interval; if (_timer_ticks - target->timestart > target->interval) 这样就不存在定时时间必须小于UINT32_MAX/2问题,但是还是存在一个长定时而且主循环超时导致时间点错过的问题 以byte类型为例,假如当前tick为254,定时时长253,这样只有3次tick满足,如果主循环超过3次tick,就会造成错过时间点。 而if((int32_t)((uint32_t)(target->timeout -_timer_ticks)) <= 0)方式存在定时时长不能超过UINT32_MAX/2 完全处理还是要针对UINT32_MAX/2设置标志位,或者设置_timer_ticks过零标志位。 if((_timer_ticks - target->timestart > target->interval)|| (target->overFlag == 1)&&(_timer_ticks > target->timestart)){ target->overFlag = 0;//在_timer_ticks过零时设置 } _timer_ticks - target->timestart > target->interval
timestart :254 interval:253 timeout:251
ticks = 255 1<253满足 ticks = 1 2<253满足 ...... ticks = 250 252<253满足
ticks = 251 253==253不满足,时间到 ticks = 252 254>253不满足,时间到 ticks = 253 255>253不满足,时间到
ticks = 254 0<253满足,时间错过 ticks = 255 1<253满足,时间错过 ticks = 1 2<253满足,时间错过
是不是可以换个思路,解决溢出的原因是以为溢出后会导致计时器不准。 可以将_timer_ticks定义为64位的,这样即使1ms累加一次,也要上亿年,设备也运行不到那个时候。
简单粗暴而不失优雅,哈哈, 在判断时再加个锁可以防止8位、16位、32位CPU的汇编中断问题。
感谢您的来信,我会尽快回复您! ——卓琳
//fix bug when ticks overflow if((int)((uint32_t)(target->timeout -_timer_ticks)) <= 0) { }这样修改即可,修改后暂时没有再测到溢出时出现我所述问题
赞同,这种方式是最简单而且容易理解的: 以一个字节的长度为例, 当_timer_ticks=254时,定时3ms,即target->timeout=1时发生超时 根据target->timeout -_timer_ticks <= 0 条件 当_timer_ticks=254时,1-254=3,大于0,未超时 当_timer_ticks=255时,1-255=2,大于0,未超时 当_timer_ticks=0时,1-0=1,大于0,未超时 当_timer_ticks=1时,1-1=0,等于0,超时 考虑到超时判断是在主循环进行的,可能会出现判断的时候错过了timer_ticks=1的情况 当_timer_ticks=2时,1-2=-1,小于0,超时 当_timer_ticks=3时,1-3=-2,小于0,超时
//fix bug when ticks overflow
if((int32_t)((uint32_t)(target->timeout -_timer_ticks)) <= 0) {
}
这样严谨点
感谢您的来信,我会尽快回复您! ——卓琳
推荐另外一种的解决方法: 不用设置标志,不用去考虑有符号无符号中间的转换。
定义一个获取时间长度的宏 TIME_PASSED
#define TIME_MAX 0xFFFFFFFFu /* 最大时间长度取决于使用的timer计时器长度,这里以32bit为例 */
#define TIME_PASSED(now, since) ((now) >= (since)) ? ((now) -(since)) : ((now) + (1 + TIME_MAX - (since)))
参考资料:《嵌入式系统设计与实践》5.4.2 p138
感谢您的来信,我会尽快回复您! ——卓琳
//fix bug when ticks overflow if((int)((uint32_t)(target->timeout -_timer_ticks)) <= 0) { }这样修改即可,修改后暂时没有再测到溢出时出现我所述问题
是否应该改成这样: if((int)((uint32_t)(target->timeout -_timer_ticks)) < 0) {
} 就是把等于0的情况去掉,不然永远进不去这个条件:if(_timer_ticks >= target->timeout)
感谢您的来信,我会尽快回复您! ——卓琳
实际上这里可以直接使用减法,不用考虑溢出条件,无符号减法本身就是借位操作,比如说,我们定义两个32位无符号整数: uint32_t a = x; uint32_t b = y; 当x < y 时,a - b的计算结果实际上就是2^32 + (x - y)。
感谢您的来信,我会尽快回复您! ——卓琳
实际上这里可以直接使用减法,不用考虑溢出条件,无符号减法本身就是借位操作,比如说,我们定义两个32位无符号整数: uint32_t a = x; uint32_t b = y; 当x < y 时,a - b的计算结果实际上就是2^32 + (x - y)。
应该是2^32+1+(x-y),就像1个字节补码,256表示0,255表示-1。
感谢您的来信,我会尽快回复您! ——卓琳