Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

超时溢出有问题 #1

Open
longdelu opened this issue Sep 14, 2018 · 20 comments
Open

超时溢出有问题 #1

longdelu opened this issue Sep 14, 2018 · 20 comments

Comments

@longdelu
Copy link

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

@angwangiot
Copy link

angwangiot commented Nov 2, 2018

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

@longdelu
Copy link
Author

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

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

@jiejieTop
Copy link

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

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

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

@IYQ-Github
Copy link

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

@VanchTan
Copy link

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

@Eureka1024
Copy link

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

@dongxinwanwu
Copy link

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

@askhua520
Copy link

askhua520 commented Jul 8, 2021

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

@cancundiudiu
Copy link

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

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

@VanchTan
Copy link

VanchTan commented Dec 16, 2021 via email

@ChunxingJin
Copy link

ChunxingJin commented Jan 12, 2022

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

这样严谨点

@VanchTan
Copy link

VanchTan commented Jan 12, 2022 via email

@flyiingdust
Copy link

flyiingdust commented Jun 6, 2022

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

定义一个获取时间长度的宏 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

@VanchTan
Copy link

VanchTan commented Jun 6, 2022 via email

@ZengXianCong
Copy link

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

@VanchTan
Copy link

VanchTan commented Aug 19, 2022 via email

@weiyang-github
Copy link

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

@VanchTan
Copy link

VanchTan commented Feb 28, 2024 via email

@cmpxr
Copy link

cmpxr commented Jul 8, 2024

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

@VanchTan
Copy link

VanchTan commented Jul 8, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests