11

タイミングを考慮して観察できるハードウェアのカウンターがあります。ミリ秒をカウントし、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 にオーバーフローすると、このコードは失敗します。

4

8 に答える 8

11

ここでは実際に何もする必要はありません。ms_timer()タイプ uint16_t の値を返すと仮定すると、質問に記載されている元のコードは正常に機能します。

(また、タイマーがチェック間で 2 回オーバーフローしないことを前提としています...)

これが事実であると確信するには、次のテストを試してください。

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

dtとなり0x20ます。

于 2008-09-14T19:14:09.997 に答える
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$ 
于 2010-06-04T18:01:32.313 に答える
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();
}
于 2008-09-14T16:57:21.323 に答える
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;
}

符号付き整数の性質を利用します。( 2 の補数)

于 2008-09-14T17:03:39.797 に答える
1

ms_timer < last_print_time かどうかを確認し、そうであれば 2^16 no を追加しますか?

編集:可能であれば、これには最大uint32も必要です。

于 2008-09-14T16:25:59.293 に答える
0

別のタイマー API を使用する方がうまくいくことがわかりました。2 つの 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

タイマー カウンター (32 ビット) には unsigned long int を使用します。これは、ハードウェア プラットフォームのネイティブ サイズの整数であり、1 ミリ秒から約 49.7 日までの経過時間が得られるためです。1 ミリ秒から約 65 秒までの経過時間を示す 16 ビットのタイマー カウンターを使用できます。

タイマー カウンターは配列であり、ハードウェア タイマー (割り込み、タスク、またはカウンター値のポーリング) によってインクリメントされます。これらは、ロールオーバーなしタイマーの増分を処理する関数のデータ型の最大値に制限できます。

/* 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);
}
于 2008-10-02T19:42:34.173 に答える
-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
}
于 2012-08-11T04:28:07.337 に答える