Skip to main content

Конференции

Просмотр конференции fido7.pushkin.rss.easyelectronics.ru:

Предыдущее Следующее

Дата: 26 Apr 2018, 16:00:12
От: RSS robot @ 2:5020/2140.2140
Кому: All
Тема: Простой программный таймер для конечных автоматов


http://feedproxy.google.com/~r/easyelectronics/~3/2wtCnKE2iZ0/prostoj-programmnyj-tajmer-dlya-konechnyx-avtomatov.html

Достался мне тут на доделку один проект. Точнее два, но от одного автора. Управление промышленным оборудованием. Сам проект ничего особого, простая логика на конечных автоматах. Но мне понравился там
как реализован таймер. Я обычно предпочитаю динамический таймер, а идею глобального времени только высказывал, но так и не применил, т.к. в основном все делал на диспетчере или RTOS и там этот подход
не особо удобен. Но если логика построена на простом суперцикле с набором функций-автоматов в main цикле, то такая реализация таймера фактически стала классикой. Вот, пользуясь случаем, заполняю этот
пробел. Архитектура тут не важна. Главное чтоб был таймер способный давать прерывание раз в тик. Тик обычно 1мс. ▌Принцип работы и использование У нас есть глобальная переменная TimeMs которая
инкрементируется по прерыванию таймера раз в 1мс. Когда мы хотим поставить выдержку, то просто берем текущее значение TimeMs прибавляем к нему нашу выдержку и запоминаем все это в статичной
переменной, пусть будет Delay, которая определена непосредственно в той функции автомата которая эту задержку использует. И при каждом следующем входе в автомат она будет проверять нет ли у нас
условия Delay >= TimeMs. То есть автомат мигалка будет выглядеть так: void Blink (void) { // Переменные объявленные как static не исчезают после выхода из функции. А сохраняют свое состояние. static
uint8_t blink_state = 0; // Переменная состояния конечного автомата static uint32_t Delay; // Переменная программного таймера мигалки, время моргания switch (blink_state) { case 0: // Первый вход,
инициализация и первый поджиг. { Delay = MainTimerSet(1000); // Ставим задержку на 1000мс LED_ON(); // Зажигаем диодик blink_state = 1; // Переходим в следующее состояние автомата break; // Выход из
состояния } case 1: // Первая стадия рабочего цикла (Не горим) { if ( !MainTimerIsExpired(Delay) ) break; // Если 1с не прошла - сразу выходим // Функция MainTimerIsExpired проверяет по // таймерной
переменной TimeMs не // стал ли Delay меньше чем TimeMs LED_OFF(); // Если секунда прошла, то гасим диодик Delay = MainTimerSet(500); // Запоминаем время выключенной фазы 0.5с blink_state = 2; //
Переключаем автомат во вторую стадию break; // Выход из состояния } case 2: // Вторая стадия рабочего цикла (Горим) { if ( !MainTimerIsExpired(Delay) ) break; // Если 0,5с не прошла - сразу выходим
LED_ON(); // Если секунда прошла, то зажигаем диодик Delay = MainTimerSet(1000); // Запоминаем время включенной фазы 1с blink_state = 1; // Переключаем автомат в первую стадию break; // Выход из
состояния } default: break } } Ну, а в самом Main вызов автоматов разных задач выглядит как то так: void main (void) { while (1) { KeyScan(); // Автомат отвечающий за сканирование клавы бла бла бла
LCD_process(); // Автомат работающий с дисплеем бла бла бла Blink(); // Наша мигалка } Теперь немного о реализации самой библиотечки таймера. ▌Время В коде библиотеки должна быть определена
глобальная функция времени. Область видимости — модуль таймера, дальше не обязательно. Разрядность исходя из длительности задержки и длины тика. Причем максимально возможное число тиков не
должно быть больше чем половина максимального значения переменной. Т.е. для 16 разрядной переменной максимально возможное беззнаковое число это 65535, а половина, соответственно 32767, т.е. если у
вас TimeMs 16 разрядное, то максимальная выдержка будет 32767 тика или 32.767 секунды. Почему только половина? А это особенность реализации защиты от переполнения. Ниже покажу. Поэтому удобней всего
брать сразу 32 разрядное значение. static volatile uint32_t TimeMs = 0; ▌Инициализация в начале программы void MainTimerInit(void) { TimeMs = 0; // Обнуляем переменную времени. // Запускаем
аппаратный таймер TimerInit(); } ▌Системный тик В данном случае системный тик генерироваться может как угодно, например, любым таймером. В его прерывании размещается счетчик глобального времени: //
Обработчик прерывания таймера void TimerInterrupt_Handler() { TimeMs++; // Увеличиваем переменную времени. } // Тут надо не проебать атомарность. Данный кусок кода был скопипащен с STM32 и тут у меня
все операции // атомарны, т.к. сам проц 32 разрядный и инкремент делает за один такт. Если же реализация будет на // каком-либо 8 разрядном контроллере, то надо обеспечить атомарность операции. Чтобы
никто не смел // прервать выполнение. // Для AVRGCC это делается заключением инкремента в такую вот конструкцию: // ATOMIC_BLOCK(ATOMIC_RESTORESTATE) // { // TimeMs++; // } // // И не забыть
подключить библиотечку атомарных операций #include // То же касается и других операций с TimeMs которые будут далее. Помните про это!!! ▌Который час? Эта простая функция библиотеки просто показывает
текущее время системы. Иногда нужно, а глобальная переменная видима только внутри библиотеки. И негоже ее наружу вытаскивать. uint32_t MainTimerGetMs(void) { return TimeMs; } ▌Установка таймера Ну и
тут все проще некуда. Мы получаем сколько нам надо миллисекунд, прибавляем к текущему времени и отдаем результат. uint32_t MainTimerSet(const uint32_t AddTimeMs) { return TimeMs + AddTimeMs; }
Результат полагается сохранить и периодически спрашивать&#8230;. ▌А не пора ли? bool MainTimerIsExpired(const uint32_t Timer) { if ((TimeMs - Timer) < (1UL

--- rss2mail.pl
Origin: RSS robot (2:5020/2140.2140)

Предыдущее Следующее

К списку сообщений
К списку конференций