- Зачем нужен таймер, когда у нас есть Delay ()?
- Таймеры микроконтроллера PIC:
- Программирование и объяснение работы:
- Принципиальная схема и моделирование Proteus:
Это будет пятый учебник в нашей серии учебных пособий по PIC, который поможет вам изучить и использовать таймеры в PIC16F877A. В наших предыдущих руководствах мы начали с Введение в PIC и MPLABX IDE, затем мы написали нашу первую программу PIC для мигания светодиода с помощью PIC, а затем создали последовательность мигания светодиода, используя функцию задержки в микроконтроллере PIC. Теперь давайте воспользуемся той же последовательностью мигания светодиода, которую мы использовали в предыдущем учебном пособии, и с ее помощью мы узнаем, как использовать таймеры в нашем PIC MCU. Мы только что добавили еще одну кнопку на светодиодную панель для этого урока. Прочтите руководство, чтобы узнать больше.
Таймеры - одна из важных рабочих лошадок для встроенного программиста. Каждое приложение, которое мы разрабатываем, каким-то образом будет включать в себя приложение для измерения времени, например, включение или выключение чего-либо по истечении определенного интервала времени. Хорошо, но зачем нам таймеры, если у нас уже есть макросы задержки (__delay_ms ()), делающие то же самое !!
Зачем нужен таймер, когда у нас есть Delay ()?
Макрос задержки называется задержкой «дампа». Потому что во время выполнения функции задержки MCU сидит в дампе, просто создавая задержку. Во время этого процесса MCU не может прослушивать свои значения АЦП или читать что-либо из своих регистров. Следовательно, не рекомендуется использовать функции задержки, за исключением таких приложений, как мигание светодиода, где время задержки не обязательно должно быть точным или длинным.
Макросы задержки также имеют следующие недостатки:
- Значение задержки должно быть постоянным для макросов задержки; его нельзя изменить во время выполнения программы. Следовательно, это остается определенным программистом.
- Задержка не будет точной по сравнению с использованием таймеров.
- Большие значения задержек не могут быть созданы с помощью макросов, например, задержка в полчаса не может быть создана макросами задержки. Максимальная задержка, которую можно использовать, зависит от используемого кварцевого генератора.
Таймеры микроконтроллера PIC:
Физически таймер - это регистр, значение которого постоянно увеличивается до 255, а затем он запускается заново: 0, 1, 2, 3, 4… 255…. 0, 1, 2, 3……и т.д.
PIC16F877A PIC MCU имеет три модуля таймера. Они называются Timer0, Timer1 и Timer2. Таймер 0 и Таймер 2 - это 8-битные таймеры, а Таймер 1 - это 16-битный таймер. В этом руководстве мы будем использовать таймер 0 для нашего приложения. Как только мы поймем таймер 0, будет легко работать и с таймером 1, и таймером 2.
Таймер / счетчик модуля Timer0 имеет следующие особенности:
- 8-битный таймер / счетчик
- Читаемый и записываемый
- 8-битный программно-программируемый предделитель
- Выбор внутренних или внешних часов
- Прерывание при переполнении с FFh на 00h
- Выбор фронта для внешних часов
Чтобы начать использовать таймер, мы должны понимать некоторые причудливые термины, такие как 8-битный / 16-битный таймер, предделитель, прерывания таймера и фокусы. Теперь давайте посмотрим, что на самом деле означает каждый из них. Как было сказано ранее, в нашем микроконтроллере PIC есть как 8-битные, так и 16-битные таймеры, основное различие между ними состоит в том, что 16-битный таймер имеет гораздо лучшее разрешение, чем 8-битный таймер.
Предделитель - это название части микроконтроллера, которая делит часы генератора, прежде чем они достигнут логики, увеличивающей состояние таймера. Диапазон идентификатора предварительного делителя - от 1 до 256, а значение предварительного делителя может быть установлено с помощью регистра OPTION (того же, что мы использовали для подтягивающих резисторов). Например, если значение предварительного делителя равно 64, то для каждого 64- го импульса таймер будет увеличиваться на 1.
По мере увеличения таймера и достижения максимального значения 255 он инициирует прерывание и снова инициализирует себя до 0. Это прерывание называется прерыванием по таймеру. Это прерывание сообщает MCU, что это конкретное время истекло.
Fosc обозначает частоту генератора, то частота кварцевого резонатора. Время, затрачиваемое на регистр таймера, зависит от значения предделителя и значения Fosc.
Программирование и объяснение работы:
В этом уроке мы установим две кнопки как два входа и 8 светодиодов как 8 выходов. Первая кнопка будет использоваться для установки временной задержки (500 мс на каждое нажатие), а вторая кнопка будет использоваться для запуска мигания последовательности таймера. Например, если первая кнопка нажата трижды (500 * 3 = 1500 мс), задержка будет установлена на 1,5 секунды, а при нажатии второй кнопки каждый светодиод будет включаться и выключаться с предварительно заданной временной задержкой. Посмотрите демонстрационное видео в конце этого учебного пособия.
Теперь, имея в виду эти основы, давайте посмотрим на нашу программу, приведенную в конце раздела «Код».
Ничего страшного, если вы не получили программу, но если получили !! Дайте себе файл cookie и сбросьте программу, чтобы насладиться результатом. Для других я разобью программу на значимые части и объясню вам, что происходит в каждом блоке.
Как всегда, первые несколько строк кода - это настройки конфигурации и файлы заголовков, я не буду объяснять это, поскольку я уже делал это в своих предыдущих руководствах.
Затем давайте пропустим все строки и сразу перейдем к функции void main, внутри которой у нас есть конфигурация PORT для Timer0.
void main () {/ ***** Конфигурация порта для таймера ****** / OPTION_REG = 0b00000101; // Timer0 с внешней частотой и 64 в качестве предскалярного // Также включает PULL UP TMR0 = 100; // Загружаем значение времени для 0.0019968s; delayValue может находиться в диапазоне 0-256 только TMR0IE = 1; // Разрешить бит прерывания таймера в регистре PIE1 GIE = 1; // Разрешить глобальное прерывание PEIE = 1; // Включение периферийного прерывания / *********** ______ *********** /
Чтобы понять это, мы должны взглянуть на регистр OPTION в нашей таблице данных PIC.
Как обсуждалось в предыдущем руководстве, бит 7 используется для включения слабого подтягивающего резистора для PORTB. Посмотрите на приведенный выше рисунок, бит 3 установлен в 0, чтобы указать MCU, что следующий устанавливаемый прескалер должен использоваться для таймера, а не для WatchDogTimer (WDT). Режим таймера выбирается сбросом бита 5 T0CS.
(OPTION_REG <5>)
Теперь биты 2-0 используются для установки значения предварительного делителя для таймера. Как показано в таблице выше, чтобы установить значение предделителя 64, бит должен быть установлен как 101.
Затем давайте посмотрим на регистры, связанные с Timer0.
Таймер начнет увеличиваться после установки и переполняться после достижения значения 256, чтобы разрешить прерывание от таймера в этот момент, регистр TMR0IE должен быть установлен в высокий уровень. Поскольку Таймер 0 сам по себе является периферийным устройством, мы должны включить Периферийное прерывание, установив PEIE = 1. Наконец, мы должны включить глобальное прерывание, чтобы MCU был уведомлен о прерывании во время любой операции, это делается путем установки GIE = 1.
Задержка = ((256-REG_val) * (Prescal * 4)) / Fosc
Приведенная выше формула используется для расчета значения задержки.
где
REG_val = 100;
Prescal = 64
Fosc = 20000000
Это по расчету дает, Задержка = 0,0019968 с
Следующий набор строк предназначен для установки портов ввода-вывода.
/ ***** Конфигурация порта для ввода / вывода ****** / TRISB0 = 1; // Сообщаем MCU, что вывод PORTB 0 используется в качестве входа для кнопки 1. TRISB1 = 1; // Сообщаем MCU, что вывод PORTB 1 используется в качестве входа для кнопки 1. TRISD = 0x00; // Сообщаем MCU, что все выводы на ПОРТУ D являются выходными PORTD = 0x00; // Инициализируем все выводы на 0 / *********** ______ *********** /
Это то же самое, что и в нашем предыдущем руководстве, поскольку мы используем то же оборудование. За исключением того, что мы добавили еще одну кнопку в качестве ввода. Это делается строкой TRISB1 = 1.
Далее, внутри из бесконечного во время цикла мы имеем два блока кода. Один используется для получения ввода таймера от пользователя, а другой для выполнения последовательности задержки с помощью светодиодов. Я объяснил их, используя комментарии к каждой строке.
в то время как (1) {count = 0; // Не запускать таймер в основном цикле // ******* Получить номер задержки от пользователя **** ////// if (RB0 == 0 && flag == 0) // Когда ввод задан {get_scnds + = 1; // get_scnds = get_scnds + http: // Увеличение переменной flag = 1; } if (RB0 == 1) // Для предотвращения непрерывного увеличения flag = 0; / *********** ______ *********** /
Переменный называется get_scnds увеличиваются каждый раз, когда пользователь нажимает на кнопку 1. А флаг (программное обеспечение определенно) переменный используются для хранения процесса увеличивающегося до тех пор, пока пользователь не снимает палец с кнопки.
// ******* Выполнить последовательность с задержкой **** ////// while (RB1 == 0) {PORTD = 0b00000001 <
Следующий блок вступает в действие, если нажата кнопка два. Поскольку пользователь уже определил требуемую задержку с помощью первой кнопки, она сохранена в переменной get_scnds. Мы используем переменную hscnd, эта переменная управляется ISR (подпрограммой обслуживания прерывания).
Процедура обслуживания прерывания - это прерывание, которое будет вызываться каждый раз при переполнении Timer0. Давайте посмотрим, как это контролируется ISR в следующем блоке, например, мы хотим увеличить задержку на полсекунды (0,5 с) при каждом нажатии кнопки, а затем нам нужно увеличивать переменную hscnd на каждые полсекунды. Поскольку мы запрограммировали наш таймер на переполнение каждые 0,0019968 с (~ 2 мс), поэтому для подсчета полусекундной переменной count должно быть 250, потому что 250 * 2 мс = 0,5 секунды. Итак, когда count достигает 250 (250 * 2 мс = 0,5 секунды), это означает, что прошло полсекунды, поэтому мы увеличиваем hscnd на 1 и инициализируем count равным нулю.
void interrupt timer_isr () {if (TMR0IF == 1) // Флаг таймера сработал из-за переполнения таймера {TMR0 = 100; // Загрузить значение таймера TMR0IF = 0; // Сброс флага прерывания таймера count ++; } если (количество == 250) {hscnd + = 1; // hscnd будет увеличиваться каждые полсекунды count = 0; }}
Поэтому мы используем это значение, сравниваем его с нашим hscnd и сдвигаем наш светодиод в зависимости от времени, заданного пользователем. Он также очень похож на предыдущий урок.
Вот и все, наша программа понята и работает.
Принципиальная схема и моделирование Proteus:
Как обычно, давайте сначала проверим вывод с помощью Proteus, я связал здесь файлы схемы Proteus.
Добавьте кнопку на нашу предыдущую светодиодную плату, и наше оборудование готово к работе. Это должно выглядеть примерно так:
После подключения загрузите код и проверьте вывод. Если у вас возникли проблемы, воспользуйтесь разделом комментариев. Также посмотрите видео ниже, чтобы понять весь процесс.