2008-09-14 11 views
6

私はタイミングの考慮事項として私が観察できるハードウェアのカウンタを持っています。ミリ秒数をカウントし、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ちょうどms_timer < last_print_time場合かどうかを確認0

答えて

1

にオーバーフローしたときにこのコードは失敗します。タイマ値が一定時間を経過しかつ安全に避けられないロールオーバーを処理しているかどうかはどのように安全にご確認ください2^16を追加しないでください。

編集:可能であれば、uint32も必要です。

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(); 
} 
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

8

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

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

dt0x20に等しくなります。

は、このような場合は、自分自身を納得させるために(また...タイマーがチェックの間で二回オーバーフローしないと仮定して)、次のテストをしてみてください。

+0

私はそれがオーバーフローポイントまで動作し、その後、すべてのカウントに印刷し、元のコードを実行します。 – JeffV

+4

私の最終的なスタブ・イン・ザ・ダークは、元の動作と置き換えの解決策を考えれば、x変数/ ms_timer()関数は16ビットを超えるintを返すため、時間を計算するときに意図しない型昇格が発生する差。 – smh

0

私は、別のタイマーAPIを使用する方がより効果的だと分かりました。私は私のタイマカウンタのためのunsigned long int型を使用

#define TIMER_PRINT 0 
#define TIMER_LED 1 
#define MAX_MILLISECOND_TIMERS 2 

(32ビット:

void timer_milliseconds_reset(unsigned index); 
bool timer_milliseconds_elapsed(unsigned index, unsigned long value); 

タイマーインデックスはまた、タイマーヘッダーファイルで定義されています。私は2つのAPI呼び出しを持つタイマモジュールを作成しました)これは私のハードウェアプラットフォーム上のネイティブサイズの整数なので、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); 
} 
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); 

} 

出力例:

[email protected]:~/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 
[email protected]:~/work2/test$ 
+0

INT_MAXの次に提供される遅延の長さが正しくありません。 – AndresR

-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 
} 
1

私は、このような場合のために、次のようなコードを書くために使用。
私はテストケースでテストし、100%動作することを保証します。
さらに、uint16_t0xFFFFFFFFから0xFFFFまで32ビットのタイマーティックでuint32_tに変更してください。

uint16_t get_diff_tick(uint16_t test_tick, uint16_t prev_tick) 
{ 
    if (test_tick < prev_tick) 
    { 
     // time rollover(overflow) 
     return (0xFFFF - prev) + 1 + test_tick; 
    } 
    else 
    { 
     return test_tick - prev_tick; 
    } 
} 

/* your code will be.. */ 
uint16_t cur_tick = ms_timer(); 
if(get_diff_tick(cur_tick, last_print_time) > print_interval) 
{ 
    printf("Fault!\n"); 
    last_print_time = cur_tick; 
} 
関連する問題