翻滚安全计时器(勾选)比较

时间:2008-09-14 16:23:23

标签: c++ c timer rollover embedded

我有一个硬件计数器,我可以观察时间考虑因素。它计算毫秒数并存储在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时,此代码将失败。

8 个答案:

答案 0 :(得分:9)

你实际上并不需要在这里做任何事情。假设ms_timer()返回uint16_t类型的值,问题中列出的原始代码将正常工作。

(还假设计时器在检查之间不会溢出两次......)

为了说服自己就是这种情况,请尝试以下测试:

uint16_t t1 = 0xFFF0;
uint16_t t2 = 0x0010;
uint16_t dt = t2 - t1;

dt将等于0x20

答案 1 :(得分:3)

我使用此代码来说明使用签名比较的错误和可能的解决方案。

/* ========================================================================== */
/*   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$ 

答案 2 :(得分:3)

我曾经为这种情况编写类似下面的代码 我测试了测试用例并确保它100%工作 此外,在以下代码中使用32位计时器时间点从uint32_tuint16_t更改为0xFFFFFFFF

0xFFFF

答案 3 :(得分:1)

检查ms_timer&lt; last_print_time如果是这样加2 ^ 16没有?

编辑:如果可以,您还需要最多使用uint32。

答案 4 :(得分:1)

避免此问题的最安全方法可能是使用带符号的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();
}

答案 5 :(得分:1)

这似乎适用于高达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;
}

使用有符号整数的性质。 (twos complement

答案 6 :(得分:0)

我发现使用不同的计时器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);
}

答案 7 :(得分:-1)

有时我会这样做:

#define LIMIT 10   // Any value less then ULONG_MAX
ulong t1 = tick of last event;
ulong t2 = current tick;

// This code needs to execute every tick
if ( t1 > t2 ){
    if ((ULONG_MAX-t1+t2+1)>=LIMIT){
       do something
    }
} else {
if ( t2 - t1 >= LIMT ){
    do something
}