Сравнение безопасного таймера (тика) - PullRequest
8 голосов
/ 14 сентября 2008

У меня есть аппаратный счетчик, который я могу наблюдать за соображениями синхронизации. Он считает миллисекунды и сохраняется в 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 ]

9 голосов
/ 14 сентября 2008

Вам на самом деле ничего не нужно делать здесь. Исходный код, указанный в вашем вопросе, будет работать нормально, при условии, что ms_timer() возвращает значение типа uint16_t.

(также при условии, что таймер не переполняется дважды между проверками ...)

Чтобы убедиться в этом, попробуйте следующий тест:

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

dt будет равно 0x20.

3 голосов
/ 24 августа 2017

Я использовал для такого случая код, подобный следующему.
Я проверил с тестовым примером и заверяю, что он работает на 100%.
Кроме того, измените значение uint32_t с uint16_t и 0xFFFFFFFF с 0xFFFF в приведенном ниже коде с 32-битным таймером.

uint16_t get_diff_tick(uint16_t test_tick, uint16_t prev_tick)
{
    if (test_tick < prev_tick)
    {
        // time rollover(overflow)
        return (0xFFFF - prev_tick) + 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;
}
3 голосов
/ 04 июня 2010

Я использую этот код для иллюстрации ошибки и возможного решения с использованием сравнения со знаком.

/* ========================================================================== */
/*   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$ 
1 голос
/ 14 сентября 2008

Это, кажется, работает для интервалов до 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;
}

Использует природу целых чисел со знаком. ( дополняют две )

1 голос
/ 14 сентября 2008

Вероятно, самый безопасный способ избежать этой проблемы - использовать 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 голос
/ 14 сентября 2008

Просто проверьте, если ms_timer

Edit: вам также нужно до uint32 для этого, если вы можете.

0 голосов
/ 11 августа 2012

Иногда я делаю это так:

#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
}
0 голосов
/ 02 октября 2008

Я обнаружил, что использование другого 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);
}
...