MultiTimer icon indicating copy to clipboard operation
MultiTimer copied to clipboard

超时溢出有问题

Open longdelu opened this issue 7 years ago • 20 comments

99 _timer_ticks 是一个无符号数, 假如是一个无符号8位,当_timer_ticks为254,我超时为2m, target->timeout此时为0吧,这时候判断感觉出现问题了,会立马导致超时事件发生

longdelu avatar Sep 14 '18 04:09 longdelu

可以通过增加溢出标识来处理,具体如下: 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;
  1. 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; 有可能有考虑不周到的情况,可以帮忙检查下这样是否有漏洞。

angwangiot avatar Nov 02 '18 06:11 angwangiot

    //fix bug when ticks overflow
    if((int)((uint32_t)(target->timeout -_timer_ticks)) <= 0) {
       
    }

这样修改即可,修改后暂时没有再测到溢出时出现我所述问题

longdelu avatar Nov 15 '18 04:11 longdelu

    //fix bug when ticks overflow
    if((int)((uint32_t)(target->timeout -_timer_ticks)) <= 0) {
       
    }

这样修改即可,修改后暂时没有再测到溢出时出现我所述问题

其实还有一种更优雅的解决方法~采用双链表去维护,溢出时切换链表

jiejieTop avatar May 02 '20 03:05 jiejieTop

if( (_time_ticks - target->time) < (UINT32_MAX)/2) 结构体多增加了一个target->time,用于在启动定时器时缓存,target->time = _time_ticks+ target->timeout;
target->timeout 值不能超过(UINT32_MAX)/2

IYQ-Github avatar May 23 '20 09:05 IYQ-Github

    //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,超时

VanchTan avatar Jul 23 '20 02:07 VanchTan

是不是可以换个思路,解决溢出的原因是以为溢出后会导致计时器不准。 可以将_timer_ticks定义为64位的,这样即使1ms累加一次,也要上亿年,设备也运行不到那个时候。

Eureka1024 avatar Aug 09 '20 02:08 Eureka1024

这个方案是否可行:timeout参数采用倒计时。 (1)在timer_init函数中timeout参数采用倒计时的方式,不需要算上当前的时间戳; (2)在HAL_IncTick中每次减去tick,减到0时,增加一个标志位 (3)在timer_loop中检查标志位。

dongxinwanwu avatar Jan 25 '21 10:01 dongxinwanwu

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满足,时间错过

askhua520 avatar Jul 08 '21 08:07 askhua520

是不是可以换个思路,解决溢出的原因是以为溢出后会导致计时器不准。 可以将_timer_ticks定义为64位的,这样即使1ms累加一次,也要上亿年,设备也运行不到那个时候。

简单粗暴而不失优雅,哈哈, 在判断时再加个锁可以防止8位、16位、32位CPU的汇编中断问题。

cancundiudiu avatar Dec 16 '21 16:12 cancundiudiu

感谢您的来信,我会尽快回复您!                                 ——卓琳

VanchTan avatar Dec 16 '21 16:12 VanchTan

    //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) {
       
    }

这样严谨点

ChunxingJin avatar Jan 12 '22 01:01 ChunxingJin

感谢您的来信,我会尽快回复您!                                 ——卓琳

VanchTan avatar Jan 12 '22 02:01 VanchTan

推荐另外一种的解决方法: 不用设置标志,不用去考虑有符号无符号中间的转换。

定义一个获取时间长度的宏 TIME_PASSED #define TIME_MAX 0xFFFFFFFFu /* 最大时间长度取决于使用的timer计时器长度,这里以32bit为例 */ #define TIME_PASSED(now, since) ((now) >= (since)) ? ((now) -(since)) : ((now) + (1 + TIME_MAX - (since)))

image

参考资料:《嵌入式系统设计与实践》5.4.2 p138

flyiingdust avatar Jun 06 '22 16:06 flyiingdust

感谢您的来信,我会尽快回复您!                                 ——卓琳

VanchTan avatar Jun 06 '22 16:06 VanchTan

    //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)

ZengXianCong avatar Aug 19 '22 03:08 ZengXianCong

感谢您的来信,我会尽快回复您!                                 ——卓琳

VanchTan avatar Aug 19 '22 03:08 VanchTan

实际上这里可以直接使用减法,不用考虑溢出条件,无符号减法本身就是借位操作,比如说,我们定义两个32位无符号整数: uint32_t a = x; uint32_t b = y; 当x < y 时,a - b的计算结果实际上就是2^32 + (x - y)。

weiyang-github avatar Feb 28 '24 01:02 weiyang-github

感谢您的来信,我会尽快回复您!                                 ——卓琳

VanchTan avatar Feb 28 '24 01:02 VanchTan

实际上这里可以直接使用减法,不用考虑溢出条件,无符号减法本身就是借位操作,比如说,我们定义两个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。

cmpxr avatar Jul 08 '24 06:07 cmpxr

感谢您的来信,我会尽快回复您!                                 ——卓琳

VanchTan avatar Jul 08 '24 06:07 VanchTan