Как работать со счетчиком переноса во встроенном C

Мне нужно иметь дело со счетчиком, который дает мне тики для моего приложения. Счетчик 32-битный, поэтому мне нужно знать, как с ним обращаться, когда он переносится. Например:

У меня есть функция, которая возвращает (timestamp + shifttime), и у меня есть другая функция, которая будет возвращать 1 или 0 в зависимости от того, истекло ли время, но может быть вероятность того, что мой счетчик завершится, как мне справиться с этим? .

Спасибо

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

Я использую STM32 Cortex-M3. Я хочу использовать счетчик RTC, чтобы использовать его в качестве отметки для моего приложения для планирования задач, которые должны выполняться через определенные промежутки времени. RTC может генерировать прерывание переполнения, так что это не проблема обнаружить прерывание. основная проблема, которая у меня есть (или, по крайней мере, я думаю, что это проблема), заключается в том, что определенные задачи получают (метка времени + сдвиг), т.е.


int main( void )
{
FlashLedTimeStamp = ReturnCounter( 20 );  // currentcounter value + a shift of 20
StatusLedTimeStamp = ReturnCounter( 3 );  // currentcounter value + a shift of 3

//then later on ....
while(1)
{
    /* other tasks could go here */

    if( HasTimeElapsed( FlashLedTimeStamp );
    {
       /* do something and get another timestamp value */
       FlashLedTimeStamp = ReturnCounter( 20 );  // currentcounter value + a shift of 20
    }

    if( HasTimeElapsed( StatusLedTimeStamp );
    {
       /* do something and get another timestamp value */
       FlashLedTimeStamp = StatusLedTimeStamp( 3 );  // currentcounter value + a shift of 3
    }
}   
}

давайте предположим, что мой счетчик RTC имеет длину всего 8 бит, чтобы упростить математику.

если мой текущий счетчик равен 250, когда я получаю временные метки, это означает, что FlashLedTimeStamp = 14 и StatusLedTimeStamp = 253, как я могу проверить, что срок действия FlashLedTimeStamp истек??

имейте в виду, что я не обязательно все время проверяю, что такое текущий счетчик и истек ли срок действия определенной метки времени. Я надеюсь, что это проясняет, в чем проблема, которая у меня есть, благодаря всем продвинутым.


person jramirez    schedule 22.06.2010    source источник
comment
Какова частота тактовых импульсов ваших часов и с какого значения начинается счетчик? Кроме того, не могли бы вы отредактировать более подробную информацию о платформе, на которой вы работаете (ОС, набор микросхем, производитель платы и т. д.). Могут быть какие-то специфичные для платформы решения.   -  person A. Levy    schedule 22.06.2010


Ответы (10)


Если вы берете два показания метки времени, и ваше первое значение больше, чем второе, значит, ваш счетчик свернут. Это основной способ обнаружения счетчика переноса.

Это, однако, не обнаружит, был ли счетчик свернут несколько раз, или случай, когда счетчик свернулся и оказался больше, чем первое чтение. Поскольку вы сказали, что это встроенная система, а ваше описание заставляет ваш «счетчик» звучать как часы, посмотрите, можете ли вы настроить прерывание на срабатывание всякий раз, когда часы достигают нуля (чтобы вы получали прерывание каждый раз, когда часы сбрасываются). При срабатывании этого прерывания увеличивается отдельный счетчик. Это должно эффективно добавить дополнительную точность вашим часам и позволить вашему счетчику переноситься без проблем.

person bta    schedule 22.06.2010
comment
Возможно, отредактируйте, чтобы сказать: если вы берете два показания метки времени инкрементного счетчика/таймера... -- последние 3 (встроенных) проекта, над которыми я работал, использовали таймер обратного отсчета , поэтому зацикливание происходит, когда второе значение больше первого. Например, встроенный в ARM Cortex M3 таймер SYSTICK считает до 0, а затем перезагружает определенное значение перед повторным отсчетом. Хороший ответ, хотя. - person Dan; 23.06.2010
comment
@ Дэн- Хороший вопрос. Возможно, было бы яснее сказать, что если вы вычисляете (подписанную) разницу между двумя временными метками и результат имеет знак, противоположный тому, что вы ожидаете, тогда счетчик завершается. - person bta; 23.06.2010
comment
вам нужно вычислить или определить экспериментально время, необходимое для переноса, и убедиться, что вы сэмплируете быстрее, чем это. - person old_timer; 23.06.2010

Это не будет иметь значения, пока разница между начальным и конечным счетчиком меньше, чем (2 ^ 32)/2, и при условии, что выполняется 32-битная арифметика с дополнением до 2 (почти универсально верно), даже если значение счетчика охватывает точку переноса . Например:

Start count: 0xfffffff
End Count:   0x00000002 (incremented through 0,1,2 - i.e. three counts)

End - Start == 0x00000002 - 0xfffffff == 0x00000003

Таким образом, правильный ответ достигается до тех пор, пока счетчик представляет собой разрядность встроенного целочисленного типа, и этот тип используется. Там, где, возможно, регистр счетчика не имеет ширины встроенного целочисленного типа, вы можете добиться того же эффекта, маскируя биты «переполнения» более высокого порядка.

Если вам нужен больший счетчик по другим причинам или если разница между последовательными отметками времени слишком велика, вы можете просто использовать другое целое число, которое увеличивается при переносе счетчика нижнего порядка. Это целое число будет формировать биты старшего разряда большего целого числа, поэтому младший бит второго целого числа является 33-м битом этого большего целого числа.

person Clifford    schedule 22.06.2010
comment
Это правильный ответ. Всегда делайте вычитание и сравнивайте с нулем, а не сравнивайте необработанные данные. Вычитание устраняет проблему переноса. - person c.fogelklou; 18.04.2017

Если вы используете переменные без знака для хранения времени истечения счетчика и таймера, вы можете просто использовать этот тест:

if (current_time - expiry_time < 0x80000000UL)
    /* timer has expired */

Это предполагает, что вы проверяете срок действия по крайней мере один раз каждые 0x80000000 тиков, и что ваш самый длинный таймер настроен на истечение менее 0x80000000 тиков в будущем.

person caf    schedule 23.06.2010

Приведите результат беззнакового вычитания к знаковому и сравните с нулем. Должен обрабатывать переполнение, когда вы проверяете его достаточно часто (и ваш тайм-аут составляет менее половины диапазона вашего таймера).

uint32_t timer( void);             // Returns the current time value
uint32_t timeout;

timeout = timer() + offset;

// wait until timer() reaches or exceeds timeout value
while ((int32_t)(timeout - timer()) > 0);
person tomlogic    schedule 22.06.2010

Вопрос немного расплывчатый. Одна из возможностей — установить флаг, когда вы впервые заметите, что время истекло. Надежным способом было бы добавить второй счетчик, который увеличивается при переполнении первого счетчика. Фактически это создаст 64-битный счетчик, который не будет переполняться.

person Peter G.    schedule 22.06.2010
comment
Это просто задержка исполнения. В конце концов, второй счетчик также переполнится. - person mtvec; 22.06.2010
comment
@Job: Да, но это адская задержка. Два в степени 64 позволяют получить 18 446 744 073 709 551 616 тиков. Если каждый тик равен миллисекунде, то получается чуть более 580 миллионов лет. - person torak; 22.06.2010
comment
Конечно, все, что я пытался сказать, это то, что это не настоящее решение проблемы. - person mtvec; 22.06.2010
comment
Решение, которое работает для периодов времени менее 580 миллионов лет, не является реальным решением проблемы? Почему нет? - person Mike Pelley; 22.06.2010
comment
Я знаю, что жизнь кода, как правило, длиннее, чем любой из нас думает при его написании, но я могу с уверенностью предположить, что код, который я напишу, не будет использоваться через 500 миллионов лет. - person Chimmy; 23.06.2010
comment
Это все очень верно: это решение будет работать. Все, что я хочу сказать, это то, что это не настоящее решение проблемы: вы все еще не можете обнаружить перенос. Потому что, если вы найдете такое решение, не имеет значения, будут ли ваши часы работать быстрее и тикать каждую пикосекунду (в этом случае время до того, как счетчик свернется, составляет всего полгода!). - person mtvec; 23.06.2010

Самый простой способ сделать это — создать «счетчик эпох», который явно подсчитывает ролловеры. (Пример: у вас есть аппаратный счетчик, который считает секунды от 0 до 59. Ваш счетчик эпох будет считать минуты, увеличиваясь каждый раз, когда он замечает, что счетчик секунд перевернулся.)

Затем ваша функция future_scheduler считывает текущую эпоху и время и вычисляет новую эпоху и время для вашего события.

В качестве альтернативы вы можете просто нажать на кнопку и заставить вашу функцию синхронизации отсчитывать ваши расписания событий до нуля на каждом тике таймера.

person John R. Strohm    schedule 22.06.2010

Одна из возможностей - привести обе переменные к 64-битной длине, а затем выполнить суммирование. После этого сравните с максимальным 32-битным значением, чтобы определить, упаковано ли оно.

person pmod    schedule 22.06.2010
comment
Большинство встроенных целей не поддерживают 64-битные целые числа. - person tomlogic; 23.06.2010
comment
В этом случае можно использовать простую обертку, которая эмулирует работу с 64-битной с помощью 2 x 32-битной (думаю, можно найти много готовых реализаций). - person pmod; 23.06.2010

Предполагая, что вы имеете дело с беззнаковыми типами, вы можете довольно легко проверить перенос --

if (timestamp + shifftime < timestamp) 
    it_wrapped();
person Jerry Coffin    schedule 22.06.2010
comment
Что делать, если время сдвига отрицательно? - person pmod; 22.06.2010
comment
может рассмотреть возможность форматирования этого как блока кода Markdown, а не встроенного, перенос строки (иронический, я знаю) находится в действительно неудачном месте - person Mark Elliot; 22.06.2010

Поскольку вы встроены, у вас может быть доступ к биту переполнения ЦП. Это будет установлено, когда добавление переполняет его регистр. Полезно для добавления цепочки AddCarry.

person Michael Dorgan    schedule 22.06.2010

Я думаю, что одним из самых простых способов сделать это было бы иметь еще один счетчик (давайте назовем его счетчиком Wrap, пусть это будет статический глобальный для модуля таймера), подсчитывающий каждый раз, когда ваш исходный 32-битный счетчик сворачивается.

В функции, где ваш счетчик тикает, каждый раз, когда этот счетчик достигает своего максимального значения, у вас увеличивается счетчик переноса. Поэтому, когда вы читаете функцию, которая возвращает значение, истекшее или нет на таймере, вы также читаете счетчик переноса, чтобы проверить, сколько раз он перешел. Важно также сделать это: каждый раз, когда вы читаете счетчик переноса, вы хотите очистить его для следующего чтения.

person IntelliChick    schedule 23.06.2010
comment
Вам не нужно очищать счетчик переноса, если вы просто рассматриваете его как старшие биты большего счетчика. - person Clifford; 23.06.2010