Глава 13 Главное — время

Во многих системах ключевые операции тем или иным образом связаны со временем. Это может быть измерение длительности какого-либо события, подсчет числа внешних событий или же управление внешним объектом в течение определенного периода времени. В качестве примера можно указать задачу измерения интервала между импульсами, формируемыми датчиком при прохождении мимо него зубцов маховика коленчатого вала двигателя. Впоследствии это значение может быть использовано для определения скорости вращения вала (см. Рис. 3.8. на стр. 78).

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

• Узнаете, как можно увеличить надежность микроконтроллерной системы с помощью сторожевого таймера, а также научитесь использовать интегрированный модуль сторожевого таймера микроконтроллеров PIC.

• Сможете использовать модуль базового 8-битного Таймера 0 как в режиме счетчика, так и в режиме таймера.

• Познакомитесь с возможностями модуля 16-битного Таймера 1 и разберетесь, каким образом он взаимодействует с модулями захвата/сравнения/ШИМ (Capture/Compare/PWM — ССР).

• Сможете использовать модуль 8-битного Таймера 2 совместно с модулями ССР для формирования сигнала с широтно-импульсной модуляцией.

Многие системы на базе микроконтроллеров работают в сложной электромагнитной обстановке, когда помехи наводятся как по сигнальным линиям, так и по линиям питания. Типичным примером такого устройства является система управления приборной панелью автомобиля, на которую воздействуют помехи, создаваемые высоковольтными разрядами в блоке зажигания, и пульсации напряжения питания, вызываемые работой генератора. Даже если поместить блок в экран, а на всех линиях поставить фильтры, никто не сможет гарантировать, что в какой-нибудь момент времени программа не собьется с корректного положения в памяти программ и микроконтроллер не «сойдет с ума»[167], что вполне может привести к серьезным последствиям в работе системы управления. Иногда эти проблемы можно решить ручным сбросом системы[168]. Однако во многих случаях это невозможно, например в случае имплантированного кардиостимулятора или космического зонда.

Один из способов решения данной проблемы заключается в использовании связки генератор/двоичный счетчик, которая будет сбрасывать процессор при переполнении счетчика[169]. Если программа будет периодически обнулять этот счетчик во избежание переполнения, то микроконтроллер никогда не сбросится. Если по какой-либо причине микроконтроллер выйдет из основного цикла, в котором выполнялся сброс счетчика, то счетчик рано или поздно переполнится и микроконтроллер будет сброшен, а программа начнет выполняться с самого начала. Эта схема называется сторожевым таймером (watchdog timer), поскольку увеличивает безопасность системы.

Чтобы исключить использование внешних сторожевых таймеров, все микроконтроллеры PIC, даже представители самой старой линейки начального уровня, имеют встроенный модуль сторожевого таймера, структурная схема которого приведена на Рис. 13.1. Встроенный генератор сторожевого таймера никак не связан с основным тактовым генератором процессора и, если сторожевой таймер включен, постоянно генерирует сигнал с номинальным периодом 18 мс. В качестве времязадающего элемента этого генератора используется внутренняя RC-цепочка, поэтому в зависимости от конкретного экземпляра, температуры и напряжения питания период генератора может изменяться от 7 мс (—40 °C, VDD = 6 В) до 33 мс (+85 °C, VDD = 2 В) — см. Рис. 15.8 на стр. 561.

Рис. 13.1. Встроенный в микроконтроллеры PIC модуль сторожевого таймера с подключенным к нему постделителем

Генератор сторожевого таймера подключен к 8-битному постделителю (postscaler). С его помощью период тайм-аута сторожевого таймера можно увеличить до 0.018 х 128 ~= 2.3 с (0.9…4.2 с). Конкретное значение периода тайм-аута определяется состоянием битов PS[2:0] регистра OPTION_REG (см. Рис. 13.2). Генератор сторожевого таймера и счетчик постделителя (эту связку мы будем называть блоком сторожевого таймера) сбрасываются при выполнении команды clrwdt (CLeaR WatchDoG Timer — сброс сторожевого таймера). Соответственно, для предотвращения наступления тайм-аута сторожевого таймера необходимо периодически вызывать эту команду.

Рис. 13.2. Формат регистра OPTION_REG

Постделитель сторожевого таймера является разделяемым ресурсом, поскольку также используется модулем Таймера 0 (см. Рис. 13.3). Очевидно, что этот узел не может использоваться одновременно обоими модулями. Бит PSA регистра OPTION_REG определяет, к какому из модулей подключен данный узел. По умолчанию после сброса микроконтроллера постделитель подключен к сторожевому таймеру, а множитель периода сторожевого таймера равен 128.

Компания Microchip рассматривает сторожевой таймер больше как системный ресурс, нежели как периферийный модуль. Поэтому пользователь должен разрешить работу сторожевого таймера программированием бита конфигурации WDTE при записи кода в память программ (см. Рис. 10.6 на стр. 312). Например, так:

_config _HS_OSC & _WDT_ON & _PWRTE_OFF & _CP_OFF

или с помощью аналогичной директивы, поддерживаемой конкретным Си-компилятором. К примеру, в компиляторе CCS используется следующая директива:

#fuses HS, WDT, NOPUT, NOPROTECT

В приведенных выше строках работа сторожевого таймера разрешается. Для отключения сторожевого таймера необходимо использовать константы _WDT_OFF и NOWDT соответственно.

Если сторожевой таймер включен, то при сбросе по питанию выполняется инициализация модуля сторожевого таймера и деактивизируется (устанавливается в 1) 4-й бит регистра STATUS —  (см. Рис. 4.6 на стр. 95). Через заданный промежуток времени произойдет переполнение сторожевого таймера и бит ТО будет сброшен, как указано в Табл. 10.4 на стр. 322. Большинство программ для микроконтроллеров представляют собой бесконечный цикл, в теле которого вызываются различные подпрограммы. Если сделать так, чтобы при нормальном выполнении программы периодически выполнялась команда clrwdt, то независимо от возникающих событий можно будет — гарантировать, что тайм-аут сторожевого таймера не наступит. В случае же сбоя программы наступит тайм-аут, в результате чего микроконтроллер автоматически сбросится и начнет выполнять программу с адреса вектора сброса h’000’. Однако состояние флага  при этом не изменится, чем можно воспользоваться при необходимости отличить сброс по тайм-ауту сторожевого таймера от «нормального» сброса. Флаг  доступен только для чтения, т. е. он не может быть установлен обычной командой, как bsf STATUS,NOT_TO (NOT_TO — символическое имя для бита , определенное в стандартном заголовочном файле). Команда clrwdt деактивирует  (а также флаг , сбрасываемый командой sleep) и, разумеется, перезапускает блок сторожевого таймера.

В качестве примера давайте рассмотрим систему, выполняющую подсчет консервных банок, перемещаемых по конвейеру (см. Рис. 13.4), и накапливающую суммарное значение в регистре BEAN_COUNT. При сбросе по питанию в указанный регистр заносится нулевое значение. Если по какой-либо причине возникнет сбой программы и в результате тайм-аута сторожевого таймера микроконтроллер сбросится, то данное значение не должно измениться. Для этого нам придется написать код, проверяющий состояние бита  и выполняющий требуемые действия, например:

      __config _WDT_ON; Включаем сторожевой таймер

        org h’000’; Вектор сброса

MAIN btfSS STATUS,NOT_TO; Сброс от сторожевого таймера?

            clrf BEAN_COUNT; ЕСЛИ нет, ТО обнуляем счетчик

; Остальной инициализационный код ------------

             clrwdt; Устанавливаем NOT_ТO и сбрасываем сторожевой таймер

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

Поскольку генератор сторожевого таймера полностью независим от системного тактового сигнала, он продолжает работать даже после перевода микроконтроллера в «спящий» режим. Для этого команда sleep сбрасывает сторожевой таймер и деактивирует флаг . К тому же она активизирует флаг  (STATUS[3]), указывающий на то, что процессор находится в «спящем» режиме. Благодаря всем этим действиям между выполнением команды sleep и наступлением тайм-аута сторожевого таймера проходит время, равное одному периоду сторожевого таймера. Если тайм-аут наступит при нахождении микроконтроллера в спящем режиме, то микроконтроллер проснется и продолжит выполнение программы с команды, следующей за командой sleep[170]. Обычно этой командой является команда сброса сторожевого таймера clrwdt.

При необходимости программа может определить, что выход из спящего режима произошел по тайм-ауту сторожевого таймера, проверив флаги  и . В микроконтроллерах младшего уровня тайм-аут сторожевого таймера является единственным способом вывода микроконтроллера из спящего режима, не считая аппаратного сброса. В микроконтроллерах среднего и верхнего уровней выход из спящего режима тоже может осуществляться по внешнему прерыванию, а также, в некоторых случаях, по сигналу от Таймера 1 и аналоговых модулей, которые могут работать от собственного тактового генератора. Следует иметь в виду, что при включенном сторожевом таймере ток потребления микроконтроллера в спящем режиме возрастает с 0.9 мкА (5 мкА шах) до 7.5 мкА (30 мкА шах) (цифры приведены для модели PIC16F87X при VDD = 3 В, ТА —40…+85 °C). Если требуется длительная работа устройства от батарей (см., например, Вопрос для самопроверки 12.6 на стр. 449), то это может оказаться серьезной проблемой. Для сравнения скажу, что процессор, работающий на частоте 32.768 кГц при напряжении 3 В и выключенном сторожевом таймере, потребляет ток около 20 мкА (35 мкА max).

* * *

Модели младшего уровня имеют базовый 8-битный счетчик/предделитель, который первоначально назывался счетчиком/таймером реального времени (Real-Time Clock/Counter — RTCC). Хотя этот термин до сих пор встречается в документации на старые микроконтроллеры линейки PIC16CXXX, появление дополнительных таймеров привело к возникновению более логичного термина — Таймер 0. Тем не менее сокращение «RTCC» до сих пор встречается в старых книгах и программном обеспечении. Например, в компиляторе CCS задание внешнего тактового сигнала Таймера 0 с активным спадающим фронтом и коэффициента деления предделителя, равного 4, осуществляется вызовом функции setup_counters (rtcc_ext_1_to_h, rtcc_div_4[171]).

Из Рис. 13.3 можно заметить, что Таймер 0 представляет собой 8-битный счетчик, расположенный в регистре с адресом h’01’, подключенный к 8-битному предделителю. Таким образом, с помощью трех битов PS[2:0] регистра OPTION_REG мы можем задавать восемь различных частот сигнала, подаваемого на вход счетного регистра таймера. Поскольку предделитель Таймера 0 является также постделителем сторожевого таймера[172], для его подключения к Таймеру 0 необходимо сбросить бит PSA регистра OPTION_REG.

Рис. 13.3. Упрощенная структура Таймера 0

По умолчанию после сброса предделитель подключен к сторожевому таймеру. В этом случае счетчик таймера может работать либо от внутреннего системного тактового сигнала частотой fOSC/4 (т. е. с частотой, в четыре раза меньшей частоты на выводе XTAL1) или от внешнего сигнала, поступающего на вход RA4/T0CKI (или GP5/T0CKI) микроконтроллера. Для переключения между внутренним и внешним тактовым сигналом служит бит T0CS регистра OPTION_REG (OPTION_REG[5]). При работе от внешнего тактового сигнала активный фронт, по которому происходит инкрементирование счетного регистра, задается битом T0SE регистра OPTION_REG (OPTION_REG [4]).

Очевидно, что появление активного фронта на входе T0CKI никак не синхронизировано с внутренним тактовым сигналом микроконтроллера. Чтобы можно было обычным образом считывать и изменять содержимое счетного регистра Таймера 0, потребовалось ввести узел синхронизации. Синхронизация осуществляется с использованием 2-ступенчатого сдвигового регистра, подключенного к тактовому входу счетного регистра Таймера 0. Работа синхронизатора вызывает задержку длительностью 2 машинных цикла (1 мкс при резонаторе частотой 8 МГц). Такая же задержка возникает между операцией записи нового значения в счетный регистр Таймера 0 (h’01’) и реальным обновлением его содержимого при работе таймера непосредственно от внутреннего тактового сигнала.

При переполнении Таймера 0 (11111111 —> 00000000) устанавливается флаг прерывания T0IF (INTCON[2]). Если при этом установлен бит разрешения прерывания T0IE (INTCON[5]), то автоматически будет сгенерировано прерывание (см. Рис. 7.3 на стр. 213).

Длительность каждого из интервалов ВЫСОКОГО и НИЗКОГО уровней внешнего сигнала, используемого для непосредственного тактирования Таймера 0, должна быть не менее 2tOSC + 20 нc. Таким образом, при использовании 8-МГц резонатора (tOSC = 125 нc), как Thigh, так и 7iow должны быть не менее 270 нc, в результате чего максимальная частота счета составит 1.8 МГц. При использовании предделителя указанный минимальный суммарный период 4tOSC + 40 нc может быть уменьшен в заданное число раз. При этом к сигналу, подаваемому на предделитель, предъявляется единственное требование — длительность его импульсов должна быть не менее 10 нc. Таким образом, при использовании коэффициента деления 256 на вход T0CKI можно подавать сигнал частотой 50 МГц.

Предделитель недоступен для чтения, поэтому таймер не является 16-битным счетчиком в строгом смысле этого слова. Чтение счетного регистра таймера не влияет на предделитель, а вот любая команда, осуществляющая запись в счетный регистр (например clrf h’01’, movwf h’01’), наряду с изменением состояния Таймера 0 сбрасывает как предделитель, так и узел синхронизатора тактового сигнала.

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

clrwdt ; Сбрасываем предделитель и сторожевой таймер

bsf STATUS,RP0; Переключаемся в 1-й банк

movlw b’11110001’; Внешний тактовый сигнал, активный фронт — спадающий

movwf OPTION_REG; Предделитель 1:4, подключенный к Таймеру 0

bcf STATUS,RP0; Возвращаемся в 0-й банк

Разумеется, переключать предделитель между Таймером 0 и сторожевым таймером можно и в процессе выполнения основной программы. По уже указанным причинам перед изменением регистра OPTION_REG следует выполнить команду clrwdt во избежание непроизвольного сброса от сторожевого таймера.

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

Проиллюстрируем использование Таймера 0 в качестве счетчика событий на двух примерах. В первом примере, где мы также задействуем сторожевой таймер, производится подсчет консервных банок, перемещающихся по конвейеру (см. Рис. 13.4). После прохождения очередных 24 банок датчик должен сформировать импульс для упаковочной машины, чтобы заполненная коробка была заменена пустой. Длительность этого импульса должна составлять всего несколько микросекунд. Кроме того, необходимо предусмотреть двухбайтный счетчик, в котором будет накапливаться общее количество упакованных коробок с момента последнего сброса микроконтроллера. В конце смены это значение пересылается в центральный компьютер завода для инвентаризации.

Рис. 13.4. Подсчет консервных банок, проходящих по конвейеру

Сначала разберемся с секцией инициализации. Код этой секции, приведенный ниже, начинается с проверки флага . Если этот флаг сброшен, то фаза инициализации пропускается, поскольку сброс произошел из-за тайм-аута сторожевого таймера. В противном случае вывод T0CKI настраивается на вход, а вывод RB1 — на выход (на последнем формируется импульс управления упаковочной машиной).

    include "p16F877a.inc"

    __config _WDT_ON; Разрешаем работу сторожевого таймера

    cblock h’20’

     _work:1, _status:1

     COUNT:2

     endc

     org 0; Вектор сброса

     btfss STATUS,NOT_TO; Сброс от сторожевого таймера?

        goto MAIN_LOOP; ЕСЛИ да, TO пропускаем секцию инициализации

     goto MAIN; ИНАЧЕ начинаем с нуля

     org 4; Вектор прерывания

     goto ISR; Обработчик

MAIN bsf PORTB,1; Неактивное состояние линии управления упаковщиком

         bsf STATUS,RP0; Переключаемся в 1-й банк

         bsf TRISA,4; Вывод TOCKI — вход, а вывод RB1, управляющий

         bcf TRISB,1; упаковочной машиной, — выход

         movlw b’00101111’; Таймер 0 работает от внешнего сигнала, активный фронт -

         movwf OPTION_REG; спадающий, предделитель подключен к сторожевому таймеру

         movlw b’0110’; He забыть переключить порт А в цифровой режим

         movwf ADCON1; To есть выключить аналоговые входы

         bcf STATUS,RP0; Возвращаемся в 0-й банк

         bsf INTCON,T0IE; Разрешаем прерывание от Таймера 0

         movlw -d'24'; Загружаем в таймер -24 (E8h)

         movwf TMR0

         clrf COUNT+1; Сбрасываем 2-байтный счетчик коробок

         clrf COUNT

         bsf INTCON,GIE; Разрешаем прерывания

; Фоновая процедура, которая выполняет различные операции

MAIN_LOOP

         clrwdt; Периодически сбрасываем сторожевой таймер

... ...; Остальной код фонового цикла

Находясь в 1-м банке, мы также инициализируем регистр OPTION_REG — подключаем предделитель к сторожевому таймеру и увеличиваем его период тайм-аута в 128 раз. После этого конфигурируем Таймер 0, который должен тактироваться по спадающему фронту сигнала, поступающего на вход T0CKI. И наконец, после возвращения в 0-й банк заносим в счетный регистр таймера значение h’E8’ (т. е. -24), чтобы после поступления 24 импульсов отдатчика происходило переполнение таймера и генерировалось прерывание. После этого для разрешения данного прерывания мы устанавливаем флаги T0IE и GIE регистра INTCON.

Основная фоновая программа начинается с команды clrwdt. Если время выполнения одного прохода основного бесконечного цикла будет не более 7 х 128 = 0.8961 с, т. е. меньше периода сторожевого таймера, то тайм-аут никогда не наступит.

Теперь нам осталось только написать процедуру обработки прерывания, которая будет автоматически вызываться после накопления 24 банок, т. е. после поступления 24 импульсов на вход Таймера 0 и его переполнения. При этом устанавливается флаг T0IF и микроконтроллер переходит по адресу вектора сброса h’004’. В нашем инициализационном коде по указанному адресу мы разместили команду goto ISR. Сам код обработчика прерывания приведен в Программе 13.1.

Программа 13.1. Процедура обработки прерывания счетчика консервных банок

; ****************

; * В обработчике формируется импульс управления упаковочной *

; * машиной и реинициализируется Таймер 0 значением -24. Также *

; * в COUNT:2 накапливается общее количество упакованных коробок *

; * для анализа в фоновом цикле *

; ****************

; Сначала сохраняем контекст

ISR movwf _work; Сохраняем W

     swapf STATUS,w; и регистр STATUS

     movwf _status

; =============

; Основной код

     btfss INTCON,T0IF; Было переполнение таймера?

        goto ISR_EXIT; ЕСЛИ нет, ТО ложная тревога

      bcf PORTB,1; Формируем передний фронт импульса

      movlw -d’24’; Реинициализируем Таймер 0

      movwf TMR0

      incf COUNT+1,f; Увеличиваем счетчик коробок на 1

      btfsc STATUS,Z

        incf COUNT,f

      bcf INTCON,T0IF; Сбрасываем флаг прерывания

      bsf PORTB,1; Формируем задний фронт импульса

; ===============

ISR_EXIT swapf _status,w; Восстанавливаем регистр STATUS

              movwf STATUS

              swapf _work,f; Восстанавливаем W,

              swapf _work,w; не затрагивая STATUS,

              retfie; и выходим из прерывания

Основной код обработчика заключен в «обертку», выполняющую переключение контекста согласно Программе 7.2 (стр. 226). Ядро обработчика выполняет следующие операции:

• Формирует импульс управления упаковочной машиной на выводе RB1.

• Заносит в счетный регистр таймера число —24.

• Инкрементирует двухбайтную переменную общего количества коробок.

• Сбрасывает флаг прерывания от Таймера 0 — T0IF.

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

Альтернативный подход с использованием внешнего прерывания можно посмотреть в Программе 7.2, приведенной на стр. 226.

Во втором примере демонстрируется работа модуля Таймера 0 в качестве собственно таймера, выполняющего измерение времени между событиями. В данном случае этими событиями являются всплески ЭКГ, изображенные на Рис. 7.1 (стр. 208). При обнаружении такого всплеска пиковый детектор прерывает работу микроконтроллера, в котором организован 2-байтный счетчик, работающий от внешнего генератора частотой 10 кГц. Таким образом, мы можем с дискретностью 100 мкс (назовем эту величину «тиком») определить интервал между событиями. Мы несколько изменим требования, предъявляемые к системе, чтобы избавиться от внешнего генератора, и воспользуемся для накопления числа тиков Таймером 0 (один тик в данном случае будет равен 1 мс).

Для решения данной задачи нам потребуется так сконфигурировать Таймер 0, чтобы при его работе от основного тактового сигнал микроконтроллера (через предделитель) переполнение происходило бы каждую миллисекунду (1000 мкс). Если мы возьмем кварцевый резонатор с частотой 4.096 МГц, то из уравнения

Тайм-аут = 1000 мкс = (4/4.096) х 256 х Коэффициент деления предделителя

получим, что коэффициент деления предделителя должен быть равен 4. Учитывая указанные требования, напишем секцию инициализации:

       org 0; Вектор сброса

        goto MAIN; Фоновая программа

        org 4;Вектор прерывания

        goto ISR; Обработчик

MAIN clrwdt; Сбрасываем сторожевой таймер

         bsf STATUS,RP0; Переключаемся в 1-й банк

         movlw b’00000001’; Прерывание по спадающему фронту, внутр. такт, сигнал

         movwf OPTION_REG; Предделитель — 1:4, подключен к Таймеру 0

         bcf STATUS,RP0; Возвращаемся в 0-й банк

         clrf NEW; Обнуляем флаг нового события

         bsf NTCON,T0IE; Разрешаем прерывание от Таймера 0

         bsf INTCON,INTE; Разрешаем внешнее прерывание

         bsf INTCON,GIE; Разрешаем работу системы прерываний

         clrf TMR0; Сбрасываем таймер

         clrf COUNT; Обнуляем счетчик тиков

         clrf COUNT+1

Помимо разрешения прерывания от Таймера 0, устанавливается также флаг INTE для разрешения внешнего прерывания с вывода INT, на который подается сигнал с пикового детектора. При этом нам не требуется обнулять ни Таймер 0, ни 2-байтный счетчик тиков, поскольку первый отсчет из серии всегда будет неверным — ведь сердцебиение пациента не синхронизировано со сбросом микроконтроллера! Однако регистр NEW, в который заносится ненулевое значение при каждом обнаружении импульса ЭКГ, сбрасывается.

Основное ядро обработчика прерывания, код которого приведен в Программе 13.2, выполняет следующие действия:

1. По прерыванию от Таймера 0:

• инкрементирует 2-байтный счетчик тиков;

• сбрасывает флаг прерывания от таймера T0IF;

• выходит из прерывания.

2. По внешнему прерыванию от пикового детектора:

• копирует содержимое счетчика тиков в РОНы;

• обнуляет Таймер 0;

• устанавливает флаг NEW;

• сбрасывает флаг внешнего прерывания;

• выходит из прерывания.

Программа 13.2. Измерение периода ЭКГ с разрешением 1 мс

; *******************

; * По прерыванию от Таймера 0 в обработчике инкрементируется *

; * 2-байтный счетчик COUNT *

; * По внешнему прерыванию COUNT:2 копируется в DATA:2 *

; * и устанавливается флаг NEW, извещающий фоновую программу *

; * о готовности новых данных *

; ********************

; Сначала сохраним контекст

ISR movwf _work; Сохраняем W

      swapf STATUS,w; и регистр STATUS

      movwf _status

; ********************

; Основной код

      btfss INTCON,T0IF; Сердечный импульс?

        goto HEART_BEAT; ЕСЛИ да, TO обрабатываем его

      incf COUNT+1,f; Регистрируем очередной 1-мс тик

      btfsc STATUS,Z; ЕСЛИ перешли через ноль,

        incf COUNT,f; ТО инкрементируем старший байт

      bcf INTCON,T0IF; Сбрасываем флаг прерывания

      goto ISR_EXIT

HEART_BEAT; Сюда попадаем при обнаружении импульса ЭКГ

      movf COUNT+1,w; Берем младший байт периода

      movwf DATUM+1; Копируем в пользовательский регистр

      movf COUNT,w; Берем старший байт периода

      movwf DATUM

      clrf COUNT+1; Обнуляем счетчик тиков

      clrf COUNT

      btfsc INTCON,INTF; Сбрасываем флаг прерывания

      incf NEW,f; Сообщаем о наличии новых данных

; ********************

ISR_EXIT swapf _status,w; Восстанавливаем регистр STATUS

              movwf STATUS

              swapf _work,f; Восстанавливаем W,

              swapf _work,w; не затрагивая STATUS,

              retfie; и выходим из прерывания

По внешнему прерыванию оба байта из регистров COUNT: COUNT+1 копируются в пользовательские регистры DATUM: DATUM+1, после чего счетчик тиков и Таймер 0 обнуляются для регистрации следующего события. Фоновая программа постоянно опрашивает регистр NEW, ненулевое содержимое которого говорит о том, что имеется новое значение. В дальнейшем это значение можно будет, например, записать в последовательную EEPROM, как в Примере 12.3 на стр. 439, или же передать по последовательному каналу в ПК для последующей обработки и отображения.

* * *

Большинство PIC-микроконтроллеров среднего и старшего уровней имеют, по крайней мере, два дополнительных таймера/счетчика со следующими возможностями:

Таймер 1

Этот 16-битный таймер имеет собственный вспомогательный генератор и программируемый предделитель. Состояние этого таймера можно запомнить по внешнему событию. Кроме того, он может изменять состояние определенного вывода микроконтроллера при достижении некоторого предустановленного значения.

Таймер 2

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

Модуль захвата/сравнения/ШИМ

Оба таймера могут работать совместно с модулем захвата/сравнения/ШИМ (Capture/Compare/PWM — ССР), который позволяет считывать текущее состояние Таймера 1 («захват»), сравнивать состояние Таймера 1 с заданным значением («сравнение»), а также обеспечивает автоматическую генерацию ШИМ-сигнала на базе Таймера 2.

Таймер 1 состоит из базового 16-битного счетчика, реализованного в виде пары РСН, которые называются TMR1L (младший байт) и TMR1H (старший байт). При переполнении этого счетчика устанавливается флаг прерывания TMR1IF в регистре прерываний от периферийных устройств PIR1 (PIR1 [0]).

Для тактирования этого таймера может использоваться внешний сигнал — либо подаваемый на вход TICKI микроконтроллера, либо от собственного генератора Таймера 1. Кроме того, в качестве тактового сигнала таймера может использоваться системный тактовый сигнал частотой fOSC/4. Независимо от источника тактового сигнала его частота может быть уменьшена с помощью счетчика предделителя. Внешние импульсы дополнительно могут синхронизироваться с системным генератором. Управляемая версия модуля таймера позволяет извне запрещать подачу тактового сигнала на счетный регистр с помощью вывода  микроконтроллера.

Регистр управления Таймера 1 T1CON (см. Рис. 13.5) используется для задания различных функций таймера. После сброса все биты этого регистра равны О, что соответствует следующим установкам: Таймер 1 и его внешний генератор отключены, коэффициент деления предделителя равен 1, для тактирования таймера используется системный тактовый сигнал.

Рис. 13.5. Функциональная схема Таймера 1

∙ TMR1ON

Установка бита T1CON[0] в 1 разрешает работу Таймера 1. В одних случаях[173] связанные с Таймером 1 выводы микроконтроллера автоматически переключаются в режим входа независимо от установок регистров TRIS, а в других[174] — программа должна принудительно переключить эти выводы на вход.

∙ TMR1CS, T10SCEN

Если бит TMR1CS (T1CON[1]) установлен в 1, то Таймер 1 будет тактироваться от системного тактового сигнала. В противном случае используется внешний источник тактовых импульсов.

В последнем случае счет осуществляется либо по нарастающему фронту сигнала на выводе T1CKI, либо, если бит разрешения генератора Таймера 1 TIOSCEN (T1CON[3]) установлен в 1, по сигналу «собственного» генератора таймера, независимого от основного генератора микроконтроллера. Наличие такого генератора позволяет избежать подбора частоты основного кварцевого резонатора в соответствии с требованиями таймера, чем мы и воспользовались в нашем детекторе пиков ЭКГ на базе Таймера 0 (см. стр. 460). В качестве времязадающего элемента в этом генераторе используется кварцевый резонатор, подключаемый к выводам TIOSCO/TICKI и T10SC1. Максимальная частота такого резонатора составляет 200 кГц, однако, как правило, используется часовой кварц с частотой 32.768 кГц (215 Гц).

∙ T1CKPS[1:0]

Независимо от источника тактовых импульсов инкрементирование 16-битного счетного регистра может производиться как непосредственно по данному сигналу, так и по каждому второму, четвертому или восьмому импульсу. Это определяется установками битов T1CON[5:4], как показано на Рис. 13.5.

Переполнение Таймера 1 и установка флага прерывания TMR1IF происходит после 216 = 65 536 событий, считая от нуля. Этот флаг, в свою очередь, может использоваться для прерывания работы процессора, если парный ему бит маски TMR1IE в регистре PIE1 (см. Рис. 7.5 на стр. 223) установлен в 1. Разумеется, никто не запрещает отслеживать это событие путем обычного опроса данного флага. В любом случае флаг TMR1IF должен быть сброшен вручную после обнаружения переполнения.

К примеру, при использовании резонатора с частотой 32.768 кГц переполнение Таймера 1 будет происходить каждые 2 с при T1CKPS[1:0] = 00 и каждые 16 с при T1CKPS[1:0] = 11.

По умолчанию сигнал с выхода программируемого предделителя синхронизируется с системным тактовым сигналом, что приводит к появлению задержки, равной двум машинным циклам. Однако в отличие от Таймера 0 в этом таймере сигнал может передаваться в обход сдвигового регистра синхронизатора при установке бита  (T1CON[2]) в 1. Наличие асинхронного режима позволяет использовать Таймер 1 с внешним источником тактового сигнала при нахождении микроконтроллера в «спящем» режиме. Поскольку сдвиговый регистр синхронизатора тактируется системным тактовым сигналом fOSC, который в «спящем» режиме отключается, возможность обхода данного регистра просто необходима. Также асинхронный режим необходимо использовать, когда частота внешнего тактового сигнала в 4 раза больше частоты системного резонатора (в этом случае наличие синхронизатора приведет к пропуску некоторой части событий).

За исключением указанных случаев, бит  должен быть сброшен, поскольку отсутствие синхронизации может привести к непредсказуемому результату при попытке записи в счетный регистр Таймера 1 в тот момент, когда осуществляется его инкрементирование по внешнему событию. Если требуется изменить состояние Таймера 1 в асинхронном режиме, то его необходимо остановить сбросом бита TMR1ON. Так, для записи в Таймер 1 константы h’8000’:

movlw h’80’; Новое значение старшего байта

bcf T1CON,TMR1ON; Останавливаем таймер

movwf TMR1H; Загружаем в Таймер 1 число 8000h

clrf TMR1L

bsf T1CON,TMR1ON; Перезапускаем таймер

Изменение состояния Таймера 1 всегда вызывает сброс счетчика предделителя.

Если Таймер 1 работает в синхронном режиме, его значение можно изменять «на лету». Необходимо только быть аккуратным при изменении обоих байтов счетчика, поскольку может случиться так, что после изменения старшего байта переполнение младшего произойдет до записи в него нового значения, в результате чего старший байт изменится нежелательным образом. Чтобы избежать этого, перед записью нового значения следует обнулить младший байт. Вот как, например, можно записать в Таймер 1 «налету» число h’9FFF’:

movlw h’9F’; Новое значение старшего байта

clrf TMR1L; Предохраняемся от переполнения младшего байта

movwf TMR1H; Обновляем старший байт счетного регистра

movlw h’FF’; Новое значение младшего байта

movwf TMR1L; Обновляем младший байт счетного регистра

Содержимое Таймера 1 можно прочитать в любой момент времени даже при работе в асинхронном режиме. Однако, поскольку одновременно можно считать только один байт[175], возможна ситуация, при которой между двумя последовательными операциями чтения произойдет переполнение таймера, например:

; Предположим, что текущее состояние Таймера 1 — h’80FF’

     movf TMR1L,w; Читаем младший байт = h’FF’

     movwf TEMPL; Запоминаем

; ««в этот момент состояние Таймера 1 изменилось на h’8100’»»

     movf TMR1H,w; Читаем старший байт = h’81’

     movwf TEMPH; Запоминаем. Считанное значение равно h’81FF’!!!

В результате выполнения предыдущего кода мы получим значение h’81FF’ вместо h’80FF’. Возникновение такой ситуации наиболее вероятно при генерации прерывания от другого периферийного устройства между последовательными операциями чтения.

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

Т1_СЕТ movf TMR1H,w; Считываем старший байт

             movwf TEMPH; Запоминаем

             movf TMR1L,w; Считываем младший байт

             movwf TEMPL; Запоминаем

; Теперь проверим старший байт на предмет изменения

             movf TMR1H,w; Снова считываем старший байт

             subwf TEMPH,w; Проверим на равенство с предыдущим значением

             btfss STATUS,Z; ЕСЛИ не отличается, ТО пропускаем

                goto T1_GET; ИНАЧЕ выполняем чтение повторно

Так или иначе, но при необходимости считывания точных значений крайне желательно запрещать прерывания сбросом бита GIE до окончания чтения обоих байтов. Альтернативный способ, позволяющий считывать два байта за один цикл, обсуждается на стр. 469.

При работе Таймера 1 от внутреннего тактового сигнала (TMR1CS = 0) синхронизация не требуется. В этом случае значение бита  игнорируется.

∙ TMR1GE, T1GINV

Некоторые последние модели, такие как PIC12F675, имеют управляемую версию модуля Таймера 1 (см. Рис. 13.5). При установленном бите TMR1GE (T1CON[6 |) изменение состояния таймера может быть приостановлено подачей на вывод  ВЫСОКОГО уровня.

В других вариантах управляемого Таймера 1 для задания активного уровня сигнала на выводе  используется бит T1GINV (T1CON[7]). Такой модуль таймера (реализованный, к примеру, в микроконтроллере PIC16F684) может управляться не только сигналом с вывода , но и сигналом с выхода 2-го компаратора (см. Рис. 14.6 на стр. 497). Указанная возможность позволяет измерять временные параметры аналоговых сигналов.

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

Поскольку максимально возможный период переполнения таймера составляет всего 16 с (см. стр. 464), для отсчета интервала в 900 с нам потребуется хранить число переполнений. Задав период переполнения таймера, равный 4 с, получим, что для отсчета требуемых 15 мин нам потребуется отсчитать 900/4 = 225 переполнений. Соответственно, процедура инициализации и общая структура программы будут похожи на код, приведенный в Программе 13.3. В данном случае Таймер 1 конфигурируется для использования внешнего генератора с коэффициентом предделителя 2, что даст нам значение тика, равное 4 с.

Программа 13.3. Формирование 15-минутного интервала

        include "p16f877a.inc"

         __config _WDT_OFF & _CP_OFF

        cblock h’20’

          JIFFY:1

         endc

         org 0

MAIN movlw Ь’00011111’; Таймер включен, внешний тактовый сигнал асинхронный режим

         movwf T1CON; Внешний генератор включен, предделитель — 1:2

         clrf JIFFY; Обнуляем счетчик тиков

         bsf STATUS,RP0; Переключаемся в 1-й банк

         bsf PIE1,TMR1IE; Разрешаем прерывание от Таймера 1

         bcf STATUS,RP0; Возвращаемся в 0-й банк

DOOZE sleep; Ждем прерывания

           bcf PIR1,TMR1IF; Сбрасываем флаг прерывания

           incf JIFFY,f; Запоминаем очередной тик

           movlw d’225’; Уже 225 тиков =15 мин?

           subwf JIFFY,w

           btfss STATUS,Z; ЕСЛИ да, ТО делаем что-нибудь полезное

             goto DOOZE; ИНАЧЕ ждем еще 15 с

; Делаем выборку

           clrf JIFFY; Сбрасываем счетчик тиков

           call SAMPLE; Считываем температуру и передаем ее

           goto DOOZE; Начинаем отсчет следующего интервала

Для снижения энергопотребления микроконтроллер будет бóльшую часть времени находиться в «спящем» режиме, пробуждаясь каждые четыре секунды. Для этого бит маски TMR1IE (PIE1 [0]) устанавливается в 1. После выхода микроконтроллера из «спящего» режима флаг прерывания TMR1IF сбрасывается и инкрементируется значение счетчика тиков. Затем оно сравнивается с константой 225. В случае равенства счетчик обнуляется и вызывается подпрограмма, осуществляющая передачу нового значения температуры на базовый компьютер.

Следует отметить, что включенный Таймер 1 увеличивает потребление микроконтроллера на величину порядка 20 мкА. Особенно следует обращать на это внимание, если Таймер 1 используется для вывода микроконтроллера из режима пониженного потребления, в котором потребление микроконтроллера с выключенной периферией составляет всего 0.9 мкА (все цифры приведены для моделей PIC16F87X).

* * *

Совместно с Таймером 1 (а также, как мы увидим позже, и с Таймером 2) используется один (например, в PIC16F62X) или два (например, в PIC16F87X) модуля захвата/сравнения/ШИМ (Capture/Compare/PWM — ССР). Каждый модуль ССР, по существу, состоит из 16-битного регистра (поскольку счетный регистр Таймера 1 является двухбайтным) и 16-битного цифрового компаратора для сравнения состояния Таймера 1 и содержимого регистра модуля ССР. Поскольку модули ССР1 и ССР2 практически идентичны (они используют один и тот же задающий Таймер 1[176]), отличаясь только различными контактами ввода/вывода ССР1 и ССР2, мы будем рассматривать модуль ССР1. Там, где это необходимо, различия между модулями будут указаны отдельно.

Модуль ССР выполняет три основных функции:

• При его работе в режиме захвата (Capture) появление заданного события на выводе, связанном с модулем ССР, вызывает копирование состояния счетного регистра Таймера 1 в регистр ССР. Эту возможность можно использовать для определения момента наступления данного события или его длительности с разрешением вплоть до 12.5 нc.

• При работе в режиме сравнения (Compare) в случае равенства счетного регистра Таймера 1 и содержимого регистра модуля ССР изменяется состояние соответствующего вывода модуля или осуществляется сброс Таймера 1. Эта возможность может использоваться для аппаратного формирования сигналов с разрешающей способностью 200 нc.

• При работе в режиме ШИМ (PWM) модуль ССР совместно с Таймером 2 может использоваться для аппаратного формирования сигнала с широтноимпульсной модуляцией разрядностью до 10 бит (разрешение 0.1 %) с программируемыми периодом и скважностью.

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

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

Работа модуля в режиме захвата показана на Рис. 13.6. В этом режиме имеются следующие подрежимы:

∙ 0000

При сбросе по питанию или по снижению напряжения питания все биты регистра обнуляются. При этом модуль ССР отключается, а предделитель сбрасывается. Чтобы избежать непредусмотренных прерываний при изменении режима работы модуля, его рекомендуется выключать перед сменой режима.

∙ 0100

По спадающему  фронту на выводе ССР1 оба байта счетного регистра Таймера 1 одновременно копируются в пару регистров CCPR1H: L. При этом устанавливается флаг прерывания CCP1IF (PIR1 [2]) и, при установленном бите маски CCP1IE (PIE1 [2]), генерируется прерывание.

Модуль ССР2 функционирует точно так же, за исключением того, что соответствующие биты флага CCP2IF и маски CCP2IE прерывания расположены в регистрах PIR2 (PIR2[0]) и PIE2 (Р1Е2[0]) соответственно. Причем во многих моделях среднего уровня эти биты являются единственными задействованными в указанных регистрах.

∙ 0101

Описанный выше процесс захвата производится по нарастающему  фронту на выводе модуля ССР.

∙ 0110

Захват производится по четвертому нарастающему фронту на выводе ССРn.

∙ 0111

Захват производится по шестнадцатому нарастающему фронту на выводе ССРn.

Рис. 13.6. «Захват» времени наступления события

После наступления заданного события процессор может считать сохраненное значение (время) либо в обработчике прерывания, либо после установки опрашиваемого флага CCPIF в 1. Если Таймер 1 после каждого события сбрасывается, то данное значение представляет собой время, прошедшее с момента наступления предыдущего события. Если же инкрементирование Таймера 1 не прекращается, то для определения времени между событиями достаточно вычесть новое значение из значения, запомненного во время предыдущего прерывания. Поскольку режим модуля ССР допускается изменять «налету», мы можем измерять интервал между нарастающим и спадающим фронтами на входе модуля ССР1, переключая между операциями захвата бит ССР1М[0]. При изменении режима возможна самопроизвольная установка флага прерывания ССР1IF. Чтобы предотвратить генерацию ложного прерывания, необходимо перед изменением режима сбрасывать бит ССР1IЕ, а после изменения режима — бит ССР1IF. Также можно использовать разные модули для захвата по каждому из фронтов, скажем, модуль ССР1 — для захвата по нарастающему фронту, а модуль ССР2 — для захвата по спадающему фронту (см. Пример 13.3).

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

В качестве примера воспользуемся модулем ССР для измерения периода сигнала кардиограммы, подключив пиковый детектор, показанный на Рис. 7.1 (стр. 208), к выводу ССР1. Предполагая, что Таймер 1 работает в синхронном режиме от собственного кварцевого резонатора частотой 32.768 кГц, инициализационная часть программы может выглядеть следующим образом:

movlw b’00001011’; Таймер включен, внешний такт, сигнал, синхр. режим

movwf T1CON; Генератор включен, предделитель — 1:1

movlw b’00000100’; Режим захвата по спадающему фронту

movwf CCP1CON

clrf NEW; Обнуляем флаг NEW

clrf TMR1H; Обнуляем Таймер 1

clrf TMR1H

bsf STATUS,RP0; Переключаемся в 1-й банк

bsf PIE1,CCP1IE; Разрешаем прерывание от ССР1

bcf STATUS,RP0; Возвращаемся в 0-й банк

bcf PIR1,CCP1IF; Сбрасываем флаг прерывания

bsf INTCON,PEIE; Разрешаем прерывания от периферийных устройств

bsf INTCON,GIE; Разрешаем работу системы прерываний

В обработчике прерывания просто считывается содержимое регистра ССР, которое затем сохраняется в двух временных регистрах. Затем в регистр NEW заносится ненулевое значение, извещающее фоновую программу о наличии нового значения. После этого Таймер 1 сбрасывается для регистрации следующего события.

При использовании резонатора частотой 32.768 кГц и отключенном предделителе разрешающая способность считываемого значения составит 30.5 мкс. Переполнение Таймера 1 при таких параметрах конфигурации будет происходить каждые 2 с, и этого достаточно для регистрации сердечного ритма частотой до 30 ударов в минуту (см. Программу 13.4).

Программа 13.4. Определение момента появления точки R на ЭКГ

; **************

; Сначала сохраняем контекст обычным образом

ISR movwf _work; Сохраняем W

      swapf STATUS,w; и регистр STATUS

      movwf _status

; ===========

; Основной код

      btfss PIR1,CCP1IF; Было прерывание от CCP1?

        goto ISR_EXIT; ЕСЛИ нет, ТО ложная тревога

      incf NEW, f; Сообщаем о новом захвате

      bcf PIR1,CCP1IF; Сбрасываем флаг прерывания

      movf CCPR1L,w; Считываем младший байт

      movwf TEMP+1; Запоминаем его

      movf CCPR1H,W; Считываем старший байт

      movwf TEMP; Запоминаем его

      clrf TMR1L; Обнуляем Таймер 1

      clrf TMR1H

; ============

ISR_EXIT swapf _status,w; Восстанавливаем регистр STATUS

              movwf STATUS

              swapf _work,f; Восстанавливаем регистр W,

              swapf _work,w; не затрагивая регистр STATUS,

              retfie ; и выходим из прерывания

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

Режимы 1000…1011, указанные на Рис. 13.7, соответствуют четырем режимам сравнения. В этих режимах производится сравнение 16-битного счетного регистра Таймера 1 с содержимым пары регистров CCPR1H: L. При совпадении указанных значений устанавливается флаг прерывания CCP1IF (PIR1[2]) и, при установленном бите маски ССР1IЕ (PIE1[2]), генерируется прерывание.

Помимо установки флага CCP1IF при наступлении события «совпадение» может выполняться одно из четырех действий, определяемое состоянием битов ССР1М[3:0]:

Рис. 13.7. Модуль ССР1 в режиме «Сравнение»

1000: Установить вывод микроконтроллера при совпадении

На вывод ССР1 выставляется ВЫСОКИЙ уровень. Защелка модуля ССР может быть сброшена только переключением модуля в режим 0000, т. е. при его выключении.

1001: Сбросить вывод микроконтроллера при совпадении

На вывод ССР1 выставляется НИЗКИЙ уровень. Защелка модуля ССР может быть установлена только выключением модуля.

1010: Генерировать прерывание при совпадении

Состояние вывода ССР1 не изменяется, единственным действием является установка флага ССР1 IF.

1011: Сформировать специальное событие при совпадении

Таймер 1 сбрасывается. При использовании модуля ССР2 (это единственное отличие в функционировании модулей ССР1 и ССР2) можно запустить преобразование АЦП (см. Рис. 14.11 на стр. 510).

В режимах 1000 и 1001 выводы микроконтроллера, используемые модулями ССР1 и ССР2, должны быть сконфигурированы как выходы. При отключенном модуле ССР (после любого сброса) эти выводы будут отображать состояние соответствующих битов порта. В качестве примера давайте рассмотрим выключение модуля ССР. Поскольку единственным способом переключения защелки модуля в ее исходное состояние является запись в регистр CCPCON режима 0000, логично будет записать это значение в соответствующий бит порта для исключения нежелательных выбросов. К примеру, при использовании режима 1001 бит регистра порта следует при инициализации установить в 1, чтобы после сброса на этом выводе присутствовал ВЫСОКИЙ уровень. В микроконтроллерах, выпускающихся в корпусах с количеством выводов более 28, вывод ССР1, как правило, задействует линию RC2, а вывод ССР2 — линию RC1.

В качестве примера предположим, что нам необходимо сконфигурировать Таймер 1 также, как и в предыдущем примере, — чтобы его переполнение происходило каждые 10 с. Для этого нам необходимо задать период тайм-аута, равный 16 с (коэффициент деления предделителя 8), а затем укоротить цикл. Соответственно в регистр CCPR1 следует загрузить 10/16 от максимального значения (216 х 10/16), что составит h’A000’. При достижении таймером данного значения он будет автоматически сбрасываться, и если бит маски CCP1IE (а также биты PEIE и GIE) установлен, то будет генерироваться прерывание.

Инициализационный код для данной задачи выглядит следующим образом:

movlw h’A0’; Загружаем в CCPR1 число h’A000’

movwf CCPR1H;

clrf CCPR1L;

movlw b’00001011’; Режим ССР1 — 1011. Специальное событие

movwf CCP1C0N

movlw b’00111011’; Таймер 1 вкл. (1), внешний такт. сигнал (1)

movwf T1CON; Синхр. режим (0), генератор вкл. (1), 1:8 (111)

bsf STATUS,RP0; Переключаемся в 1-й банк

bsf PIE1,CCP1IE; Разрешаем прерывание от ССР1

bcf STATUS,RP0; Возвращаемся в 0-й банк

bsf INTCON,PEIE; Разрешаем прерывания от таймера/ССР

bsf INTCON,GIE; Разрешаем работу системы прерываний

После выполнения этих команд микроконтроллер будет автоматически прерываться каждые 10 с.

Поскольку в режиме 1011 вывод ССР1 отключен от модуля ССР, указанный вывод может использоваться как обычный вывод порта ввода/вывода независимо от модуля ССР1.

* * *

Таймер 2 представляет собой 8-битный счетчик с программируемым предделителем и постделителем, как показано на Рис. 13.8. Этот счетчик всегда тактируется от системного тактового сигнала. В отличие от двух предыдущих таймеров, выходным сигналом является сигнал не с выхода счетчика, а с выхода компаратора Таймера 2. Этот блок сравнивает состояние Таймера 2 с содержимым регистра периода PR2. При равенстве указанных значений на выходе формируется импульс, который сбрасывает Таймер 2 в момент прихода следующего счетного импульса. Эта возможность, в частности, может использоваться для задания скорости передачи по интерфейсу SPI модуля SSP, как показано на Рис. 12.9 (стр. 385). В соответствии с установками постделителя по истечении заданного числа этих сбросов (от 1 до 16) будет устанавливаться флаг прерывания от Таймера 2 TMR2IF в регистре PIR1 (PIR1[1]), а при установленном бите маски прерывания TMR2IE также будет генерироваться прерывание.

Значения коэффициентов деления пред- и постделителя, а также управление Таймером 2 осуществляется с использованием регистра управления T2CON, как описано ниже. После сброса микроконтроллера все биты этого регистра сбрасываются, выключая Таймер 2 и устанавливая коэффициенты деления, равные 1.

Рис. 13.8. Упрощенная функциональная схема Таймера 2

∙ TMR2ON

Включение Таймера 2 осуществляется установкой бита T2CON[2] в 1.

∙ T2CKPS[1:0]

Инкрементирование счетного регистра таймера может осуществляться либо с частотой тактового сигнала fOSC/4, или же с частотой, меньшей в 4 или 16 раз. Три возможные установки битов Т2СON[1:0] показаны на Рис. 13.8.

∙ TOUTPS[3:0]

Число периодов Таймера 2, после которых устанавливается флаг прерывания TMR2IF, может быть задано с помощью битов TOUTPS[3:0] (T2CON[5:2]). Этот 4-битный код n соответствует коэффициенту 1:(n + 1); от Ь’0000’ = 1:1 до Ь’1111’ = 1:16.

Преимуществом такой архитектуры является то, что подстройку точного значения периода тайм-аута можно осуществить без использования модуля ССР, просто записью требуемого значения в регистр периода. Длительность интервала до установки флага TMR2IF будет определяться выражением

(4/fOSC) х Предделитель х (PR + 1) х Постделитель.

В качестве примера предположим, что нам необходимо формировать прерывание 100 раз в секунду. Если предположить, что микроконтроллер работает от резонатора с частотой 4 МГц, то, задав коэффициент деления предделителя равным 4, мы получим период тактового сигнала Таймера 2, равный 4 мкс. Если в регистр периода загрузить число 249, то период импульсов на выходе компаратора Таймера 2 составит 250 х 4 = 1 мс. А задав коэффициент деления постделителя, равный 10 (1001), получим период прерывания 10 мс (частота 100 Гц). Изменяя коэффициент деления постделителя от 1 до 16, мы сможем регулировать период генерации прерывания от 1 до 16 мс. Для точной подстройки периода с шагом, равным 4 х Постделитель [мкс], можно изменять содержимое регистра PR2.

Инициализационный код для этого примера выглядит следующим образом:

movlw b’01001101’; Постделитель 1:10 (1001), Таймер 2 вкл. (1)

movwf T2CON; Предделитель 1:4 (01)

bsf STATUS,RP0; Переключаемся в 1-й банк

movlw d’49’; Задаем период, равный 249

movwf PR2

bsf PIE1,TMR2IE; Разрешаем прерывание от Таймера 2

bcf STATUS,RP0; Возвращаемся в 0-й банк

bsf INTCON,PEIE; Разрешаем все прерывания от таймеров и модуле

bsf INTCON,GIE; Разрешаем прерывания

В компиляторе CCS имеется своя функция для инициализации Таймера 2 —

setup_timer_2(<режим>, <период>, <постделитель>):

       setup_timer_2(T2_DIV_BY_4, 249,10);

       enable_interrupts(INT_TIMER2);

       enable_intqrrypts(GLOBAL);

Содержимое счетного регистра таймера можно прочитать с помощью функции get_timer2 (), а изменить — с помощью функции set_timer_2 ().

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

Примеры таких сигналов показаны на Рис. 13.9. Средняя амплитуда вычисляется как A х N, где N — коэффициент заполнения. При изменении N ot 0 до 100 %, средняя мощность, выделяемая на нагрузке, будет изменяться пропорционально — и все это без использования аналоговых элементов. Такой метод преобразования цифрового сигнала в аналоговую форму называется широтно-импульсной модуляцией (ШИМ).

Тепловая или механическая инерция большинства мощных нагрузок такова, что даже при относительно низкой частоте переключения (обычно не менее 100 Гц) «выбросы» будут сглаживаться. Низкие частоты переключения более эффективны, поскольку энергия рассеивается при каждом переключении. Если же ШИМ используется для более привычного цифро-аналогового преобразования, например, в аудиотехнике, то для снижения уровня высокочастотных гармоник полученный сигнал следует пропустить через фильтр нижних частот. При этом для отсечения нежелательных гармоник и уменьшения необходимой степени фильтрации частота выборок должна быть как минимум в 10 раз больше максимальной частоты аналогового сигнала (см. Рис. 14.3 на стр. 494).

Рис. 13.9. Широтно-импульсная модуляция

Формирование ШИМ-сигнала обычно осуществляется с использованием счетчика и схемы сравнения. Выход управляется защелкой, которая устанавливается при каждом переполнении таймера. Сброс защелки производится при достижении счетчиком числа, соответствующего коэффициенту заполнения. Чем больше это число, тем больше доля времени, в течение которого на выводе будет присутствовать ВЫСОКИЙ уровень.

В качестве примера рассмотрим 3-битный счетчик, в котором длительность рабочего импульса задана равной Ь’011’:

В этом примере ВЫСОКИЙ уровень на выводе будет присутствовать в течение трех тактов, что даст нам коэффициент заполнения, равный 3/8 или 37.5 %. Изменяя это число, можно регулировать среднюю мощность от 0 до 87.5 % с шагом 1/8.

В микроконтроллерах PIC для формирования таких сигналов используются модули ССР. При работе модуля в режиме ШИМ в качестве основного счетчика используется Таймер 2, а число, определяющее коэффициент заполнения, загружается через регистр с двойным буферированием в 10-битный компаратор. На Рис. 13.10 показана структурная схема модуля ССР1 в режиме ШИМ, формирующего сигнал на соответствующем выводе. При наличии в микроконтроллере модуля ССР2 он может использоваться аналогичным образом, только сигнал в этом случае будет формироваться на выходе ССР2. Любой из выводов, используемый для формирования ШИМ-сигнала, должен быть сконфигурирован как выход сбросом соответствующего бита регистра TRIS. Несмотря на то что оба модуля ССР могут работать параллельно с различными значениями коэффициента заполнения, период формируемых этими модулями ШИМ-сигналов будет одинаковым, поскольку они используют один и тот же Таймер 2.

Рис 13.10. Таймер 2 и модуль ССР в режиме ШИМ

Период

Временная развертка осуществляется Таймером 2, как было показано на Рис. 13.8. Величина периода переполнения зависит от длительности машинного цикла 4 х tOSC, коэффициента деления предделителя и содержимого регистра периода PR2. Учитывая, что Таймер 2 сбрасывается по следующему тактовому сигналу после достижения равенства с PR2, суммарный период повторения вычисляется следующим образом:

(4 x tOSC) х Коэфф. деления предделителя х (PR2 + 1).

Например, при 16-МГц резонаторе, коэффициенте деления 16 и значении h’63’ = d’99’ в регистре PR2 получим

Период = (4 х 1/16) х 16 х (99 + 1) = 400 мкс

При каждом переходе Таймера 2 через значение, записанное в регистре периода, происходит три события:

1. Таймер 2 сбрасывается в 0 (если PR2 не равен 0).

2. Защелка модуля ССР устанавливается, и на выводе ССР1 появляется ВЫСОКИЙ уровень.

3. 10-битное значение, представляющее собой длительность рабочего импульса в следующем периоде ШИМ-сигнала, копируется из ведущего регистра в ведомый.

Скважность

Значение, подаваемое на 10-битный компаратор ШИМ, хранится в двухуровневом 10-битном регистре. Значение, загружаемое программой, хранится в регистре CCPR1L (8 старших битов) и в битах CCP1C0N[5:4] (два младших бита) — на Рис. 13.10 все эти биты вместе названы ведущим регистром. Содержимое ведущего регистра можно изменить в любой момент времени, выполнив две команды movwf. Это значение продвигается по конвейеру и поступает на компаратор только в конце каждого периода. Такое решение уменьшает вероятность появления выбросов в середине периода из-за произвольного характера изменений содержимого ведущего регистра относительно состояния Таймера 2. Роль ведомого регистра выполняет регистр CCPR1H совместно с 2-битной внутренней защелкой. При работе в режиме ШИМ регистр CCPR1H доступен только для чтения. Это сделано специально, чтобы исключить прямой доступ к значению, определяющему скважность сигнала.

Счетный регистр Таймера 2 является 8-битным. Для увеличения его разрядности до 10 бит в соответствии с разрядностью значения, задающего скважность сигнала, добавляется два младших бита. Эти биты берутся либо от счетчика предделителя, который используется для снижения частоты системного тактового сигнала перед подачей его на счетный регистр таймера, либо, если коэффициент деления предделителя равен единице, от 2-битного счетчика, формирующего внутренние тактовые сигналы (см. Рис. 4.4 на стр. 92). В том и другом случае максимальное разрешение длительности рабочего импульса получается равным 10 бит (1:1024) при частоте счета, в 4 раза превышающей частоту тактирования 8-битного счетного регистра Таймера 2.

При равенстве 10-битного значения счетчика числу, определяющему длительность импульса (скважность), защелка ШИМ сбрасывается, и на выводе ССР1 появляется НИЗКИЙ уровень. В этом состоянии вывод удерживается до начала формирования следующего периода сигнала в момент переполнения Таймера 2, после чего описанный цикл повторяется. Во всех случаях число в регистре CCPRL1 должно быть меньше, чем в регистре PR2, ведь в противном случае защелка ШИМ никогда не сбросится! Если в регистре PR2 находится число h’FF’, то разрешение системы максимально и равно 10 бит. Меньшие значения в регистре периода приводят к уменьшению разрешающей способности сигнала. Например, если PR2 = h’3F’, то разрешающая способность сигнала будет равна 8 бит (шесть битов счетного регистра Таймера 2 и два дополнительных).

В качестве примера предположим, что нам необходимо формировать сигнал с периодом 400 мкс (частота 2.5 кГц) при частоте системного резонатора, равной 16 МГц. При этом коэффициент деления предделителя Таймера 2 равен 16, а в регистре PR2 находится число h’63’. Для получения сигнала с коэффициентом заполнения, равным 25 % (как на Рис. 13.9, а), можно написать следующий инициализационный код:

bsf STATUS,RP0; Переключаемся в 1-й банк

movlw h’63’; Загружаем в регистр периода d’99’

movwf PR2

bcf TRISC,2; Переключаем ССР1 на выход

bcf STATUS,RP0; Возвращаемся в 0-й банк

movlw h’19’; Устанавливаем ведущий регистр на 1/4 от полной шкалы (h’63/4’)

movwf CCPR1L; То есть b’00011001’

movlw b’00001100’; Модуль ССР1 в режим ШИМ (1100)

movwf CCP1CON; с CCP1CON[5:4] (00)

movlw b’00000110’; Предделитель Таймера 2–1:16 (10)

movwf T2CON; Включаем Таймер 2 (1). Начинаем генерацию сигнала

Постделитель Таймера 2 при формировании ШИМ-сигнала не используется, однако влияет на установку флага TMR2IF, как и обычно. Флаг CCP1IF в данном режиме не изменяется.

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

Примеры

Пример 13.1

Покажите, как можно использовать Таймер 0 для формирования на выходе RA0 ШИМ-представления байта, находящегося в регистре DATUM. Полагая, что частота резонатора равна 8 МГц, рассчитайте период ШИМ-сигнала.

Решение

Время наступления переполнения Таймера 0 будет зависеть от значения, загруженного в счетный регистр таймера в начале периода. Если мы загрузим в этот регистр дополнительный код значения (отрицательное число), то длительность периода будет пропорциональна этому числу — чем оно больше, тем больше времени пройдет до переполнения таймера. И, наоборот, при загрузке в счетный регистр таймера самого числа DATUM период переполнения таймера будет обратно пропорционален этому значению. Загружая поочередно в счетный регистр таймера обратное значение DATUM (и выставляя при этом на выход ВЫСОКИЙ уровень) и собственно значение DATUM (выставляя НИЗКИЙ уровень), мы получим сигнал, период которого будет приблизительно равен периоду переполнения Таймера 0 при его нормальной работе (256 тактов).

В Программе 13.5 Таймер 0 конфигурируется для работы на частоте 2 МГц (fOSC/4) без использования предделителя. Таким образом, итоговая частота ШИМ-сигнала составит 2/256 МГц = 7.8125 кГц. В обработчике прерывания, которое генерируется при переполнении Таймера 0, проверяется состояние бита PORTA[0], и если он сброшен, то его состояние изменяется и вычисляется дополнительный код заданного значения (инвертирование плюс единица). Однако из-за наличия синхронизатора между записью в счетный регистр Таймера 0 и реальным его изменением проходит 2 такта, поэтому для компенсации этой задержки дополнительно прибавляется двойка. Если же в бите порта уже присутствует единица, то он сбрасывается, а в счетный регистр таймера заносится исходное значение, увеличенное на 2.

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

Программа 13.5. Широтно-импульсная модуляция с использованием Таймера 0

MAIN bsf STATUS,RP0; Переключаемся в 1-й банк

         movlw b’00001000’; Внутренний такт, сигнал, предделитель выкл.

         movwf OPTION_REG

         bcf TRISA,0; RA0 — выход

         bcf STATUS,RP0; Возвращаемся в 0-й банк

         bsf INTCON,T0IE; Разрешаем прерывание от Таймера 0

         bsf INTCON,GIE; Разрешаем все прерывания

; <<<<Остальной код фоновой программы>>>>

; *************

; * Обработчик прерывания формирует ШИМ-сигнал на выводе RA0 *

; * Значение периода в DATUM. PORTA[0] — текущее состояние ШИМ *

; *************

; Сначала сохраним контекст -

ISR movwf _work; Сохраняем W

      swapf STATUS,w; и регистр STATUS

      movwf _status

; *************

; Основной код

      btfss INTCON,T0IF; Было переполнение Таймера 0?

        goto ISR_EXIT; ЕСЛИ нет, TO ложная тревога

      bcf INTCON,T0IF; Сбрасываем флаг прерывания

      movf DATUM,w; Берем значение

      btfsc PORTA,0; Сейчас на выходе НИЗКИЙ уровень?

        goto MAKE_L0; ЕСЛИ нет, ТО выставляем НИЗКИЙ

МАКЕ_Н1 bsf PORTA,0; ИНАЧЕ выставляем ВЫСОКИЙ уровень,

      xorlw b’11111111’; вычисляем дополнительный код

      addlw 1; (инвертируем и прибавляем 1)

      goto SET_UP; и загружаем значение в Таймер 0

MAKE_L0 bcf PORTA,0; Выставляем на вывод НИЗКИЙ уровень

SET_UP addlw 2; Компенсация задержки синхронизатора

            movwf TMR0; Инициализирум счетный регистр таймера

; *************

ISR_EXIT swapf _status,w; Восстанавливаем регистр STATUS

              movwf STATUS

              swapf _work,f; Восстанавливаем регистр W,

              swapf _work,w; не затрагивая регистра STATUS,

              retfie; и выходим из прерывания

Пример 13.2

Некий тахометр предназначен для регистрации скорости вращения двигателя в диапазоне 0…120 000 об/мин. При каждом обороте вала двигателя генерируется один импульс. Для подсчета числа этих импульсов в секунду и вычисления соответствующего значения в об/мин предполагается использовать микроконтроллер PIC16F877. Используя два или три имеющихся в этой модели таймера, можете ли вы разработать схему подключения микроконтроллера и написать соответствующую программу для решения данной задачи?

Решение

Скорость в 12 000 об/мин соответствует 200 оборотам в секунду. Таким образом, в качестве счетчика импульсов мы можем использовать Таймер 0, тактируемый непосредственно с вывода T0CKI, без предделителя.

Таймер 1 совместно с модулем ССР1, работающим в режиме сравнения, будет использоваться для формирования секундного интервала. Этот таймер тактируется от собственного генератора с часовым кварцем, а его состояние изменяется от h’0000’ до h’7FFF’. Однако для облегчения перевода единиц (об/мин = 60 х об/с) предлагается уменьшить интервал счета в 60/64 раз, чтобы реализовать эквивалентное соотношение — ([об/с] х 60/64) х 64. Это можно сделать, уменьшив модуль счета до h’7FFF’ х 60/64 = h’77FF’. Итоговое умножение на 64 можно выполнить либо сдвигом результата на шесть разрядов влево (<<6), либо, что более эффективно, копированием полученного значения в об/с в старший байт результата в об/мин и сдвигом его на два разряда вправо, т. е.

[об/мин] = ([об/с] х 256) >> 2.

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

Возможный вариант программы, реализующей описанный алгоритм, приведен в Программе 13.6. В секции инициализации выполняются следующие операции:

• Таймер 0 переключается в режим счета по спадающему фронту сигнала на входе T0CKI.

• Модуль ССР1 переключается в режим сравнения 1011 для сброса Таймера 1 по событию «совпадение».

• Разрешается прерывание по этому событию.

• В регистры CCPR1H: L заносится значение, соответствующее интервалу 60/64 с.

В процедуре обработки прерывания число оборотов в секунду, считанное из Таймера 0 и расширенное до двухбайтного значения, сохраняется во временных регистрах. После этого Таймер 0 обнуляется, а сохраненное значение преобразуется к об/мин, как было описано выше. После двукратного сдвига вправо два старших бита регистра RPM сбрасываются, чтобы исключить воздействие флага переноса. Итоговое 14-битное число в регистрах RPM: RPM+1 является искомым результатом, который впоследствии может использоваться в фоновой программе для выдачи на дисплей или, быть может, для передачи в компьютер по последовательному каналу.

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

Программа 13.6. Программное обеспечение тахометра

MAIN movlw h’77’; Загружаем число h’77FF’, чтобы сформировать

        movwf CCPR1H; интервал длительностью 60/64 с

        movlw h’FF’

        movwf CCPR1L

        bsf STATUS,RP0; Переключаемся в 1-й банк

        movlw b’00111000’; Таймер 0 — внешний сигнал, спад, фронт

        movwf OPTION_REG; Без предделителя

        bsf PIE1,CCP1IE; Разрешаем прерывание от ССР1

        movlw b’00000110’; Все выводы порта А — цифровые

        movwf ADCON1

        bcf STATUS,RP0; Возвращаемся в 0-й банк

        movlw b’00001011’; Модуль ССР в режиме сравнения (1011)

        movwf CCP1CON; сбрасывает Таймер 1

        movlw b’00001011’; Таймер 1 — предделитель 1:1, собств. генератор,

        movwf T1CON; синхронный режим

        clrf NEW; Сбрасываем флаг

        bsf INTCON,PEIE; Разрешаем прерывания от Таймера/ССР

        bsf INTCON,GIE; Разрешаем все прерывания

        clrf TMR0; Обнуляем счетчик импульсов

        clrf TMR1H

        clrf TMR1L

; <<<< Остальной код фоновой программы >>>>

; **********************

; Сначала сохраним контекст

ISR movwf _work; Сохраняем W

     swapf STATUS,w; и регистр STATUS

     movwf _status

; ***********************

; Основной код

     btfss PIR1,CCP1IF; Сброс Таймера 1 от ССР1?

        goto ISR_EXIT; ЕСЛИ нет, ТО ложная тревога

     incf NEW,f; Индицируем наличие нового значения

     movf TMR0,w; Берем подсчитанное число импульсов

     clrf TMR0; Обнуляем счетчик

     movwf RPM; Сохраняем результат во временном регистре

; Теперь умножим на 64

     clrf RPM+1; Обнуляем младший байт

     rrf RPM,f; об/м — старший бит, т. е. х256

     rrf RPM+1,f; >>2 для преобразования об/с в об/мин

     rrf RPM,f

     rrf RPM+1,f

     bcf RPM,7; Сбрасываем два старших бита

     bcf RPM,6

     bcf PIR1,CCP1IF; Сбрасываем флаг прерывания

; ****************************

ISR_EXIT swapf _status,w; Восстанавливаем регистр STATUS

              movwf STATUS

              swapf _work,f; Восстанавливаем регистр W,

              swapf _work,w; не затрагивая регистра STATUS,

              retfie ; и выходим из прерывания

Пример 13.3

Необходимо с помощью микроконтроллера PIC16F877 измерить длительность некоторого события. Этим событием является ВЫСОКИЙ уровень сигнала, как показано на Рис. 13.11. Предполагается, что частота системного резонатора равна 8 МГц, а длительность измеряемого импульса не превышает 100 мс.

Рис 13.11. Длительность импульса в качестве длительности события

Решение

Один из возможных вариантов решения этой задачи заключается в одновременной подаче отслеживаемого сигнала на выводы ССР1 и ССР2. Используя один из модулей для захвата нарастающего фронта, а другой — спадающего фронта, можно будет вычислить интервал между событиями, равный разности между двумя сохраненными значениями. В Программе 13.7 по нарастающему фронту импульса Таймер 1 обнуляется, соответственно состояние Таймера 1, захваченное по спадающему фронту, представляет собой искомую длительность. Если таймер будет работать от системного тактового сигнала с коэффициентом деления предделителя, равным 4, то инкрементирование счетного регистра будет происходить с частотой 500 кГц, т. е. временное разрешение составит 2 мкс. Максимальная длительность, которая может быть измерена при такой конфигурации, равна 216 х 2 мкс = 131.077 мс. Этого достаточно для работы с нашим сигналом, длительность которого не превышает 100 мс.

Обработчик прерывания, код которого приведен в Программе 13.7, просто проверяет по очереди флаги прерывания от каждого модуля ССР и выполняет соответствующие блоки программы. Если установлен флаг прерывания от модуля ССР1 (обнаружен нарастающий фронт  сигнала), то Таймер 1 обнуляется для запуска нового счета. Инкрементирование этого таймера осуществляется с частотой 500 кГц и при появлении спадающего фронта  сигнала его состояние считывается модулем ССР2 и помещается в 16-битный регистр CCPR2H: L. Затем в обработчике прерывания это значение, представляющее длительность импульса в 2-мкс тиках, копируется в два пользовательских регистра — ТIМЕ: ТIМЕ+1.

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

Программа 13.7. Измерение длительности импульса

MAIN movlw b100000101’; Модуль ССР1 — захват по нарастающему фронту

         movwf CCP1CON

         movlw b’00000100’; Модуль ССР1 — захват по спадающему фронту

         movwf CCP2CON

         bsf STATUS,RP0; Переключаемся в 1-й банк

         bsf PIE1,CCP1IE; Разрешаем прерывание от ССР1

         bsf PIE2,CCP2IE; Разрешаем прерывание от ССР2

         bcf STATUS,RP0; Возвращаемся в 0-й банк

         movlw b’00100001’; Таймер 1 включен (1), внутренний генератор (0!

         movwf T1CON; Синхронный режим (0), предделитель 2:1 (10)

         clrf NEW; Сбрасываем признак нового значения

         bsf INTCON,PEIE; Разрешаем прерывания от Таймера/ССР

         bsf INTCON,GIE; Разрешаем работу системы прерываний

<<<< Остальной код фоновой программы >>>>

; **************

; Сначала сохраним контекст

ISR movwf _work; Сохраняем W

      swapf STATUS,w; и регистр STATUS

      movwf status

; **************

; Основной код

      btfsc PIR1,CCP1IF;Прерывание от CCP1 (нараст. фронт)?

         goto CAPTURE1;ЕСЛИ да, ТО обработаем его!

      btfss PIR2,CCP2IF;Прерывание от ССР2 (спад, фронт)?

         goto ISR_EXIT

CAPTURE2

      movf CCPR2L,w; Берем младший байт захваченного значения

      movwf TIME+1; и сохраняем его

      movf CCPR2H,w; Берем старший байт захваченного значения

      movwf TIME; и сохраняем его

      bcf PIR2,CCP2IF; Сбрасываем флаг прерывания

      incf NEW,f; Сообщаем фоновой программе о наличии нового значения

        goto ISR_EXIT

CAPTURE1

     clrf TMR1L; Обнуляем счетный регистр таймера

     clrf TMR1H

     bcf PIR1,CCP1IF; Сбрасываем флаг прерывания

; ****************

ISR_EXIT swapf _status,w; Восстанавливаем регистр STATUS

              movwf STATUS

              swapf _work, f ; Восстанавливаем регистр W,

              swapf _work,w; не затрагивая регистра STATUS,

              retfie ; и выходим из прерывания

Вопросы для самопроверки

13.1. Используя Таймер 1 совместно с модулем ССР1, напишите программу, формирующую на выходе ССР1 меандр с периодом 20 мс. Частоту кварцевого резонатора примите равной 8 МГц. Подсказка: помните, что состояние выхода модуля ССР изменяется только при событии «совпадение», поэтому режим сравнения потребуется переключать «налету» каждые 10 мс.

13.2. В схеме ультразвукового дальномера, приведенной на Рис. 7.9 (стр. 236), используется внешний генератор частотой 17.2 кГц, который прерывает работу микроконтроллера каждые 58 мкс, т. е. с периодом, соответствующим времени прохождения звуковой волной расстояния в один сантиметр в воздухе. Полагая, что микроконтроллер работает на частоте 20 МГц, покажите, как можно использовать Таймер 2 для генерации прерывания с такой периодичностью и точностью, составляющей более 0.1 %.

13.3. Микроконтроллеры PIC среднего уровня имеют только один вход внешнего прерывания, INT. Предложите вариант использования Таймера 0 для симуляции дополнительного внешнего прерывания на выводе T0CKI.

13.4. При программной реализации асинхронного канала последовательной передачи данных со скоростью 300 бод, необходимо формировать задержки длительностью 3.33 мс. Предполагая, что микроконтроллер работает на частоте 8 МГц, покажите, как можно использовать таймер для генерации прерывания с периодичностью, равной длительности битового интервала. Усовершенствуйте процедуру таким образом, чтобы она поддерживала скорости передачи до 19 200 бод (каждое последующее значение скорости получается удвоением предыдущего).

13.5. Покажите, как можно использовать Таймер 1, работающий от собственного генератора с резонатором 32.768 кГц, для реализации часов реального времени (регистры HOURS: MINUTES: SECONDS) системы центрального отопления из Примера 7.3 (стр. 231).

13.6. В Си-компиляторе CCS имеются встроенные функции для работы с таймерами и модулями ССР. Например, запись в счетный регистр Таймера 1 можно осуществить вызовом функции set_timer1 (<значение>). Для считывания состояния таймера предназначена функция get_timer1 () >). Функция setup_timer1 (<режим>) используется для инициализации таймера. Аналогично, функция setup_ccp1 (<режим>) предназначена для инициализации регистра CCP1CON. При задании конфигураций Таймера 1 и модуля ССР1 используются следующие константы:

Значение, передаваемое в подпрограмму, получается объединением указанных констант с помощью оператора ИЛИ «|».

Покажите, как можно переписать ответ на Вопрос для самопроверки 13.5 с использованием языка Си. В компиляторе CCS функцию можно объявить в качестве обработчика прерывания от модуля ССР1, поставив перед ней директиву #int_ccp1 (см. Программу 9.6 на стр. 293 для дополнительной информации). При этом в вашем распоряжении имеется зарезервированная переменная ССР_1, представляющая содержимое 16-битного регистра CCPR1H: L.

13.7. Широтно-импульсная модуляция может использоваться для управления скоростью вращения электродвигателя постоянного тока за счет изменения среднего тока, протекающего по его обмотке. Однако запуск такого электродвигателя представляет известную проблему, поскольку ток обмотки при пуске в несколько раз превышает ток, протекающий в установившемся режиме. Для предотвращения выхода из строя силового управляющего транзистора предлагается постепенно увеличивать скважность ШИМ-сигнала с О до максимального значения в течение нескольких секунд. Покажите, как это можно осуществить с помощью микроконтроллера PIC, работающего на частоте 4 МГц, и его модуля ССР.

13.8. Дорожные светофоры на регулируемых пешеходных переходах в Англии при нажатии на любую из кнопок разрешения перехода работают по следующему алгоритму:

1. Зеленый свет (нормальный режим).

2. Оранжевый свет в течение 3 с.

3. Красный свет, сопровождающийся звуковым сигналом в течение 15 с.

4. Мигающий оранжевый свет — пять вспышек длительностью по 3 с с трехсекундными паузами между вспышками.

5. Возврат в нормальный режим.

Используя подходящий микроконтроллер PIC с модулем Таймера 1, напишите программу, управляющую сигналами светофора и звуковым излучателем. Хотя световые сигналы расположены по обе стороны дороги, можете считать, что они соединены параллельно и включаются ВЫСОКИМ уровнем на соответствующем выводе порта. Управляющие кнопки CROSS_REQUEST0 и CROSS_REQUEST1 при нажатии формируют лог. О на входе микроконтроллера. Звуковой излучатель включается НИЗКИМ уровнем на соответствующем выводе порта микроконтроллера.

Более 800 000 книг и аудиокниг! 📚

Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением

ПОЛУЧИТЬ ПОДАРОК