2008-09-14 10 views
6

Ich habe einen Zähler in Hardware, die ich für Timing Überlegungen beachten kann. Es zählt Millisekunden und wird in einem vorzeichenlosen 16-Bit-Wert gespeichert. Wie kann ich sicher, wenn ein Timer-Wert eine gewisse Zeit vergangen ist und sicher den unvermeidlichen Rollgriff:Rollover-Safe-Timer (Tick) Vergleiche

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

Dieser Code wird fehlschlagen, wenn ms_timer auf 0 überläuft

Antwort

1

einfach überprüfen, ob ms_timer < last_print_time und wenn also add 2^16 nein?

Edit: Sie müssen auch bis zu einem uint32 dafür, wenn Sie können.

1

Der sicherste Weg zur Vermeidung des Problems wäre die Verwendung eines 32-Bit-Werts mit Vorzeichen. So verwenden Sie Ihr Beispiel:

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

Dies scheint bis zu 64k/2 für Intervalle zu arbeiten, was für mich geeignet ist:

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

Nutzt Art von signierten Zahlen. (twos complement)

8

Sie müssen hier eigentlich nichts tun. Der ursprüngliche Code, der in Ihrer Frage aufgeführt wird, funktioniert problemlos, vorausgesetzt, ms_timer() gibt einen Wert vom Typ Uint16_t zurück.

(auch unter der Annahme, dass der Timer nicht zweimal zwischen den Überprüfungen nicht überlaufen ...)

sich zu überzeugen, dies der Fall ist, versuchen Sie den folgenden Test:

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

dt wird 0x20 gleich.

+0

Wenn ich ausführen, um den Original-Code an den über Fließpunkt arbeitet und druckt dann für jede Zählung. – JeffV

+4

Mein endgültiger Stich im Dunkeln ist, dass das ursprüngliche Verhalten und Ihre Ersatzlösung, ist, dass Ihre x Variable/ms_timer() Funktion ist/gibt einen Int größer als 16 Bit, was eine unbeabsichtigte Art der Förderung bei der Berechnung der Zeit verursacht Unterschied. – smh

0

Ich fand, dass die Verwendung einer anderen Timer-API für mich besser funktioniert. Ich habe ein Timer-Modul, das zwei API-Aufrufe hat:

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

Der Timer-Indizes wird auch in der Timer-Header-Datei definiert:

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

ich für meine Zeitzähler unsigned long int verwenden (32-bit) da dies die native Integer-Größe auf meiner Hardware-Plattform ist, und das gibt mir verstrichene Zeiten von 1 ms bis etwa 49,7 Tage. Sie könnten Timer-Zähler haben, die 16-Bit sind, die Ihnen verstrichene Zeiten von 1 ms bis ungefähr 65 Sekunden geben würden.

Die Timer-Zähler sind ein Array und werden durch den Hardware-Timer (Interrupt, Task oder Abfrage des Zählerwerts) inkrementiert. Sie können auf den Maximalwert des Datentyps in der Funktion begrenzt werden, die das Inkrement für einen No-Rollover-Timer behandelt.

/* 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; 
    } 
} 

Dann wird Ihr Code wird:

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

ich diesen Code verwenden, um die Fehler und mögliche Lösung unter Verwendung eines signierten Vergleich zu illustrieren.

/* ========================================================================== */ 
/* 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); 

} 

Beispielausgabe:

[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

Die Verzögerung neben INT_MAX hat eine falsche Länge, oder? – AndresR

-1

Manchmal mache ich es wie folgt aus:

#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

Ich verwendete Code schreiben wie folgt für die einem solchen Fall.
Ich habe mit Testfall getestet und versichern, dass es 100% funktioniert.
Zusätzlich zu uint32_t von uint16_t und 0xFFFFFFFF von 0xFFFF in unter Code mit 32-Bit-Timer-Tick ändern.

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