滚动安全计时器(刻度)比较
时间:2020-03-05 18:53:01 来源:igfitidea点击:
我在硬件方面有一个计数器,可以观察到时序方面的考虑。它以毫秒为单位,并以16位无符号值存储。如何安全检查计时器值是否经过了一定时间并安全地处理不可避免的翻转:
//this is a bit contrived, but it illustrates what I'm trying to do const uint16_t print_interval = 5000; // milliseconds static uint16_t last_print_time; if(ms_timer() - last_print_time > print_interval) { printf("Fault!\n"); last_print_time = ms_timer(); }
当ms_timer溢出到0时,此代码将失败。
解决方案
回答
只需检查ms_timer <last_print_time,是否添加2 ^ 16否?
编辑:如果可以的话,我们还需要为此最多安装一个uint32.
回答
避免该问题的最安全方法可能是使用带符号的32位值。使用示例:
const int32 print_interval = 5000; static int32 last_print_time; // I'm assuming this gets initialized elsewhere int32 delta = ((int32)ms_timer()) - last_print_time; //allow a negative interval while(delta < 0) delta += 65536; // move the difference back into range if(delta < print_interval) { printf("Fault!\n"); last_print_time = ms_timer(); }
回答
这似乎适用于最大64k / 2的间隔,这适合我:
const uint16_t print_interval = 5000; // milliseconds static uint16_t last_print_time; int next_print_time = (last_print_time + print_interval); if((int16_t) (x - next_print_time) >= 0) { printf("Fault!\n"); last_print_time = x; }
利用带符号整数的性质。 (补码)
回答
我们实际上不需要在这里做任何事情。假设" ms_timer()"返回类型为uint16_t的值,则问题中列出的原始代码将可以正常工作。
(还假设计时器在两次检查之间不会两次溢出...)
要说服自己,请尝试以下测试:
uint16_t t1 = 0xFFF0; uint16_t t2 = 0x0010; uint16_t dt = t2 - t1;
dt等于0x20。
回答
我发现使用其他计时器API更适合我。我创建了一个具有两个API调用的计时器模块:
void timer_milliseconds_reset(unsigned index); bool timer_milliseconds_elapsed(unsigned index, unsigned long value);
计时器索引也在计时器头文件中定义:
#define TIMER_PRINT 0 #define TIMER_LED 1 #define MAX_MILLISECOND_TIMERS 2
我将unsigned long int用于我的计时器计数器(32位),因为那是我的硬件平台上的本机大小的整数,这使我经过的时间从1毫秒变为大约49.7天。我们可能具有16位的计时器,这将使我们经过的时间从1毫秒到大约65秒。
计时器计数器是一个数组,由硬件计时器递增(中断,任务或者计数器值的轮询)。在处理无翻转计时器增量的函数中,可以将它们限制为数据类型的最大值。
/* variable counts interrupts */ static volatile unsigned long Millisecond_Counter[MAX_MILLISECOND_TIMERS]; bool timer_milliseconds_elapsed( unsigned index, unsigned long value) { if (index < MAX_MILLISECOND_TIMERS) { return (Millisecond_Counter[index] >= value); } return false; } void timer_milliseconds_reset( unsigned index) { if (index < MAX_MILLISECOND_TIMERS) { Millisecond_Counter[index] = 0; } }
然后代码变为:
//this is a bit contrived, but it illustrates what I'm trying to do const uint16_t print_interval = 5000; // milliseconds if (timer_milliseconds_elapsed(TIMER_PRINT, print_interval)) { printf("Fault!\n"); timer_milliseconds_reset(TIMER_PRINT); }
回答
我使用此代码通过签名比较来说明错误和可能的解决方案。
/* ========================================================================== */ /* timers.c */ /* */ /* Description: Demonstrate unsigned vs signed timers */ /* ========================================================================== */ #include <stdio.h> #include <limits.h> int timer; int HW_DIGCTL_MICROSECONDS_RD() { printf ("timer %x\n", timer); return timer++; } // delay up to UINT_MAX // this fails when start near UINT_MAX void delay_us (unsigned int us) { unsigned int start = HW_DIGCTL_MICROSECONDS_RD(); while (start + us > HW_DIGCTL_MICROSECONDS_RD()) ; } // works correctly for delay from 0 to INT_MAX void sdelay_us (int us) { int start = HW_DIGCTL_MICROSECONDS_RD(); while (HW_DIGCTL_MICROSECONDS_RD() - start < us) ; } int main() { printf ("UINT_MAX = %x\n", UINT_MAX); printf ("INT_MAX = %x\n\n", INT_MAX); printf ("unsigned, no wrap\n\n"); timer = 0; delay_us (10); printf ("\nunsigned, wrap\n\n"); timer = UINT_MAX - 8; delay_us (10); printf ("\nsigned, no wrap\n\n"); timer = 0; sdelay_us (10); printf ("\nsigned, wrap\n\n"); timer = INT_MAX - 8; sdelay_us (10); }
输出示例:
bob@hedgehog:~/work2/test$ ./timers|more UINT_MAX = ffffffff INT_MAX = 7fffffff unsigned, no wrap timer 0 timer 1 timer 2 timer 3 timer 4 timer 5 timer 6 timer 7 timer 8 timer 9 timer a unsigned, wrap timer fffffff7 timer fffffff8 signed, no wrap timer 0 timer 1 timer 2 timer 3 timer 4 timer 5 timer 6 timer 7 timer 8 timer 9 timer a signed, wrap timer 7ffffff7 timer 7ffffff8 timer 7ffffff9 timer 7ffffffa timer 7ffffffb timer 7ffffffc timer 7ffffffd timer 7ffffffe timer 7fffffff timer 80000000 timer 80000001 bob@hedgehog:~/work2/test$