Как организовывать таймер в программе

В различного рода сложности реализуемых алгоритмов при программировании МК, всегда возникают рутинные циклические и не очень задачи. Одни требуют повышенной точности, другие таким критерием не обязаны обладать. Аппаратных таймеров на борту МК может быть приличное количество, например STM32F4 — аж 14 штук, и это не считая SysTick (системного), а в других и пара тройка за счастье: тот же PIC16, например.

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

Вместо предисловия

Спросим у ГУГЛА что он об этом думает?

Не задумываясь поисковик выдает примерно такой результат:

  1. Программные таймеры. Часть 1
  2. Многозадачный программный таймер.
  3. Многозадачный программный таймер, ver 2.0
  4. Программный таймер. Применение HAL

Предисловие

Как разработчик АСУ ТП, я часто программирую ПЛК различных фирм. Для любого ПЛК в среде разработки заготовлены библиотеки для программных таймеров. Почти все они имеют однотипную функциональность. Использование таймеров в программе ПЛК требуется во многих родах задачах, все их описывать смысла нет, поэтому я покажу пару примеров из АСУ ТП и эти примеры «грубо портируем» в данный модуль.

09. Таймеры, использование времени простоя программы и локального цикла обработки сообщений

Мини ТЗ

Какого вида таймеры нужны в данном модуле? Я остановил выбор на четырех видах:

  • Таймер с задержкой на включение
  • Таймер с задержкой на выключение
  • Циклический таймер
  • Одиночный таймер
Таймер с задержкой на включение

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

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

Таймер с задержкой на выключение

Таймер по сути аналогичен первому, но с логикой наоборот. Пока активный вход, активный и выход таймера. Как только на входе низкий уровень (не активный) таймер начинает обратный отсчет и по окончанию сбрасывает свой выход тоже в ноль. Вот его временная диаграмма.

Нужно это бывает, когда необходимо остановить задачу не одновременно с её «родителем», а немного позже. Данный вид таймера может показаться кому то экзотичном, при программировании МК, может и так, но как говорится пусть будет.

Как измерить время выполнения кода | Изучение С++ для начинающих. Урок #145

Циклический таймер

Ну тут все банально. Каждое переполнение данного таймера должно срабатывать событие, по которому выполняем ту или иную рутинную задачу. Опрос датчика инерционной среды, мигание светодиодом, говорящий нам что МК в «порядке», для организации равных промежутков времени для разного рода фильтров и т.д. Временная диаграмма ниже.

Одиночный таймер

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

Теперь все это реализуем в коде.

Модуль SwTimer

Название модуля говорит само за себя. Модуль состоит из двух файлов: хидера и сорца.

#ifndef SW_TIMER_H_ #define SW_TIMER_H_ #define SwTimerCount 64 //Количество программных таймеров /*Режимы работы таймеров*/ typedef enum < SWTIMER_MODE_EMPTY, SWTIMER_MODE_WAIT_ON, SWTIMER_MODE_WAIT_OFF, SWTIMER_MODE_CYCLE, SWTIMER_MODE_SINGLE >SwTimerMode; /*Стурктура программного таймера*/ typedef struct < unsigned LocalCount:32; //Переменная для отсчета таймера unsigned Count:24; //Переменная для хранения задержки unsigned Mode:3; //Режим работы unsigned On:1; //Разрешиющий бит unsigned Reset:1; //Сброс счета таймера без его выключения unsigned Off:1; //Останавливающий бит unsigned Out:1; //Выход таймера unsigned Status:1; //Состояние таймера >SW_TIMER; #if (SwTimerCount>0) volatile SW_TIMER TIM[SwTimerCount]; //Объявление софтовых таймеров #endif void SwTimerWork(volatile SW_TIMER* TIM, unsigned char Count); //Сама функция для обработки таймеров void OnSwTimer(volatile SW_TIMER* TIM, SwTimerMode Mode, unsigned int SwCount); //Подготовливает выбранный из массива таймер unsigned char GetStatusSwTimer(volatile SW_TIMER* TIM); //Считывание статуса таймера #endif /* SW_TIMER_H_ */

Читайте также:
Разновидность современных праздничных программ

Использование таймеров

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

В этом разделе содержатся следующие подразделы.

  • Создание таймера
  • Уничтожение таймера
  • Использование функций таймера для перехвата входных данных с помощью мыши
  • Связанные темы

Создание таймера

В следующем примере функция SetTimer используется для создания двух таймеров. Первый таймер устанавливается каждые 10 секунд, второй — каждые пять минут.

// Set two timers. SetTimer(hwnd, // handle to main window IDT_TIMER1, // timer identifier 10000, // 10-second interval (TIMERPROC) NULL); // no timer callback SetTimer(hwnd, // handle to main window IDT_TIMER2, // timer identifier 300000, // five-minute interval (TIMERPROC) NULL); // no timer callback

Чтобы обработать сообщения WM_TIMER , созданные этими таймерами, добавьте оператор WM_TIMER case в процедуру window для параметра hwnd .

case WM_TIMER: switch (wParam) < case IDT_TIMER1: // process the 10-second timer return 0; case IDT_TIMER2: // process the five-minute timer return 0; >

Приложение также может создать таймер, WM_TIMER сообщения которого обрабатываются не процедурой main окна, а определенной приложением функцией обратного вызова, как показано в следующем примере кода, который создает таймер и использует функцию обратного вызова MyTimerProc для обработки сообщений WM_TIMER таймера.

// Set the timer. SetTimer(hwnd, // handle to main window IDT_TIMER3, // timer identifier 5000, // 5-second interval (TIMERPROC) MyTimerProc); // timer callback

Соглашение о вызовах для MyTimerProc должно основываться на функции обратного вызова TimerProc .

Если приложение создает таймер без указания дескриптора окна, приложение должно отслеживать очередь сообщений на наличие WM_TIMER сообщений и отправлять их в соответствующее окно.

HWND hwndTimer; // handle to window for timer messages MSG msg; // message structure while (GetMessage( // Post WM_TIMER messages to the hwndTimer procedure. if (msg.message == WM_TIMER) < msg.hwnd = hwndTimer; >TranslateMessage( // translates virtual-key codes DispatchMessage( // dispatches message to window >

Уничтожение таймера

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

// Destroy the timers. KillTimer(hwnd, IDT_TIMER1); KillTimer(hwnd, IDT_TIMER2); KillTimer(hwnd, IDT_TIMER3);

Использование функций таймера для перехвата входных данных с помощью мыши

Иногда при наличии указателя мыши на экране необходимо запретить дополнительный ввод.

Один из способов сделать это — создать специальную подпрограмму, которая перехватывает ввод с помощью мыши до тех пор, пока не произойдет определенное событие. Многие разработчики называют эту процедуру «построением мышеловки».

В следующем примере используются функции SetTimer и KillTimer для перехвата входных данных с помощью мыши. SetTimer создает таймер, который отправляет WM_TIMER сообщение каждые 10 секунд. Каждый раз, когда приложение получает WM_TIMER сообщение, оно записывает расположение указателя мыши. Если текущее расположение совпадает с предыдущим расположением и окно main приложения свернуто, приложение перемещает указатель мыши на значок. Когда приложение закрывается, KillTimer останавливает таймер.

HICON hIcon1; // icon handle POINT ptOld; // previous cursor location UINT uResult; // SetTimer’s return value HINSTANCE hinstance; // handle to current instance // // Perform application initialization here. // wc.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(400)); wc.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(200)); // Record the initial cursor position. GetCursorPos( // Set the timer for the mousetrap. uResult = SetTimer(hwnd, // handle to main window IDT_MOUSETRAP, // timer identifier 10000, // 10-second interval (TIMERPROC) NULL); // no timer callback if (uResult == 0) < ErrorHandler(«No timer is available.»); >LONG APIENTRY MainWndProc( HWND hwnd, // handle to main window UINT message, // type of message WPARAM wParam, // additional information LPARAM lParam) // additional information < HDC hdc; // handle to device context POINT pt; // current cursor location RECT rc; // location of minimized window switch (message) < // // Process other messages. // case WM_TIMER: // If the window is minimized, compare the current // cursor position with the one from 10 seconds // earlier. If the cursor position has not changed, // move the cursor to the icon. if (IsIconic(hwnd)) < GetCursorPos( if ((pt.x == ptOld.x) (pt.y == ptOld.y)) < GetWindowRect(hwnd, SetCursorPos(rc.left, rc.top); >else < ptOld.x = pt.x; ptOld.y = pt.y; >> return 0; case WM_DESTROY: // Destroy the timer. KillTimer(hwnd, IDT_MOUSETRAP); PostQuitMessage(0); break; // // Process other messages. // >

Читайте также:
Программа которая скачивает с Youtube на Android

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

UINT uResult; // SetTimer’s return value HICON hIcon1; // icon handle POINT ptOld; // previous cursor location HINSTANCE hinstance; // handle to current instance // // Perform application initialization here. // wc.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(400)); wc.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(200)); // Record the current cursor position. GetCursorPos( // Set the timer for the mousetrap. uResult = SetTimer(hwnd, // handle to main window IDT_MOUSETRAP, // timer identifier 10000, // 10-second interval (TIMERPROC) MyTimerProc); // timer callback if (uResult == 0) < ErrorHandler(«No timer is available.»); >LONG APIENTRY MainWndProc( HWND hwnd, // handle to main window UINT message, // type of message WPARAM wParam, // additional information LPARAM lParam) // additional information < HDC hdc; // handle to device context switch (message) < // // Process other messages. // case WM_DESTROY: // Destroy the timer. KillTimer(hwnd, IDT_MOUSETRAP); PostQuitMessage(0); break; // // Process other messages. // >// MyTimerProc is an application-defined callback function that // processes WM_TIMER messages. VOID CALLBACK MyTimerProc( HWND hwnd, // handle to window for timer messages UINT message, // WM_TIMER message UINT idTimer, // timer identifier DWORD dwTime) // current system time < RECT rc; POINT pt; // If the window is minimized, compare the current // cursor position with the one from 10 seconds earlier. // If the cursor position has not changed, move the // cursor to the icon. if (IsIconic(hwnd)) < GetCursorPos( if ((pt.x == ptOld.x) (pt.y == ptOld.y)) < GetWindowRect(hwnd, SetCursorPos(rc.left, rc.top); >else < ptOld.x = pt.x; ptOld.y = pt.y; >> >

Источник: learn.microsoft.com

Руководство по таймерам в микроконтроллерах PIC для начинающих

Ранее на нашем сайте мы рассмотрели основы работы с микроконтроллерами PIC с помощью MPLABX IDE, отладку программы мигания светодиодом для них в симуляторе и на «реальном» железе, а также мигание с их помощью последовательностью светодиодов. В этой же статье на основе предыдущего проекта мигания последовательностью светодиодов мы рассмотрим основы работы с таймерами в микроконтроллерах PIC. По сравнению с предыдущим проектом мигания последовательностью светодиодов в данном проекте мы добавим одну дополнительную кнопку.

Проект для объяснения работы таймеров в микроконтроллерах PIC

Ранее на нашем сайте мы достаточно подробно рассматривали работу с таймерами в платах Arduino.

Читайте также:
Партия новые люди кто лидер программа

Зачем нужны таймеры

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

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

Также у задержек, организуемых с помощью функции __delay_ms()), есть и другие недостатки:

  1. Значение задержки задается константой и его нельзя изменить во время исполнения программы.
  2. Значение задержки не такое точное как в случае использования для этой цели таймеров.
  3. Невозможно организовать очень длительные задержки, к примеру, полчаса и более. Максимальная величина задержки зависит от используемой частоты кварцевого генератора.

Таймеры в микроконтроллерах PIC

На физическом уровне таймер представляет собой регистр, значение в котором непрерывно увеличивается до 255, и затем счет в нем начинается сначала: 0, 1, 2, 3, 4. 255. 0, 1, 2, 3. и т. д.

Микроконтроллер PIC16F877A содержит три таймера, с именами Timer0, Timer1 и Timer2. Timer0 и Timer2 являются 8-битными, а Timer1 – 16-битным. В нашем проекте мы рассмотрим работу с Timer0. Если вы поймете принцип его работы, то по аналогии с ним вы сможете работать и с таймерами Timer1 и Timer2

Таймер/счетчик Timer0 имеет следующие особенности:

  • 8-битный;
  • можно записывать и считывать информацию;
  • 8-битный программируемый предварительный делитель частоты (предделитель, prescaler);
  • выбор внешней или внутренней частоты синхронизации;
  • прерывание при переполнении от FFh до 00h;
  • выбор спада или нарастания импульса для сигнала внешней синхронизации.

Для начала разберемся с рядом терминов, оказывающих влияние на функционирование таймера.

Предварительный делитель частоты (предделитель, prescaler) – это компонент микроконтроллера, который делит частоту задающего генератора перед тем как она достигает логического уровня, увеличивающего состояние таймера. Коэффициент деления предделителя для микроконтроллеров PIC лежит в диапазоне от 1 до 256, он устанавливается с помощью регистра OPTION (этот же регистр используется и для управления подтягивающими резисторами). К примеру, если значение (его коэффициент деления) предделителя равно 64, то с каждым 64-м импульсом частоты генератора значение таймера будет увеличиваться на 1.

Когда при инкрементировании таймера его значение достигает 255 (максимальное его значение) он инициализирует прерывание и сбрасывает свое значение снова до 0. Этот прерывание называется прерыванием от таймера. Оно информирует микроконтроллер что истек определенный промежуток времени.

Обозначим за Fosc частоту тактового генератора (Frequency of the Oscillator) , то есть частоту используемого кристалла. Интервал времени, отсчитываемый таймером, зависит от значения предделителя (Prescaler) и значения частоты Fosc.

Объяснение программы для микроконтроллера PIC

Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.

В нашем проекте мы будем использовать 2 кнопки и 8 светодиодов, которые подключены к 8 контактам микроконтроллера PIC. С помощью 1-й кнопки мы будем устанавливать временную задержку (500ms на каждое нажатие), а 2-й кнопкой будем запускать процесс мигания последовательности светодиодов. К примеру, если первую кнопку мы нажмем трижды (500*3 = 1500ms), то задержка составит 1,5 секунды и при нажатии второй кнопки светодиоды будут включаться и выключаться с этим значением задержки. Более подробно работу проекта вы можете посмотреть на видео, приведенном в конце статьи.

Тестирование работы проекта

Первым делом в программе мы зададим значение битов конфигурации, но этот фрагмент программы мы здесь рассматривать не будем поскольку мы его подробно рассмотрели в соответствующей статье. Поэтому перейдем сразу к функции main, в которой мы будем задавать параметры работы таймера Timer0.

Источник: microkontroller.ru

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru