Программирование в присутствии сторожевого таймера - PullRequest
6 голосов
/ 29 июня 2010

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

Вот проблема: мне нужно программировать небольшую систему на микроконтроллере NXP LPC2103(На базе ARM 7), без операционной системы.Он имеет сторожевой таймер, который необходимо регулярно обновлять.В систему встроен GPRS-модем со встроенным стеком TCP / IP, и для его инициализации требуется больше времени, чем требуется тайм-ауту.Когда я вызываю функцию инициализации, система перезагружается.

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

Я не хотел бы вызывать сторожевой таймер из функции инициализации, я не нахожуэто хорошо.

Ответы [ 5 ]

6 голосов
/ 29 июня 2010

Я бы не хотел вызывать сторожевой таймер из функции инициализации, я не нахожу это хорошим.

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

int (callback_t*)(void* progress, void* context);

Длительная функция будет периодически вызывать обратный вызов, с некоторой информацией, которая указывает на его прогресс (то, как этот прогресс отображается в зависимости от деталей конкретной функции) и со значением контекста, которое передал исходный вызывающий объект. вместе с указателем обратного вызова (опять же - что означает этот параметр и как он интерпретируется, полностью зависит от обратного вызова). Как правило, возвращаемое значение функции обратного вызова может использоваться для указания того, что «долго работающая вещь» должна отменять или иным образом изменять поведение.

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

int watchdog_callback( void* progress, void* context)
{
    kick_the_watchdog();

    return 0;  // zero means 'keep going...'
}


void init_modem( callback_t pCallback, void* callback_context)
{
    // do some stuff

    pCallback( 0, callback_context);

    // do some other stuff

    pCallback( 1, callback_context);


    while (waiting_for_modem()) {
         // do work...

         pCallback( 2, callback_context);
    }    
}

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

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

5 голосов
/ 29 июня 2010

Как правило, есть два подхода, которые я принял для этой ситуации.

Инициализация конечного автомата

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

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

Ограниченная по времени обработка сторожевого таймера ISR

Существует другая альтернатива, если у вас есть «синдикат» или эквивалентное прерывание, например, прерывание, которое запускается каждые 1 мс,В этой ситуации вы можете рассмотреть подачу сторожевого таймера (например, каждые 50 вызовов прерывания), но ограничив число раз, когда сторожевой таймер подается к нему, чтобы равняться максимально допустимому времени для выполнения процедур инициализации.Тогда, как правило, необходимо (если, на мой взгляд, у вас есть оконный сторожевой таймер) иметь короткий цикл синхронизации в конце инициализации, чтобы убедиться, что сторожевой таймер не подается до достижения минимального времени окна,но это тривиально для реализации.

Это довольно чистое решение (так как оно не превращает подпрограммы инициализации в ненужный конечный автомат) и имеет дело с проблемами зависания подпрограммы инициализации.Однако очень важно, чтобы в ISR было установлено ограничение на сторожевые вызовы.

Обсуждение

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

Я уверен, что другие будут предлагать и другие альтернативные идеи ...

3 голосов
/ 29 июня 2010

Watchdog в LPC2103 обладает широкими возможностями настройки.У вас есть много вариантов управления:

Вы можете не включать его, пока не закончится последовательность инициализации.

Вы можете продлить период между каналами до очень продолжительного времени.

Вопрос в том, для чего вы используете сторожевой таймер?

Если он использовался для проверки работоспособности вашего программного обеспечения и не зависания, я не вижу, как вариант ISR от AI поможет вам (ISR можно продолжитьработать, даже если ваша программа застряла).

Подробнее о параметрах сторожевого таймера см. главу (17) «Сторожевой таймер» (WDT) в Руководстве пользователя для MCU.http://www.nxp.com/documents/user_manual/UM10161.pdf

2 голосов
/ 29 июня 2010

Watchdogs - это замечательно, но и боль в тылу, когда ваша программа или система не легко вписываются в нее.Лучше всего работать, когда у вас есть код, который выглядит (как правило) как:

Watchdog_init();

hardware_init();
subsystem1_init();
subsystem2_init();
subsystem3_init();
...
subsystemN_init();

forever {
   Watchdog_tickle();

   subsystem1_work();
   subsystem2_work();
   subsystem3_work();
   ...
   subsystemN_work();
}

Очень часто вы можете разработать свою программу таким образом, чтобы это работало, и, как правило, это очень надежно (но не полностью).

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

Один хорошийЧтобы изменить приведенный выше код для обработки более сложных ситуаций, можно изменить функции subsystemX_work, чтобы не отставать от состояния.Это можно сделать с помощью статических переменных в функциях или с помощью указателей функций, а не функций, и изменить фактическую функцию, которая выполняется, чтобы отразить текущее состояние этой подсистемы.Каждая подсистема становится конечным автоматом.

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

slow_device_init();
Watchdog_tickle();

Вы можете сделать:

slow_device_init_begin();
Watchdog_tickle();
slow_device_init_finish();
Watchdog_tickle();

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

slow_device_init_begin();
for ( i = SLOW_DEV_TRIES; i ; i--) {
   Watchdog_tickle();
   if (slow_device_init_done()) {
       break;
   }
}
Watchdog_tickle();

Даже если это возможностановиться все сложнее и сложнее.Часто вам в конечном итоге приходится создавать сторожевого делегата, который просто проверяет условия, которые должны быть выполнены, и делает или не ласкает сторожевого таймера, основываясь на этих условиях.Это начинает становиться очень сложным.Это может быть реализовано путем создания объекта для каждой из ваших подсистем, который имеет некоторый метод / функцию для вызова, чтобы проверить состояние подсистемы.Методы работоспособности могут быть очень сложными и даже могут меняться по мере изменения состояния этой подсистемы, хотя это должно быть как можно более простым, чтобы как можно проще было проверить правильность кода, а также потому, что меняется то, какРабота подсистемы потребует изменений в том, как вы измеряете работоспособность.

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

Watchdog_periodic() {
   for_each subsustem in subsystem_list { // not C, but you get the idea
      if ( 0 > --(subsystem->count_down) ) {
           // Do something that causes a reset. This could be returning and not petting
           // the hardware watchdog, doing a while(1);, or something else
      }
   }
   Watchdog_tickle();
}

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

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

Следует также отметить, что чем сложнее сторожевая платформа, тем больше возможностейесть ошибки в нем, а также возможность ошибок в другом коде, чтобы заставить его работать неправильно.Например, ошибка указателя, такая как:

int x;
fscanf(input, "%i", x); // Passed uninitialized x rather than address of x

, может привести к установке значения count_down в подсистеме, что может привести к тому, что сторожевой таймер не будет кусаться, когда это необходимо.

1 голос
/ 29 июня 2010

Вы можете пересмотреть, где в коде обслуживается таймер WD.

Обычно таймер WD должен работать во время простоя (цикл бездействия или задача простоя) и в драйверах самого низкого уровня (например, когда вычтение / запись с / на модем GPRS или MAC для вашего соединения TCP / IP и т. д.).

Если этого недостаточно, ваша микропрограмма может также ничего не делать, кроме как сгорает циклы ЦП с задержкойрутина.Здесь можно добавить службу таймера WD, но вам, возможно, придется настроить таймер задержки для учета времени обслуживания WD.

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

...