Глава 7 Обработка прерываний

Подпрограммы, которые мы с вами обсуждали в главе 6, можно назвать «предсказуемыми» событиями, поскольку они вызываются в соответствии с логикой программы. Ситуации же реального времени, наступающие в результате взаимодействия процессора с внешними физическими воздействиями, далеко не так просты. Очень часто вне ядра ЦПУ происходят различные события, требующие немедленной реакции процессора. Подавляющее большинство контроллеров способны реагировать на самые разнообразные события такого рода, нарушающие их нормальное функционирование. Что же касается микроконтроллеров, то запросы на обслуживание могут исходить как от встроенных периферийных устройств, например при переполнении таймера, так и извне от источника, совершенно не связанного с микроконтроллером. По меньшей мере, по сигналу внешнего сброса (одно из внешних событий) микроконтроллер должен перейти к первой команде программы. Аналогичным образом, в качестве реакции на внешний запрос на обслуживание, или прерывание, микроконтроллер должен перейти к специальной подпрограмме, называемой подпрограммой обработки прерывания.

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

Прочитав эту главу, вы:

• Осознаете необходимость обработки прерываний.

• Разберетесь в концепции таблицы векторов как отправной точки при реакции на события сброса и прерываний.

• Ознакомитесь с последовательностью событий, происходящих при обнаружении PIC-микроконтроллером запроса на прерывание.

• Поймете причины возникновения задержки.

• Разберетесь в назначении бита глобального разрешения прерываний.

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

• Сможете писать простые обработчики прерываний, реализующие:

— Переключение контекста.

— Определение источника прерывания.

— Возврат по команде rеtfiе.

Простой пример, демонстрирующий необходимость быстрой реакции на событие, показан на Рис. 7.1. В данном случае нам необходимо измерить время между точками R сигнала электрокардиограммы (ЭКГ), который, по определению, является внешним событием реального времени. Временное разрешение должно быть не менее 0.1 мс, а наибольший интервал между максимальными значениями сигнала скорее всего не превысит 1.5 с. Для измерения этого интервала с заданными параметрами можно было бы использовать независимый 16-битный счетчик, работающий на частоте 10 кГц. Как мы увидим в главе 13, все микроконтроллеры среднего уровня имеют 8-битный счетчик, счетный регистр которого расположен по адресу h’01’. На Рис. 7.1 показано, как можно с помощью регистра h’3F’ организовать 16-битный счетчик. Этой конфигурации соответствует Программа 13.2, приведенная на стр. 462. Пока же предположим, что состояние счетчика можно считать из двух указанных регистров в любой момент времени. Если состояние счетчика, соответствующее последней точке R, было сохранено в двух временных регистрах, то, вычитая состояние счетчика, соответствующее текущей точке R, получим требуемую длительность.

Рис. 7.1. Обнаружение и обработка внешнего события

Следующей задачей является обнаружение максимального уровня сигнала, поскольку сердце пациента, по определению, не синхронизировано с микроконтроллером! Один из возможных способов определения точки R заключается в непрерывном считывании этого сигнала и обработки его по алгоритму выделения максимума. В данном случае для обеспечения заданного временного разрешения применение метода последовательного опроса (polling) потребует проведения измерений 10 000 раз в секунду. Учитывая, что частота сердцебиения обычно составляет около 60 ударов в минуту, 99.99 % времени будет затрачено впустую. Более того, это означает, что бóльшая часть вычислительной мощности процессора будет затрачена на обнаружение одного события из 10 000.

В качестве альтернативы можно воспользоваться внешним устройством, задачей которого будет обнаружение максимального уровня сигнала. Это устройство может быть как полностью аналоговым, так и построенным на базе микроконтроллера с аналого-цифровым преобразователем (см. Пример 14.2 на стр. 529). Независимо от реализации, этот блок будет посылать сигнал основному процессору при обнаружении точки R. Этот сигнал используется для прерывания работы микроконтроллера, который должен приостановить выполнение текущей задачи и изменить состояние счетчика не позже чем через 100 мкс.

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

Обратной стороной мониторинга сигналов в реальном времени с использованием прерываний является усложнение аппаратных средств и аппаратно-программного интерфейса. Если вы совсем запутались, вообразите себе телефонную сеть. Можно построить такую сеть, при которой абонент снимал бы трубку, скажем, каждые 5 мин и спрашивал: «Эй! Есть тут кто-нибудь?» Не говоря уже о неудобствах (накладные расходы), связанных с выполнением этой операции[98], звонящему может просто надоесть ждать, и он повесит трубку. Разумеется, можно снизить вероятность такого события, увеличивая частоту опроса до, скажем, одного раза в минуту. Однако в этом случае вам придется проводить все свое время у телефона, принимая при этом всего несколько звонков в день. То есть 99 % ваших усилий будет затрачено впустую.

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

Микроконтроллеры могут реагировать на запросы прерывания от самых разных источников, находящихся вне микроконтроллера, либо от различных портов и периферийных устройств, имеющихся в составе конкретного представителя семейства. Например, микроконтроллеры PIC16F874/7 поддерживают до 13 различных прерываний от этих периферийных устройств, а также одно внешнее прерывание, подаваемое через вывод INT (вывод 6 на Рис. 4.1, стр. 89). Вход внешнего прерывания использует ту же ножку микроконтроллера, к которой подключена 0-я линия порта В, т. е. вывод RB0. Программист может в индивидуальном порядке запретить или разрешить прерывания от этих источников, а также полностью запретить работу всей системы прерываний. Поскольку процесс реакции на прерывание практически не зависит от его источника, в этой главе мы главным образом будем вести речь именно об этом внешнем прерывании.

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

1. Завершение исполнения текущей команды.

2. Автоматическое сохранение, по меньшей мере, состояния счетчика команд (PC) — это необходимо для возврата из обработчика прерывания. Некоторые процессоры (такие как микроконтроллеры PIC старшего семейства) могут также автоматически сохранять содержимое регистра STATUS и других внутренних регистров.

3. Переход к соответствующей процедуре обработки прерывания.

4. Выполнение требуемых действий.

5. Восстановление состояния процессора и возврат к тому месту основной программы, в котором произошло прерывание.

Короче говоря, возникновение сигнала прерывания приводит к тому, что микроконтроллер прекращает выполнение текущей задачи, сохраняет свое состояние в прерываемой фоновой программе в стеке и переходит к выполнению специальной подпрограммы, называемой процедурой обработки прерывания (Interrupt Service Routine — ISR). Эта высокоприоритетная процедура представляет собой обычную подпрограмму, которая выполняется при наступлении заданного события.

Конкретные детали отклика на запрос прерывания в некоторой степени отличаются от процессора к процессору. В микроконтроллерах семейства среднего уровня реакция на прерывание осуществляется следующим образом (см. Рис. 7.2)[99]:

Рис. 7.2. Отклик на запрос прерывания

1. При выполнении каждой команды процессор проверяет наличие запроса прерывания от разрешенного источника. Независимо от наличия такого запроса он дожидается завершения исполнения команды, т. е. выполнение команды не прерывается даже в случае команд, выполняющихся за 2 машинных цикла.

2. Если такой запрос отсутствует, микроконтроллер просто переходит к выполнению следующей команды, и описанный процесс повторяется.

3. При наличии запроса следующие три машинных цикла затрачиваются на передачу управления процедуре обработки прерывания. Из этих циклов первый является холостым[100], а во время двух оставшихся производится сброс конвейера. Эта задержка длительностью от 3 до 4 машинных циклов между подачей внешнего сигнала на вывод INT и моментом выполнения 1-й команды обработчика называется задержкой обработки прерывания (interrupt latency). Бóльшую точность получить нельзя в связи со случайной природой сигнала внешнего прерывания, который может появиться на любом этапе машинного цикла.

4. Во время этой задержки микроконтроллеры PIC выполняют следующие операции:

а) Запрещается вся система прерываний, что гарантирует блокирование всех прерываний на время обработки текущего. Это осуществляется сбросом 7-го бита регистра управления прерываниями INTCON, который на Рис. 7.3 помечен как флаг общего разрешения прерываний (GIE). Указанный бит является маской прерывания, поскольку он используется для маскирования активности прерываний. При сбросе микроконтроллера бит GIE всегда сбрасывается, так что по умолчанию прерывания запрещены.

б) Состояние 13-битного счетчика команд заносится в стек точно так же, как и при выполнении команды call (см. Рис. 6.3 на стр. 172). Как и в случае подпрограмм, эта операция позволяет процессору после выхода из процедуры обработки прерывания вернуться к выполнению прерванной фоновой программы. Поскольку в PIC-микроконтроллерах среднего уровня реализован 8-уровневый аппаратный стек, из обработчика прерывания можно вызывать до семи вложенных друг в друга подпрограмм.

в) Первая команда обработчика прерывания всегда размещается по адресу h’004’ памяти программ. Так что завершающий этап рассматриваемой последовательности состоит в занесении в PC указанного адреса, называемого вектором прерывания. Разумеется, если код обработчика прерывания находится в каком-либо другом месте памяти программ, то первой командой будет команда goto, как показано в Программе 7.1.

5. Как и все подпрограммы, процедура обработки прерывания должна завершаться командой возврата. Однако при прерывании необходимо не только извлечь из стека сохраненное значение PC, но и установить бит GIE регистра INTCON для разрешения последующих прерываний. Напоминаю, что указанный бит был сброшен на этапе 4а при переходе к обработчику прерывания. Для этого используется команда возврата из прерывания retfie (см. Табл. 6.1 на стр. 170). Таким образом, после возврата в фоновую программу можно будет обработать все отложенные или будущие прерывания.

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

Хотя большинство представителей микроконтроллеров PIC среднего уровня поддерживают прерывания от различных источников, три из этих источников во всех без исключения устройствах связаны с регистром INTCON, как показано на Рис. 7.3.

Рис. 7.3. Логика системы прерываний микроконтроллера PIC16F84

Этими основными источниками являются:

• Внешний сигнал, подаваемый на вывод INT. Это внешнее прерывание может генерироваться либо по нарастающему , либо по спадающему  фронту входного сигнала, что определяется состоянием бита INTEDG регистра OPTION_REG. Этот сигнал проходит через вентиль Исключающее ИЛИ, выполняющий в данном случае роль программируемого инвертора, как было описано на стр. 28.

• Изменение состояния любого из четырех старших выводов порта В (регистр h’06’) с момента последнего чтения из этого порта.

• Переполнение счетного регистра таймера/счетчика TMR0 (регистр h’01’) с h’FF’ до h’00’.

Формат регистра INTCON микроконтроллера PIC16F84 приведен на Рис. 7.3. С каждым из четырех источников прерываний связан соответствующий бит флага прерывания. Например, при появлении на 6-м выводе микроконтроллера отрицательного перепада сигнала  будет установлен флаг INTF (бит 1). Это произойдет независимо от того, разрешена работа системы прерываний или нет.

В тех случаях, когда разрешены прерывания более чем от одного источника, состояние этих флагов можно контролировать программно для определения конкретного источника; см. листинг на стр. 220. Вы можете опрашивать эти биты даже при выключенной системе прерываний. Несмотря на то что флаг устанавливается внешним (по отношению к ЦПУ) событием, сбрасываться он должен программно. При обработке прерывания жизненно важно сбрасывать этот флаг в процедуре обработки прерывания перед возвратом из обработчика, т. е. следует выполнить команду bcf INTCON, INTF.

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

На самом деле каждый из флагов прерывания логически умножается (AND) на соответствующий бит маски прерывания. На Рис. 7.3 2-й элемент И показывает этот механизм для внешнего прерывания. Например, если требуется разрешить прерывания как от вывода INT, так и от Таймера 0, то придется установить биты 7, 5 и 4, т. е. выполнить команды

movlw b’10110000’

movwf INTCON

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

Что же касается именно PIC16F84, то в этой модели прерывание может генерироваться также при завершении цикла записи во внутреннюю EEPROM-память данных. В регистре INTCON для флага EEIF не хватило места, поэтому этот флаг разместили в 4-м бите регистра управления EEPROM — регистре EECON1. Чуть позже в этой главе (Рис. 7.5) мы с вами увидим, каким образом осуществляется поддержка дополнительных прерываний в более развитых представителях семейства.

Поскольку имеется четыре источника прерываний, выходы всех элементов И, на вход каждого из которых подаются сигналы флага и маски, необходимо объединить по ИЛИ для получения итогового сигнала запроса прерывания, который, собственно, и инициирует реакцию ЦПУ на прерывание. Из Рис. 7.3 видно, что сигнал с выхода этого элемента ИЛИ управляется (с помощью еще одного элемента И) битом общего разрешения прерываний GIE, который расположен в 7-м бите регистра INTCON. Однако для «пробуждения» процессора, если он находится в режиме пониженного потребления, используется именно исходный сигнал с выхода элемента ИЛИ. Как мы увидим далее в главе 10, при останове программы и переводе микроконтроллера в режим пониженного потребления можно значительно снизить ток, потребляемый устройством (до значения не более 1 мкА). Например, при контроле температуры на дне озера в течение года с интервалом в 1 час с помощью регистратора данных с батарейным питанием собственно вычисления занимают ничтожную долю от общего времени работы. Перевод микроконтроллера в режим Power Down после считывания и сохранения каждого отсчета уменьшает емкость батареи, необходимую для обеспечения работы прибора в течение столь длительного времени. Перевод в этот режим осуществляется командой sleep. Для «пробуждения» микроконтроллера используется прерывание от внешнего источника, в данном случае от экономичного генератора с периодом сигнала, равным одному часу. Как говорилось выше, процесс вывода микроконтроллера из «спящего» режима не зависит от состояния бита GIE.

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

Рис. 7.4. Контроль числа посетителей магазина

Программа 7.1. Подсчет числа посетителей

STATUS equ 3; Регистр STATUS

INTCON equ h’0B’; Регистр управления прерываниями

INTF equ 1; Флаг внешнего прерывания — бит 1

INTE equ 4; Бит маски внешнего прерывания — бит 4

GIE equ 7; Бит глобального разрешения прерываний — бит 7

_status equ h’4F’; Ячейка для сохранения регистра STATUS

EVENT equ h’20’; Счетчик общего числа посетителей

; Вектор сброса --------------

           org 000; При сбросе в PC заносится число h’000’

           goto MAIN; Переходим к началу фоновой программы

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

           org 004; При прерывании PС переходит к адресу h’004’

           goto PERS_COUNT; Переходим к обработчику прерывания

; Фоновая программа начинается с инициализации

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

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

           clrf EVENT; Обнуляем счетчик посетителей

; Бесконечный цикл основной программы —

M_LOOP; Выполняем то

            ; Выполняем это

            ; Выполняем еще что-нибудь

           goto M_LOOP

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

; * ФУНКЦИЯ: Обработчик инкрементирует счетчик EVENT *

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

PERS_COUNT

          movwf _work; Сохраняем W в памяти данных

          swapf STATUS,w; Считываем регистр STATUS, не меняя флагов

          movwf _status; Сохраняем его в памяти данных

; -----------------

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

          incf EVENT,f; Регистрируем событие

; -----------------

          swapf _status,w; Восстанавливаем исходное состояние

          movwf STATUS; регистра STATUS

          swapf _work,f; Теперь восстанавливаем исходное состояние W,

          swapf _work,w; не воздействуя на флаги

          retfie ; и возвращаемся в фоновую программу

В самом начале Программы 7.1 указано два вектора. По адресу h’000’, который является вектором сброса, размещена команда goto MAIN. Аналогично, по адресу h’004’ размещена команда перехода к обработчику прерывания goto PERS_COUNT. Размещение команд по конкретным адресам осуществляется с помощью директивы org (см. стр. 244). Таким образом, при реагировании микроконтроллера на прерывание мы получаем следующую последовательность: прерывание —> h’004’ —> PERS_COUNT.

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

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

Повреждение содержимого регистра STATUS в таких случаях может иметь гораздо более серьезные последствия, например, в цикле опроса (см. листинг на стр. 219). В данном случае при входе в обработчик прерывания был установлен бит RP0 регистра STATUS (см. Рис. 4.7 на стр. 97) для обращения к регистрам, расположенным в 1-м банке памяти. Эта операция была необходима, поскольку регистры управления EEPROM имеются только в данном банке, тогда как РСН STATUS и INTCON отражены на оба банка. При выходе из подпрограммы бит RP0 сбрасывается для возврата к 0-му банку памяти, т. е. предполагается, что в момент возникновения прерывания фоновая программа работала с 0-м банком. Очевидно, что если прерывание возникнет во время работы с банком 1, то дальнейшая программа будет работать неправильно.

Отсюда становится ясно, что независимо от сложности обработчика прерывания нам необходимо сохранить, по меньшей мере, содержимое рабочего регистра и регистра STATUS. Для работы в качестве временного хранилища резервируют несколько регистров данных, которые больше ни для чего не используются. Обычно названия этих переменных начинаются с символа подчеркивания, показывающего, что эти регистры используются для системных нужд и не должны использоваться прикладной программой. В соответствии с данным соглашением в Программе 7.1 регистр h’4E’ обозначен как _work, а регистр h’4F’ — как _status.

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

Сохранение контекста

Сначала копия рабочего регистра сохраняется в регистре _work. Напоминаю, что команда movwf не влияет на биты регистра STATUS. Затем регистр STATUS сохраняется в регистре данных _status (h’4F’). Казалось бы, что может быть проще — скопировать регистр STATUS в W, а затем сохранить рабочий регистр в регистре _status. Однако команда movf изменяет состояние флага Z. Поэтому для копирования данных в рабочий регистр мы вместо команды movf воспользуемся командой swapf. Команда swapf не изменяет состояние флагов, однако переставляет местами старший и младший полубайты. Но мы можем восстановить их нормальное положение при восстановлении регистра.

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

Основной код

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

В рабочей секции основного кода просто инкрементируется содержимое регистра EVENT. Естественно, это основная задача процедуры обработки прерывания.

Восстановление контекста

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

Исходное значение W восстанавливается из временной переменной _work с использованием двух последовательных команд swapf. При этом состояние регистра STATUS не изменяется.

И наконец, выполняется команда возврата retfie, которая также не влияет на состояние флагов регистра STATUS.

Хотя в нашем примере мы сохраняли только рабочий регистр и регистр STATUS[101], в других случаях может потребоваться сохранение и других РСН. Так, в Примере 7.3 сохраняется регистр FSR, поскольку он используется как в основной программе, так и в обработчике прерывания. Одним словом, если в процедуре обработки прерывания изменяются какие-либо РСН, то при выходе из обработчика должно быть восстановлено их исходное состояние. В любом случае первым необходимо сохранить содержимое рабочего регистра, поскольку он будет использоваться в качестве промежуточного хранилища при сохранении остальных регистров. Соответственно восстанавливаться рабочий регистр должен в самую последнюю очередь.

По мере возможности регистры, в которых сохраняется контекст программы, следует выбирать таким образом, чтобы они не зависели от банка памяти, используемого процессором в момент-прерывания. В микроконтроллере PIC16F84 все РОН отображены на оба банка, поэтому можно выбирать любые. Тем не менее это не слишком типичная ситуация, особенно если в модели используется большее число банков для поддержки большого количества уникальных РСН. В более новых моделях часто предусмотрена небольшая область памяти, отображенная на все банки, например, старшие 16 байт в модели PIC16F627/8, как показано на Рис. 5.4 (стр. 121). Более старые модели, такие как PIC16C74, вообще не имеют общих РОН. В этих случаях программист должен либо гарантировать, что прерывания не возникнут в те моменты, когда процессор работает с банком, отличным от используемого для сохранения, либо проверять состояние битов RP при входе в обработчик прерывания и переключаться на системный банк (обычно банк 0) перед сохранением регистра STATUS. Затем состояние битов RP в _status изменяется таким образом, чтобы оно соответствовало их исходному значению.

В нашем, намеренно упрощенном примере предполагается, что разрешено обслуживание только внешнего прерывания. В большинстве же случаев могут быть разрешены прерывания от нескольких источников. А поскольку в микроконтроллерах PIC имеется только один вектор прерывания (h’004’), то одной из первых задач обработчика будет проверка, какое из периферийных устройств вызвало прерывание. Все флаги прерываний доступны для чтения, поэтому их можно поочередно проверять, пока не найдется флаг, который установлен. В самом сложном случае, когда активны все четыре источника прерываний микроконтроллера PIC16F84, и учитывая, что регистр EECON1 находится в 1-м банке, код для проверки флагов будет выглядеть следующим образом:

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

btfsc INTCON,1; Проверяем флаг внешнего прерывания

   goto EXTERNAL; ЕСЛИ установлен, переходим к соотв. обработчику

btfsc INTCON,2; Проверяем флаг прерывания от Таймера 0

   goto TIMER0; ЕСЛИ установлен, переходим к соотв. обработчику

btfsc INTCON,0; Проверяем флаг прерывания по изменению порта В

   goto CHANGE_B; ЕСЛИ установлен, переходим к соотв. обработчику

bcfsc EECON1,4; Проверяем флаг прерывания от EEPROM

   goto EEPROM_WR; ЕСЛИ установлен, переходим к соотв. обработчику

... ...

IRQ_EXIT

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

    retfie ; и выходим из обработчика

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

При сброшенном бите маски аналогичная методика опроса может применяться для контроля событий без использования прерываний. Например, при записи байта в EEPROM (см. Программу 15.2 на стр. 547) программа обычно ожидает установки флага EEIF (4-й бит регистра EECON1), после чего сбрасывает его и продолжает выполнение.

W_LOOP

        btfss EECON1,EEIF; Проверяем состояние флага EEIF

          goto W_LOOP; ЕСЛИ сброшен, проверяем снова

; ИНАЧЕ продолжаем выполнение программы после сброса флага EEIF

        bcf EECON1,EEIF

Вообще говоря, во всех системах, управляемых прерываниями, необходимо предпринимать некоторые меры предосторожности в случае обработки прерываний от нескольких источников. Для примера рассмотрим некоторую систему, получающую запрос прерывания от таймера, скажем, 1000 раз в секунду, а также запрос внешнего прерывания с вывода INT с нерегулярной частотой. Если обработчик внешнего прерывания выполняется, например, за 4 мс, то к моменту выхода из него будут потеряны три запроса прерывания от таймера! В некоторых процессорах[102] имеется схема назначения приоритетов прерываний, благодаря которой запросы с более высоким приоритетом (в данном случае прерывание от таймера) могут прерывать процессы с более низким приоритетом (обработчик внешнего прерывания). В нашем же случае единственным вариантом будет введение ограничения времени выполнения обработчика внешнего прерывания — не более 1 мс[103]. При наличии прерываний от нескольких источников необходимо рассчитать время выполнения (включая задержки) и частоту возникновения прерываний для наихудшего случая. Поскольку некоторые из этих параметров связаны с внешними событиями, не контролируемыми процессором, это может оказаться достаточно нетривиальной задачей.

Другая часто возникающая проблема связана с обработкой таких событий, при которых многобайтные значения контролируются и изменяются как в фоновой программе, так и в обработчике прерывания. Возьмем, к примеру, часы реального времени (RTC), в которых обновляется четыре регистра (HOURS, MINUTES, SECONDS и JIFFY), содержащих время в формате «часы: минуты: секунды: десятые доли секунд» (см. Пример 7.3). Предположим, что внешний генератор с частотой 10 Гц прерывает работу микроконтроллера 10 раз в секунду и обработчик прерывания обновляет указанные регистры.

Теперь предположим, что эти RTC являются составной частью системы центрального отопления. Фоновая программа должна переключать насос из включенного состояния в выключенное в 09:00:00:00. И в указанное время это произошло. Но вот наступило время 09:59:59:09 того же дня. Фоновая программа, основная задача которой состоит в отслеживании текущего времени, считывает значение часов, равное 09. После этого она собирается считывать значение минут, когда происходит прерывание от генератора. Работа фоновой программы прерывается, и состояние RTC изменяется на 10:00:00:00. При возврате в фоновую программу соответственно считываются значения 00:00:00. Считая, что сейчас 09:00:00:00, программа переключает насос, вследствие чего периоды, когда насос включен и выключен, меняются местами неограниченное число раз!

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

Итак, подведем итог. Процедуры обработки прерываний аналогичны обычным подпрограммам, однако необходимо помнить следующее:

• Обработчик прерывания должен завершаться командой retfie вместо return.

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

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

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

• Если в обработчике прерывания обрабатываются многобайтные данные, то при любом обращении фоновой программы к этим переменным необходимо запрещать прерывания (сбросом бита GIE).

Регистр INTCON, формат которого показан на Рис. 7.3, содержит только бит глобального разрешения прерываний, а также используется для управления тремя основными источниками прерываний, а именно внешним прерыванием, прерыванием по переполнению Таймера 0, а также прерыванием по изменению состояния выводов порта В. Последний оставшийся бит EEIE (INTCON[6]) в модели PIC16F84 используется для хранения бита маски прерывания модуля EEPROM. По причине нехватки места соответствующий ему бит флага прерывания EEIF размещен в другом регистре, а именно в регистре EEC0N1[4] (4-й бит). В 18-выводных моделях среднего уровня того же поколения часто используется такой подход. Например, бит маски прерывания от модуля АЦП ADIE в модели PIC16C71 располагается в INTCON [6], а соответствующий флаг прерывания ADIF находится в регистре управления АЦП ADCONO[1].

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

На Рис. 7.5 показана логика системы прерываний в моделях PIC16F627/8. Эти модели имеют помимо основных еще пять периферийных модулей, формирующих в общей сложности семь отдельных запросов на прерывание. Правая часть схемы, изображенной на рисунке, практически идентична приведенной на Рис. 7.3. Единственное отличие заключается в том, что в данном случае 6-й бит регистра INTCON называется PEIE (разрешение прерывания от периферийных устройств)[104]. Серым цветом в левой части Рис. 7.5 выделены семь дополнительных источников прерываний и логические элементы, используемые для разрешения прерываний от этих источников. Выходы указанных элементов объединены по ИЛИ для формирования одного-единственного сигнала, который, в свою очередь, управляется битом маски PEIE. Так что в этих моделях 6-й бит регистра INTCON выполняет функцию разрешения прерываний от всех дополнительных периферийных модулей.

Рис. 7.5. Логика системы прерываний микроконтроллеров PIC16F627/8

Устройства среднего уровня имеют различные наборы периферийных устройств, однако все они используют бит PEIE в качестве дополнительного бита маски для разрешения/запрещения прерываний от этих модулей. Если данный бит установлен, то любое из этих периферийных устройств может быть использовано для вывода микроконтроллера из спящего режима независимо от состояния бита глобального разрешения прерываний GIE.

На Рис. 7.6 изображены два дополнительных регистра.

Рис. 7.6. Регистры системы прерываний микроконтроллеров PIC16F627/8.

ЕЕ — Запись в EEPROM: СМ — Аналоговый компаратор: RC — Прием по USART: ТХ — Передача по USART: SSP — Синхронный последовательный порт: CCP1 — Захват/сравнение 1: TMR2 — Таймер 2: TMR1 — Таймер 1

Регистр флагов прерываний от периферийных устройств PIR1, расположенный в 0-м банке, который содержит семь флагов прерываний, и регистр разрешения прерываний от периферийных устройств PIE1, содержащий соответствующие биты маскирования прерываний. Для примера на рисунке также показано формирование сигнала прерывания, генерируемого при приеме символа со входа последовательного порта (см. Рис. 12.20 на стр. 419), который устанавливает флаг прерывания по приему символа RCIF, расположенный в 5-м бите регистра PIR1. Для разрешения генерации прерывания по этому событию и перехода процессора к обработчику прерывания нам необходимо установить три бита маски.

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

bsf STATUS,RP1; обратиться к регистру PIE1

bsf PIE1,RCIE; Разрешаем прерывание по приему символа

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

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

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

Необходимо сделать несколько замечаний по поводу приведенного фрагмента программы. Во-первых, как и большинство микроконтроллеров PIC среднего уровня, PIC16F627/8 имеют четыре банка регистров, выбираемых при помощи битов RP1 и RP0 регистра STATUS (см. Рис. 5.4 на стр. 121). После сброса микроконтроллера оба бита обнуляются, т. е. используется 0-й банк памяти. Поэтому в приведенном коде мы не стали дополнительно сбрасывать бит RP1. Регистр PIE1 всегда располагается в 1-м банке, так как после конфигурирования дальнейшее его изменение, как правило, не требуется. В то же время из соображений удобства регистр PIR1 размещается в 0-м банке, поскольку он часто опрашивается и изменяется. Регистр INTCON отображен на все четыре банка.

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

Примеры

Пример 7.1

Возьмем конвейерную линию по упаковке консервированного горошка. Одним из элементов автоматического упаковщика является фотоэлемент, формирующий одиночный короткий импульс при пересечении луча банкой, аналогично схеме на Рис. 7.4. После прохода 24 банок на 0-м выводе порта A (RA0) необходимо сформировать импульс длительностью 1 мс , переключающий электронику упаковочного механизма. Предположим, что PIC 16F84 тактируется от 4-МГц резонатора.

Решение

Код программы приведен в Программе 7.2. По адресу вектора сброса (h’000’) расположена команда перехода к основной фоновой программе (MAIN), а по адресу вектора прерывания (h’004) расположена команда перехода к процедуре обработки прерывания, названной CAN_COUNT.

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

1. Сброс 0-го бита порта А, что гарантирует наличие НИЗКОГО уровня на выводе RA0 после сброса.

2. Все линии параллельных портов ввода/вывода при сбросе микроконтроллера переключаются на вход. Для переключения 0-й линии порта А на выход, необходимо сбросить соответствующий бит регистра TRISA. Поскольку этот регистр располагается в 1-м банке, необходимо переключить банки памяти, изменив бит RP1 регистра STATUS (см. стр. 99). Более подробно о работе с портами ввода/вывода можно прочитать в главе 11.

3. Сбрасываются регистры EVENT, в котором подсчитывается количество импульсов от фотодетектора, и BATCH, в который заносится ненулевое значение в обработчике прерывания после прохода 24 банок.

4. Сброс всех битов регистра INTCON сбрасывает все флаги прерываний, которые могли бы установиться с момента сброса. Это очень важно, поскольку указанные флаги могут устанавливаться независимо от состояния соответствующих битов маски. Последующая установка бита глобального разрешения прерываний разрешает работу системы прерываний, а установка бита INTE разрешает внешние прерывания с вывода INT.

Основной задачей фоновой программы является периодическая проверка значения регистра BATCH. При старте программы он равен нулю, однако обработчик прерывания записывает в него ненулевое значение после прохода группы из 24 банок. При обнаружении ненулевого значения регистр обнуляется, на выходе RA0 устанавливается ВЫСОКИЙ уровень и вызывается подпрограмма 1-мс задержки[105], названная DELAY (см. Программу 6.1 на стр. 175). После этого цикл повторяется. Вообще говоря, фоновая программа во встраиваемых системах представляет собой именно такой бесконечный цикл, однако, как правило, в нем выполняется намного больше задач, чем в этом простом примере. Например, можно управлять многоразрядным семисегментным дисплеем аналогично тому, как это показано на Рис. 11.16 (стр. 362), отображая, скажем, общее число банок с момента запуска конвейера.

Программа 7.2. Программа автоматического упаковщика

              include "pl6f84.inc"

_work equ h’4E’; Для сохранения W при входе в обработчик

_status equ h’4F’; Для сохранения STATUS при входе в обработчик

EVENT equ h’20’; Счетчик количества банок

BATCH equ h’21’; Флаг прохода 24 банок

; ---------------------

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

            goto MAIN; Переходим к началу фоновой программы

; ---------------------

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

            goto CAN_COUNT; Переходим к началу обработчика прерывания

; ---------------------

; Фоновая программа начинается с секции инициализации

MAIN bcf PORTA,0; Гарантируем наличие 0 на выводе RA0

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

         bcf TRISA,0; Переключаем вывод RA0 на выход

; Примечание. При использовании модели с модулем АЦП, например PIC16F877,

; вывод PortA[0] должен быть сконфигурирован как цифровой вход!!!

         bcf STATUS,RP0; Переключаемся обратно в 0-й банк

         clrf BATCH; Обнуляем флаг группы

         clrf EVENT; и счетчик банок

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

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

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

; ----------------------

ПОКА флаг группы равен нулю, ничего не делаем

LOOP movf BATCH,f; Проверяем BATCH == 0?

         btfsc STATUS,Z; Пропускаем, если нет

            goto M_LOOP; В противном случае проверяем снова

; 24 банки прошло, Формируем импульс —

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

         bsf PORTA,0; Выставляем на RA0 ВЫСОКИЙ уровень

         call DELAY; Ждем 1 мс

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

         goto M_LOOP;Возвращаемся к началу

; ----------------------

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

; Это процедура обработки прерывания

CAN_COUNT

         movwf _work; Сохраняем W в памяти данных

         swapf STATUS,w; Считываем текущее состояние STATUS

         movwf _status; и сохраняем его в памяти данных

; -----------------------        

         dcf INTCON,INF; Сбрасываем флаг внешнего прерывания

         incf EVENT,f; Регистрируем очередное событие

         movf EVENT,w; Читаем значение счетчика

         addlw -d’24’; Сравниваем с 24 (EVENT — 24)

         btfss STATUS,С; ЕСЛИ EVENT больше или равно, ТО пропускаем (нет заема)

            goto CAN_EXIT; ИНАЧЕ выходим

         clrf EVENT; Обнуляем счетчик банок и сообщаем

         incf BАТСН, f; в фоновую программу, что прошло 24 банки

; ---------------------

CAN_EXIT

         swapf _status,w; Восстанавливаем исходное состояние STATUS

         movwf STATUS; из памяти данных

         swapf _work,f; Теперь восстанавливаем исходное состояние

         swapf _work,w; рабочего регистра, не воздействуя на флаги,

         retfie; и возвращаемся в фоновую программу

При возникновении прерывания (при пересечении банкой луча фотодетектора) управление будет передано в процедуру обработки прерывания, т. е. произойдет следующая последовательность переходов: прерывание — > h’004’ —> CAN_COUNT. Как обычно, эта процедура состоит из трех секций.

Сохранение контекста

Рабочий регистр и регистр STATUS сохраняются в памяти программ, как это было описано на стр. 217.

Основной код

Сбрасывается флаг внешнего прерывания INTF (INTCON[l]), чтобы избежать обработки ложного прерывания при возврате в фоновую программу. При наличии нескольких источников прерывания на данном этапе должен был бы производиться опрос различных флагов прерываний (см. стр. 219).

Основная задача обработчика прерываний заключается в инкрементировании регистра EVENT. Вычитая число 24 из копии этой переменной и контролируя заем (т. е. равенство С единице), программа определяет момент, когда число EVENT становится равно или даже больше 24. При этом инкрементируется переменная BATCH для передачи в фоновую программу информации о том, что прошли очередные 24 банки, a EVENT обнуляется. То есть регистр EVENT играет роль счетчика по модулю 24.

Восстановление контекста

При возврате из обработчика прерывания восстанавливаются исходные значения регистров W и STATUS, как было описано на стр. 218. Заключительная команда retfie не изменяет состояние флагов регистра STATUS.

Пример 7.2

На фабрике по производству пищевых продуктов банки с тушеной фасолью проходят по конвейеру через туннельную печь, как показано в верхней части Рис. 7.7, где их содержимое стерилизуется. Фотодетекторы используются для подсчета банок, вошедших в печь и вышедших из нее. При пересечении луча на выходе соответствующего фотодетектора устанавливается ВЫСОКИЙ уровень.

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

Решение

С аппаратной точки зрения в данном примере имеется две задачи. Первая — как определить, какой из детекторов, входной или выходной, сформировал запрос прерывания. Из Рис. 7.7 видно, что при перекрывании луча оба фотоэлемента формируют тактовый импульс, подаваемый на тактовый вход соответствующего D-триггера. Поскольку вход триггера подтянут к лог. 1, подача тактового импульса вызывает переключение триггера в состояние лог. 1. За счет объединения выходных сигналов обоих триггеров по ИЛИ при перекрытии любого луча на выводе INT формируется нарастающий фронт сигнала.

Рис. 7.7. Система контроля печи

Состояние обоих внешних флагов IN и OUT можно считать соответственно с входов RA0 и RA1 порта А, что позволяет различать эти два события (вход банки в печь и ее выход) в обработчике прерывания. Соответствующий флаг затем можно сбросить, подав управляющий сигнал на вход сброса соответствующего триггера. Под эти сигналы задействованы еще две линии порта — RA2 и RA3 (CanceMN и Cancel_OUT соответственно).

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

1. Импульс, сформированный детектором, подается на триггер выходного датчика OUT.

2. Триггер переключается, что, в свою очередь, приводит к появлению ВЫСОКОГО уровня на выводе RA1, а также на входе INT/RB0 (через элемент ИЛИ). Последний сигнал является запросом внешнего прерывания.

3. Когда микроконтроллер передает управление на обработчик прерывания, тот проверяет состояние обоих триггеров, считывая биты RA1 и RA2 порта. В данном случае на выводе RA1 будет присутствовать ВЫСОКИЙ уровень, соответственно обработчик выдаст отрицательный импульс на вывод RA3.

4. Этот импульс сбросит триггер выходного датчика (OUT) и таким образом прекратит генерацию запроса прерывания от данного источника.

Осталась одна проблема: если событие наступит до того, как программа сбросит соответствующий внешний триггер, то это событие будет пропущено, поскольку с выхода элемента ИЛИ на вход INT будет подаваться сигнал НИЗКОГО уровня. В данной ситуации последующие фронты не будут сформированы, и система прерываний надолго окажется заблокированной! Эту ситуацию можно обойти программным способом, опрашивая оба внешних флага перед выходом из обработчика прерывания и выполняя соответствующие действия, если состояние обоих битов порта отлично от нуля.

Процедура обработки прерывания для данного случая приведена в Программе 7.3. Контекст программы сохраняется при входе в обработчик и восстанавливается при выходе из него так, как уже было описано на стр. 217.

Программа 7.3. Обработчик прерывания системы контроля печи

OVEN  movwf _work; Сохраняем W в памяти данных

           swapf STATUS,w; Считываем текущее состояние STATUS

           movf _status; и сохраняем его в памяти данных

; --------------------

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

           btfsc PORTA,0; Сигнал IN?

              goto IN; ЕСЛИ не ноль, банка только что вошла в печь

           btfsc PORTA,1;Сигнал OUT?

              goto OUT;ЕСЛИ не ноль, банка только что вышла из печи

; --------------------

; Точка выхода

           swapf _status,w; Восстанавливаем исходное состояние STATUS

           movwf STATUS; из памяти данных

           swapf _work,f; Теперь восстанавливаем исходное состояние

           swapf _work,w; рабочего регистра, не воздействуя на флаги,

           retfie ; и возвращаемся в фоновую программу

; --------------------

; Основное тело процедуры обработки прерывания

IN    incf EVENT,f; Регистрируем вхождение банки в печь

       bcf PORTA,2; Сбрасываем внешний триггер IN,

       bsf PORTA,2; формируя импульс его сброса,

         goto ALARM; и проверяем наличие аварийной ситуации

OUT decf EVENT,f; Регистрируем выход банки из печи

        bcf PORTA,3; Сбрасываем внешний триггер OUT,

        bsf PORTA,3; формируя импульс его сброса

ALARM movf EVENT,w; Берем количество банок

        addlw -5; Вычитаем 5

        btfss STATUS,С; ЕСЛИ нет заема, пищим

          goto BUZ_OFF; ИНАЧЕ все в порядке, выключаем звук

        bcf PORTB,7; Включаем звуковой излучатель

          goto CHECK; и снова опрашиваем внешние триггеры

BUZ_OFF

        bsf PORTB,7; Выключаем звук

           goto CHECK; и снова опрашиваем внешние триггеры

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

1. Если на выводе RA0 ВЫСОКИЙ уровень, значит, банка пересекла луч входного детектора. Соответственно к счетчику (регистру EVENT) прибавляется единица и триггер входного детектора сбрасывается. Если значение счетчика больше четырех, то путем подачи на выход RB0 НИЗКОГО уровня включается звуковой сигнализатор, в противном случае он выключается. Выполняется повторная проверка триггеров.

2. Если на выводе RA1 ВЫСОКИЙ уровень, значит, банка пересекла луч выходного детектора. Соответственно из счетчика EVENT вычитается единица и триггер выходного детектора сбрасывается. Счетчик проверяется на равенство четырем, и звуковой излучатель переключается в соответствующее состояние. Выполняется повторная проверка триггеров.

3. Если ни один из триггеров не установлен, выполняется выход из обработчика.

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

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

Пример 7.3

На стр. 220 мы говорили о часах реального времени системы центрального отопления. Напишите процедуру обработки прерывания, которая при каждом прерывании, генерирующемся с периодом 0.1 с, увеличивала бы на единицу значение времени, хранящееся в четырех регистрах данных. Это значение представлено в 24-часовом формате. В каждом байте хранится два BCD-разряда, к примеру, BCD-число 40 в регистре MINUTES представляется как Ь’0100 0000’. Этот формат называется упакованным BCD-форматом.

Решение

При каждом вызове процедуры обработки прерывания необходимо добавлять 1 к четырехбайтному числу, хранящемуся в регистрах HOURS: MINUTES: SECONDS: JIFFY. Причем регистр JIFFY используется как счетчик по модулю 10, SECONDS и MINUTES — по модулю 60, a HOURS — по модулю 24. С учетом этого составим перечень задач:

1. Прибавить 1 kJIFFY.

2. Если JIFFY = 10, то обнулить JIFFY и прибавить единицу к SECONDS. В противном случае перейти к п. 6.

3. Если SECONDS = 60, то обнулить SECONDS и прибавить единицу к MINUTES. В противном случае перейти к п. 6.

4. Если MINUTES = 60, то обнулить MINUTES и прибавить единицу к HOURS. В противном случае перейти к п. 6.

5. Если HOURS = 24, обнулить HOURS.

6. Выйти из обработчика.

Код, реализующий описанный алгоритм, приведен в Программе 7.4. Сохранение и восстановление контекста реализовано обычным образом. Однако, поскольку в обработчике используется регистр FSR, он тоже сохраняется в регистре _fsr и восстанавливается при выходе из обработчика.

Программа 7.4. Обработчик прерывания часов реального времени

_work equ h’4D’; Копия W

_status egu h’4E’; Копия STATUS

_fsr equ h’4F’; Копия FSR

HOURS equ h’20’; Часы 2 разряда

MINUTES equ h’21’; Минуты (2 разряда)

SECONDS equ h’22’; Секунды (2 разряда)

JIFFY equ h’23’; Доли секунды (2 разряда)

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

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

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

       movwf _status

       movf FSR,w; а также регистр FSR

       movwf _fsr

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

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

; Задача 1

       incf JIFFY,f; Увеличим Jiffy на единицу

       movlw d’10’; Сравним десятью

       btfss STATUS,Z; ECЛИ равно, TO продолжаем

          goto EXIT; ИНАЧЕ выходим из обработчика

; Задача 2

       clrf JIFFY; ИНАЧЕ обнуляем Jiffy

       movlw SECONDS; Устанавливаем FSR на Seconds

       movwf FSR

       call BCD_INC; и инкрементируем BCD-число

       movlw h’60’; Сравниваем с 0110 0000 (60 BCD)

       subwf SECONDS,w

       btfss STATUS,Z; ЕСЛИ разно, TO продолжаем

          goto EXIT; ИНАЧЕ выходим из обработчика

; Задача 3

       clrf SECONDS; ИНАЧЕ обнуляем Seconds

       decf FSR,f; Устанавливаем FSR на Minutes

       call BCD_INC; и инкрементируем BCD-число

       movlw ’60’; Сравниваем с 0110 0000 {60 BCD)

       subwf MINUTES,w

       btfss STATUS,Z; ЕСЛИ разно, TO продолжаем

          goto EXIT; ИНАЧЕ выходим из обработчика

; Задача 4

       clrf MINUTES; ИНАЧЕ обнуляем Minutes

       decf FSR,f; Устанавливаем FSR ка Hours

       call BCD_INC; и инкрементируем BCD-число

       movlw h’24’; Сравниваем с 0010 0100 (24 BCD)

       subwf HOURS,w

       btfsc STATUS,Z; ЕСЛИ не равно, TO продолжаем

       clrf HOURS; ИНАЧЕ обнуляем Hours

; --------------------------

; Задача 5

EXIT reovf _fsr,w; Восстанавливаем FSR

        movwf FSR

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

        movwf STATUS

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

        swapf _work,w; не воздействуя на флаги,

        retfie; и выходим из обработчика

Основное тело обработчика прерывания разбито на секции в соответствии с составленным алгоритмом. После каждого инкрементирования из нового значения вычитается константа, равная основанию счета. Если эти значения равны, регистр обнуляется и инкрементируется следующий байт числа. Вместо проверки флага нуля можно было бы контролировать значение флага переноса, проверяя, чтобы полученное значение было равно или больше значения основания, btfss STATUS,С[106].

В примере предполагается, что данные хранятся в упакованном BCD-формате. То есть число 59 хранится в виде h’0101 1001’ или h’59’. Это означает, что операция инкрементирования должна соответствовать формату BCD. Эту коррекцию можно осуществить после обычного инкрементирования, проверяя, чтобы младший полубайт не стал больше девяти. В противном случае к числу прибавляется шесть. Поскольку число не может принимать значения более 59, нам не нужно выполнять аналогичную проверку для старшего полубайта. С алгоритмом полного инкрементирования упакованного BCD-числа можно познакомиться в Примере 4.5 на стр. 111.

Поскольку эту операцию надо выполнить 3 раза (для всех байтов, исключая JIFFY, который никогда не становится больше девяти), лучше всего оформить ее в виде подпрограммы. Код такой подпрограммы приведен в Программе 7.5. В данном случае регистр FSR указывает на регистр, который содержит подлежащее инкрементированию упакованное BCD-число. Это значение просто инкрементируется непосредственно в регистре с использованием косвенной адресации. После этого оно корректируется описанным выше способом. Предполагается, что данные, на которые указывает FSR, уже находятся в BCD-формате, т. е. в подпрограмме отсутствует преобразование натурального двоичного числа в двоично-десятичное.

Программа 7.5. Подпрограмма инкрементирования упакованного BCD-числа

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

; * ФУНКЦИЯ: Прибавляет 1 к упакованному BCD-числу (98 макс) *

; * ВХОД: FSR указывает на регистр с числом *

; * ВЫХОД: BCD-число инкрементируется *

; * W и STATUS изменяются *

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

BCD_INC incf INDF,f; Прибавляем 1 к адресованному байту

              movf INDF,w; Считываем его

              addlw 6; Прибавляем шесть

              btfss STATUS,DC; Проверяем десятичный перенос

                 goto BCD_EXIT; ЕСЛИ нет, ТО выходим

              movwf INDF; ИНАЧЕ возвращаем скорректированное значение

BCD_EXIT return

Пример 7.4

В торговом автомате монеты разных номиналов проходят через один из шести микропереключателей, подключенных к порту В. При прохождении монеты переключатель замыкается, что вызывает появление на соответствующем выводе сигнала НИЗКОГО уровня, как показано на Рис. 7.8.

Напишите процедуру обработки прерывания, которая будет накапливать значение суммы в регистре MONEY. Предполагается, что в фоновой программе регистр INTCON будет сконфигурирован таким образом, чтобы разрешить внешнее прерывание с вывода RB0/INT.

Рис. 7.8. Монетоприемник торгового автомата

Решение

Как показано в Программе 7.6, после сохранения контекста и сброса флага INTF производится последовательная проверка всех переключателей. НИЗКОМУ уровню на каком-либо выводе порта соответствует лог. О в соответствующем бите регистра PORTB. В соответствии с логикой работы механизма монетоприемника одновременно может быть замкнут только один переключатель, поэтому нет необходимости выходить из процедуры после неудачного поиска.

Программа 7.6. Обработчик прерывания монетоприемника торгового автомата

VEND  movwf _work; Сохраняем W в памяти данных

          swapf STATUS,w; Читаем STATUS, не изменяя флагов,

          movwf _status; и сохраняем его в памяти данных

; -------------------------

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

           movf MONEY,w; Берем текущее значение MONEY

           btfss PORTB,7; Проверяем $2

             addlw d’200’; ЕСЛИ 0, ТО прибавляем 200

           btfss PORTB,6; Проверяем $1

             addlw d’100’; ЕСЛИ 0, ТО прибавляем 100

           btfss PORTB,5; Проверяем 25с

             addlw d’25’; ЕСЛИ 0, ТО прибавляем 25

           btfss PORTB,4; Проверяем 10с

             addlw d’10’; ЕСЛИ 0, ТО прибавляем 10

           btfss PORTB,3; Проверяем 5с

             addlw 5; ЕСЛИ 0 ТО прибавляем 5

           btfss PORTB,2; Проверяем 1с

             addlw 1; ЕСЛИ 0, ТО прибавляем 1

           movwf MONEY; Сохраняем новую сумму

; ---------------------------

; Точка выхода

           swapf _status,w; Восстанавливаем исходное значение

           movwf STATUS; регистра STATUS

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

           swapf _work,w; не воздействуя на флаги,

           retfie; и возвращаемся в фоновую программу

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

7.1. Перепишите Программу 7.2 так, чтобы она подсчитывала число банок, равное одному гроссу (144). Это значение следует хранить в упакованном BCD-формате (СОТНИ и ДЕСЯТКИ: ЕДИНИЦЫ), и оно может использоваться фоновой подпрограммой для отображения общего числа банок.

7.2. Какие изменения следует внести в Программу 7.2, чтобы максимальное число банок в печи могло быть равным 1000?

7.3. Взяв в качестве образца Рис. 7.1, напишите процедуру обработки прерывания, выполняющую следующие операции:

• Копирование 16-битного числа в два регистра общего назначения — ТЕМР_Н и TEMP_L.

• Вычитание его из предыдущего значения, хранящегося в регистрах LAST_COUNT_H и LAST_COUNT_L, и запись разницы в регистры DIFFERENCE Н и DIFFERNCE L.

• Замещение предыдущего значения новым.

• Запись в РОН с именем NEW ненулевого значения для передачи в фоновую программу информации о том, что доступно новое значение. Фоновая процедура обнулит регистр NEW после обработки данных.

7.4. Скорость вращения вала можно измерить с использованием кодирующего диска, который генерирует импульс при повороте вала на каждые 10°. Этот импульс может использоваться в качестве сигнала внешнего прерывания микроконтроллера. Учитывая, что максимальная скорость вращения составляет 20 000 оборотов в минуту, какое наибольшее время выполнения может иметь процедура обработки прерывания, позволяющее избежать пропуска импульсов? Предполагается, что частота кварцевого резонатора равна 4 МГц.

7.5. Электронная рулетка определяет расстояние путем излучения ультразвуковых импульсов и контролируя время прихода отраженного сигнала. Схема такого ультразвукового дальномера приведена на Рис. 7.9 (за его основу взята схема с Рис. 7.7).

Наибольшее измеряемое расстояние составляет 2.5 м при разрешении 1 см. Скорость звука в воздухе при температуре 20 °C равна 344 м/с, т. е. время, за которое сигнал пройдет расстояние 1 см и вернется обратно, равно 58 мкс.

Рис. 7.9. Аппаратная часть ультразвукового дальномера

Используя в качестве задающего генератор с частотой 17.2 кГц, получим одно прерывание каждые 58 мкс.

Учитывая схему, программа должна выполнять следующие операции:

• Фоновая программа

1) Обнулить счетчик JIFFY и флаг NEW

2) Подать импульс на излучатель.

3) Ждать установления ненулевого значения флага NEW

4) Отобразить подсчитанное значение.

5) Перейти к п. 1.

• Процедура обработки прерывания

1) При каждом импульсе генератора инкрементировать счетчик JIFFY.

2) При обнаружении сигнала от приемника записать в флаг NEW ненулевое значение, извещающее фоновую программу о том, что в переменной JIFFY находится конечное число.

3) Повторять, пока активен хотя бы один сигнал.

4) Выйти из прерывания.

Напишите код процедуры обработки прерывания, которая использует регистр NEW для извещения фоновой программы о приходе отраженного сигнала. За основу можно взять Программу 7.3.

7.6. Предполагается увеличить диапазон цифрового ультразвукового дальномера до 10 м и разрешение до 1 мм. Какие изменения необходимо внести в аппаратную и программную части?

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

Vt = V0∙√(1 + (Δt/273)),

где V0 — скорость распространения при 20 °C, a Vt — скорость при температуре t. Какое изменение температуры приведет к появлению ошибки в 1 мм при работе дальномера на максимальной дистанции?

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

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

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