Глава 17 Учебный пример
До настоящего момента вся информация носила в какой-то степени фрагментарный характер. Чтобы придать процессу изучения логическую завершенность, давайте попробуем применить большую часть полученных знаний и разработать реальное устройство (как схему, так и программу). Может показаться, что в одной короткой главе это сделать не так уж легко. Однако на данном этапе нам придется изучить совсем мало нового материала, большей же частью мы будем просто применять полученные знания.
Всякая подобная работа начинается с составления подробных технических требований. Все без исключения студенты во время устных докладов говорят очень долго. Чтобы хоть как-то их ограничить, предполагается создать специализированное устройство на базе микроконтроллера, которое бы отслеживало заданный интервал времени. По умолчанию рабочий период этого устройства (назовем его таймером) равен 10 мин, однако следует предусмотреть возможность изменения этого значения в пределах 1…99 мин.
После запуска таймер должен выполнить следующие операции:
1. При нажатии кнопки СБРОС включается зеленый СИД, а на сдвоенном 7-сегментном индикаторе начинается обратный отсчет от заданного значения до числа
2. Еще через минуту включается желтый СИД, на дисплее высвечивается число
3. Еще через минуту включается красный СИД, на дисплее высвечивается число
4. И наконец, по истечении последней минуты на дисплее высвечивается число
5. В любой момент времени работу таймера можно приостановить нажатием и удерживанием кнопки ПАУЗА. При отпускании этой кнопки процесс счета продолжается с места остановки.
6. Чтобы изменить величину интервала, заданную по умолчанию (
Итак, прежде всего нам необходимо выбрать подходящий микроконтроллер. В данном случае мы вынуждены будем ограничить наш выбор моделями, рассматриваемыми в книге, т. е. PIC12F675/29, PIC16F627/8 и PIC16F87X. Поскольку модуль АЦП нам не требуется, то для реализации описанного устройства можно спокойно выбрать любой из указанных микроконтроллеров. Использование 18-выводного PIC16F627/8 вместо 40-выводного PIC16F874/7 потребует дополнительных ухищрений для реализации необходимого количества линий ввода/вывода, однако в то же время на его примере можно будет проиллюстрировать выбор оптимального решения при разработке более сложных систем. Альтернативный вариант таймера, в котором используется последний из указанных микроконтроллеров, можно найти на Web-сайте книги. В первой редакции книги[193] был использован микроконтроллер PIC16F84, и, поскольку он по выводам совместим с PIC16F627, решено было использовать последний. Единственное изменение, которое потребовалось внести в программу, было связано с модулем аналогового компаратора, отсутствовавшего в исходной модели.
Окончательная схема на базе выбранного микроконтроллера приведена на Рис. 17.1. Ниже описывается, как используются выводы микроконтроллера.
Рис. 17.1. Схема таймера со звуковой и световой сигнализацией
Органы управления
Пять кнопок S2…S6, управляющие операциями ПУСК, УСТАНОВКА, СТОП, ДИАГНОСТИКА, ПАУЗА, подключены к линиям RB[4:0] порта В. Используя внутреннюю подтяжку (см. Рис. 11.9 на стр. 342), мы можем отказаться от внешних подтягивающих резисторов.
Кнопка S1 вместе с подтягивающим резистором R1 используется для ручного сброса системы, вызывающего перезапуск отсчета времени. Этот же сигнал
В данной схеме можно использовать любые кнопки без фиксации с нормально разомкнутыми контактами.
Световая индикация
Для формирования световых сигналов используются 10-мм светодиоды D3…D1 подходящих цветов с большой яркостью, подключенные к выводами RB[7:5] порта В. Резисторы сопротивлением 330 Ом, включенные последовательно с СИД, ограничивают их ток на уровне 10 мА.
Звуковая индикация
Для звуковой индикации мы применим миниатюрный твердотельный излучатель. Типичный пьезоэлектрический излучатель может работать в диапазоне напряжений от 3 до 16 В, потребляя при напряжении 5 В чуть больше 1 мА[194]. Управление звуковым излучателем осуществляется с вывода RA2.
Цифровой дисплей
Цифровой дисплей, позволяющий отображать значения от 00 до 99, образован двумя 7-сегментными светодиодными индикаторами. Поскольку в нашем распоряжении осталось всего 4 линии ввода/вывода, мы реализовали последовательный интерфейс. Этот интерфейс похож на применяющийся в схеме на Рис. 12.2 (стр. 370), однако в данном случае для каждого 8-битного сдвигового регистра с последовательным входом и параллельным выходом 74НС164 используется отдельная линия данных (RA0 — для десятков и RA3 — для единиц). Поэтому изображение на обоих индикаторах можно будет обновлять одновременно (за восемь тактов).
Цоколевка 7-сегментных индикаторов, показанная на схеме, соответствует индикаторам с общим анодом, выпускающимся в 16-выводном DIP-корпусе и имеющим десятичные точки с обеих сторон знакоместа — lhdp и rhdp. В нашем устройстве используется только правая точка для индикации паузы. Широко распространены индикаторы в других 16-выводных и 14-выводных корпусах, в том числе и сдвоенные (с двумя знакоместами в одном корпусе). Однако даже для индикаторов в 16-выводных корпусах цоколевка не стандартизирована.
В малогабаритных индикаторах (высотой менее 20 мм/0.8 дюйма) для каждого сегмента используется один СИД, прямое падение напряжения на котором составляет около 2 В[195]. Две резисторные сборки R5 и R6 сопротивлением 330 Ом ограничивают ток через сегменты на уровне 10 мА. Общие аноды подключены непосредственно к линии +5 В и должны быть развязаны с помощью танталового конденсатора небольшой емкости. Хотя яркость свечения индикаторов обычно нормируется при токе 20 мА, она будет вполне достаточной даже при выбранном нами токе. Кроме того, при этом исключается необходимость подключения буферов к выходу сдвиговых регистров 74НС164[196].
Кварцевый резонатор
В качестве времязадающего элемента тактового генератора используется кварцевый резонатор частотой 3.2768 МГц, в результате частота выполнения команд составляет 819.21 кГц. Типичный резонатор с такой частотой имеет точность ±300 ppm и температурный коэффициент ±50 ppm в диапазоне рабочих температур. Такой необычный выбор частоты обусловлен тем, что при использовании Таймера 0 с коэффициентом деления предделителя, равным 64, мы сможем формировать прерывания ровно 50 раз в секунду (см., далее). В принципе можно было бы снизить потребление микроконтроллера, взяв резонатор частотой 32.768 кГц и генерируя прерывание каждые две секунды с использованием 16-битного Таймера 1. Однако мощность, потребляемая микроконтроллером, в любом случае будет мизерной по сравнению с потреблением светодиодных индикаторов.
В моделях PIC16F627/8 выводы OSC1 и OSC2 могут использоваться в качестве дополнительных линий порта А, при этом микроконтроллер будет работать от внутреннего ЛС-генератора с номинальной частотой 4 МГц (см. Табл. 10.2 на стр. 309). На то, что в нашей схеме используется внешний кварцевый резонатор, указывает наличие в Программе 17.3 опции конфигурации _XT_OSC.
* * *
Теперь, когда схема разработана, мы можем сосредоточить все свои усилия на разработке программы.
В общем виде модульная структура нашей системы изображена на Рис. 17.2. Прямоугольники с двойными линиями по краям соответствуют подпрограмме или процедуре обработки прерывания. На данном этапе можно выделить три обособленных процесса и две основных вспомогательных задачи.
Рис. 17.2. Модульная структура программы таймера
Задача формирования временных отсчетов
Все процессы привязаны ко времени. Временная привязка реализована аппаратно, генерацией прерывания с периодичностью 50 раз в секунду. Изменение значений секунд и минут происходит после накопления определенного количества тиков. Эти значения в дальнейшем используются для выполнения требуемых процессов.
При обнаружении нажатия на кнопку ПАУЗА декрементирование этих счетчиков приостанавливается, за счет чего обратный отсчет времени можно «заморозить» на сколь угодно длительный временной интервал.
Задача отображения времени
Состояние счетчика, а также вспомогательную информацию необходимо выводить на двухразрядный дисплей. Поскольку в процессе вывода осуществляется преобразование параллельного кода в последовательный, а также его передача по последовательному каналу, эту задачу лучше выделить в отдельный модуль.
Фоновый (основной) процесс
Фоновый процесс осуществляет в цикле вывод на индикатор значение счетчика минут до тех пор, пока он не станет равным нулю. При замыкании кнопки СТОП происходит преждевременный выход из цикла.
Процесс установки интервала
Если в момент сброса микроконтроллера кнопка УСТ. находится в замкнутом состоянии, то вызывается подпрограмма SETT. Эта подпрограмма постепенно уменьшает выводимое на дисплей число до тех пор, пока кнопка не будет отпущена. Последнее показанное число сохраняется в EEPROM и используется всеми запускаемыми впоследствии фоновыми процессами в качестве начального значения для отсчета интервала.
Процесс самодиагностики
Если при сбросе кнопка ДИАГ. находится в замкнутом состоянии, то управление передается в подпрограмму диагностики. Основной задачей этой подпрограммы является проверка всех периферийных устройств, чтобы облегчить нахождение неисправного узла.
Все процессы зависят от задачи формирования временных отсчетов, которая предоставляет базовую информацию о реальном времени. Как показано в Программе 17.1, эта задача реализована в виде обработчика прерывания от Таймера 0. Предделитель таймера сконфигурирован таким образом, чтобы при системной тактовой частоте 3.2763 МГц переполнение таймера происходило бы каждые 1/50 с. Поскольку прерывание от Таймера 0 разрешено (см. Программу 17.3), микроконтроллер будет переходить к обработчику прерывания при каждом переполнении таймера — каждые 256 импульсов с выхода предделителя. Учитывая, что частота внутреннего тактового сигнала составляет 1/4 от частоты резонатора, коэффициент деления предцелителя, равный 64, позволит нам формировать отсчеты времени 50 раз в секунду, т. е. 3.2763x106/4x64x256 = 50
Итак, обработчик прерывания будет выполнять следующие задачи:
1. ЕСЛИ кнопка ПАУЗА отпущена, ТО
а) Декрементировать счетчики на один тик.
б) Если прошла секунда, то установить соответствующий флаг.
2. ИНАЧЕ
а) Переключить флаг состояния ПАУЗА.
б) ЕСЛИ он установлен, ТО показать, что работа таймера приостановлена.
в) ИНАЧЕ отобразить время (нормальная работа).
г) Ждать, пока не будет отпущена кнопка УСТ.
3. Выйти из прерывания.
Программа 17.1. Обработка времени
; *********************
; * При каждом вызове обработчика прерывания внутренний *
; * счетчик увеличивается на 20-мс дискрет *
; * Каждую секунду в NEW_SEC заносится ненулевое значение *
; *********************
; Сначала сохраним контекст
ISR movwf _work; Сохраняем W
swapf STATUS,w; и регистр STATUS
movwf _status
; ================
; Основной код
btfss INTCON,T0IF; Произошло переполнение Таймера 0?
goto ISR_EXIT; ЕСЛИ нет, ТО ложная тревога
btfsc Pause,0; Проверяем флаг паузы
goto ISR_EXIT; ЕСЛИ нажата, не инкрементируем
incf JIFFY,f; Регистрируем очередные 1/50 с
movlw d’50’; Досчитали до 50?
subwf JIFFY,w
btfss STATUS,Z
goto ISR_EXIT; ЕСЛИ нет, ТО выходим
clrf JIFFY; ИНАЧЕ обнуляем счетчик дискретов
movf SECOND,f; Счетчик секунд равен нулю?
btfsc STATUS,Z
goto NEW_MIN; ЕСЛИ да, ТО смотрим минуты
decf SECOND,f; ИНАЧЕ декрементируем счетчик секунд,
incf NEW_SEC,f; извещаем основную программу о прохождении секунды
goto ISR_EXIT; и выходим
NEW_MIN movlw d’59’;Реинициализируем счетчик секунд
movwf SECOND
movf MINUTE,f; Счетчик минут равен нулю?
btfsc STATUS,Z
goto ISR_EXIT; ЕСЛИ да, ТО делать больше нечего
decf MINUTE,f; ИНАЧЕ декрементируем счетчик минут
; *************************
ISR_EXIT btfss PORTB,PAUSE; Проверяем кнопку ПАУЗА
call FREEZE; ЕСЛИ нажата, ТО обновляем флаг паузы
bcf INTCON,T0IF; Сбрасываем флаг прерывания
swapf _status,w; Восстанавливаем регистр STATUS
movwf STATUS
swapf _work,f; Восстанавливаем W,
swapf _work,w; не меняя состояния STATUS,
retfie ; и возвращаемся из прерывания
; ***********************
; * ФУНКЦИЯ: Инкрементирует флаг паузы. *
; * ЕСЛИ 1, ТО отображает десятичные точки *
; * ЕСЛИ 0, ТО отображает нормальный отсчет минут *
; * РЕСУРСЫ П/п SPI_WRITE, переменная Pause *
; * ВХОД;Кнопка ПАУЗА нажата *
; * ВЫХОД Кнопка ПАУЗА отжата; соответствующая индикация *
*************************
FREEZE incf Pause,f; Обновляем 0-й бит флага паузы
btfss Pause,0; Проверяем его состояние
goto UNFREEZE; Переход 1 —> 0, разблокируем
; Дисплей заблокирован
movlw b’01111111’;Код для десятичной точки
movwf DATA_OUT_L
movwf DATA_OUT_H
call SPI_WRITE
goto FREEZE EXIT
UNFREEZE; Сюда переходим, если 0-й бит флага изменился 1 —> 0.
movf MINUTE,w; Отображаем оставшееся количество минут
call OUTPUT
FREEZE_EXIT
btfss PORTB,PAUSE; Ждем отпускания кнопки
goto FREEZE_EXIT; Сбрасываем таймер/предделитель
clrf TMR0
return
Из Программы 17.1 видно, что время хранится в виде 3-байтного числа в регистрах MINUTES, SECOND и JIFFY. Полагая, что 0-й бит регистра PAUSE сброшен в 0, значение счетчика тиков JIFFY увеличивается на единицу. Обычно после этого выполняется выход из обработчика прерывания, однако если JIFFY становится равным 50, то он обнуляется и декрементируется счетчик секунд SECOND. При этом в регистр NEW_SEC заносится ненулевое значение, извещающее фоновый процесс о том, что прошла секунда. Когда счетчик секунд становится равным нулю, в него снова загружается константа 59, а счетчик минут MINUTES декрементируется. Описанная процедура похожа на операцию инкрементирования счетчика, которую мы реализовали в Примере 7.3.
Задача формирования временных отсчетов также поддерживает функцию приостановки счета. Самым простым решением был бы пропуск декрементирования счетчиков при нажатой кнопке ПАУЗА. Однако если потребуется удерживать кнопку нажатой дольше нескольких минут, то эта операция может оказаться довольно утомительной.
Формирование останова/пуска по последовательным нажатиям кнопки является более эргономичным и может быть достаточно легко реализовано программно. И уж точно, это гораздо лучше, чем использовать кнопку какого-либо другого типа (скажем, кнопку с фиксацией). В Программе 17.1 код обработки нажатия кнопки ПАУЗА вынесен в отдельную подпрограмму FREEZE. Мы вполне можем вызвать подпрограмму из обработчика прерывания точно так же, как и из другой подпрограммы. Аппаратный стек позволяет использовать до 8 уровней вложенности. В нашем случае будет использовано только два уровня стека.
Подпрограмма FREEZE вызывается только в случае замыкания кнопки ПАУЗА. При каждом входе в подпрограмму изменяется состояние 0-го бита регистра PAUSE, что реализуется простым инкрементированием регистра.
После переключения PAUSE[0] проверяется его состояние и, если он равен 1, то вызывается подпрограмма SPI_WRITE для вывода на индикаторы только двух десятичных точек. Разумеется, это только один из множества возможных способов отображения состояния паузы. Можно, например, выводить на индикатор символы
Выход из подпрограммы осуществляется только после отпускания кнопки ПАУЗА. Это очень важно, так как в противном случае при входе в обработчик прерывания по следующему переполнению Таймера 0 могло бы произойти повторное (ложное) переключение флага паузы. Чтобы предотвратить влияние дребезга контактов кнопки, после ее отпускания сначала обнуляется Таймер 0 со своим предделителем и только после этого сбрасывается флаг прерывания T0IF, благодаря чему повторная проверка состояния кнопки будет произведена только через 1/50 секунды.
Вывод на индикаторы содержимого рабочего регистра в виде десятичного числа осуществляется подпрограммой OUTPUT, код которой приведен в Программе 17.2. Эта подпрограмма выполняет следующие операции:
1. Преобразует двоичное число в 2-разрядное BCD-число.
2. Преобразует значения обоих разрядов в коды 7-сегментного индикатора.
3. Пересылает каждый байт в соответствующий индикатор по последовательному каналу.
Программа 17.2. Функция вывода на индикаторы
; *************************
; * ФУНКЦИЯ: Выводит на индикаторы 2-разрядное десят. число *
; * РЕСУРСЫ: П/п BIN_2_BCD, SPI_WRITE, SVN_SEG *
; * РЕСУРСЫ: Перем. DATA_OUT_L, DATA_OUT_H, NEW_SEC, NUMBER *
; * ВХОД: Число в W (от 0 до 99) *
; * ВЫХОД: Число выводится, NEW_SEC обнуляется *
; *************************
OUTPUT bcf PORTA,SCK; Инициализируем линию тактового сигнала
call BIN_2_BCD; Преобразовываем в BCD
movwf NUMBER; Сохраняем результат в NUMBER
movf NUMBER,w; Берем число, которое нужно вывести
andlw b’00001111’; Выделяем число единиц
call SVN_SEG; Преобразуем в 7-сегментный код
movwf DATA_OUT_L; Копируем в младший регистр
swapf NUMBER,w; Перегружаем число десятков в младший полубайт
andlw b’00001111’; Выделяем число десятков
call SVN_SEG; Преобразуем в 7-сегментный код
movwf DATA_OUT_H; Копируем в старший регистр
call SPI_WRITE; Передаем значение обоих разрядов
clrf NEW_SEC; Обнуляем флаг NEW_SEC
return
; *************************
; * ФУНКЦИЯ: Одновременно передает два байта по последовательному каналу *
; * ВХОД: Значения в DATA_OUT_L (младший разряд) *
; * ВХОД: и DATA_OUT_H (старший разряд) *
; * ВЫХОД DATA_OUT_L и DATA_OUT_H изменяются *
; *************************
SPI_WRITE
bcf PORTA,SCK; Выставляем НИЗКИЙ уровень на SCK
movlw 8; Инициализируем счетчик цикла
movwf COUNT
LOOP bcf PORTA,SDOH ; Выставляем 0 на линию данных старшего разряда
rlf DATA_OUT_H,f; Выдвигаем младший бит в бит переноса
btfsc STATUS,С; ЕСЛИ С == 0, ТО пропускаем
bsf PORTA,SDOH; ИНАЧЕ выставляем на линию данных 1
bcf PORTA,SDOL; Выставляем 0 на линию данных младшего разряда
rlf DATA_OUT_L,f; Выдвигаем младший бит в бит переноса
btfsc STATUS,С; ЕСЛИ С == 0, ТО пропускаем
bsf PORTA,SDOL; ИНАЧЕ выставляем на линию данных 1
bsf PORTA,SCK; Формируем тактовый импульс
bcf PORTA,SCK
Преобразование двоичного кода в код 7-сегментного индикатора
Подпрограмма SVN_SEG преобразует младший полубайт содержимого регистра W в соответствующий код 7-сегментного индикатора. Код подпрограммы полностью эквивалентен приведенному в Программе 6.6 (стр. 184).
Вывод по SPI
Подпрограмма SPI_WRITE похожа на свою тезку, реализованную в Программе 12.1 на стр. 371, но формирует два потока последовательных данных. Число, находящееся в регистре DATA_OUT_L, передается по линии RA3, тогда как число, находящееся в регистре DATA_OUT_H, — по линии RA0. Оба канала используют общий тактовый сигнал.
Прежде чем перейти к кодированию процессов, составляющих программу, вкратце рассмотрим используемую конфигурацию микроконтроллера, задаваемую при его программировании, а также инициализационный код, выполняемый после сброса микроконтроллера (см. Программу 17.3).
Программа 17.3. Инициализационный код
include "p16f627a.inc
SDOH equ 0
SCK equ 1
BUZ equ 2
SDOL equ 3
GREEN equ 5
YELLOW equ 6
RED equ 7
PAUSE equ 0
DIAG equ 1
STOP equ 2
SETT equ 3
GO equ 4
cblock 20h
MINUTE:1, SECOND:1, JIFFY:1, NUMBER:1, NEW_SEC:1
DATA_OUT_L:1, DATA_OUT_H, COUNT:1, TEMP:1, TIME_OUT:1
Pause:1, _work:1, _status:1
endc
__config _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _LVP_OFF & _MCLRE_ON
org 2100h; Область EEPROM
de d’10’; Значение по умолчанию — 10 мин
RESET org 0; Вектор сброса
Преобразование двоичного кода в код 7-сегментного индикатора
Подпрограмма SVN_SEG преобразует младший полубайт содержимого регистра W в соответствующий код 7-сегментного индикатора. Код подпрограммы полностью эквивалентен приведенному в Программе 6.6 (стр. 184).
Вывод по SPI
Подпрограмма SPI_WRITE похожа на свою тезку, реализованную в Программе 12.1 на стр. 371, но формирует два потока последовательных данных. Число, находящееся в регистре DATA_OUT_L, передается по линии RA3, тогда как число, находящееся в регистре DATA_OUT_H, — по линии RA0. Оба канала используют общий тактовый сигнал.
Прежде чем перейти к кодированию процессов, составляющих программу, вкратце рассмотрим используемую конфигурацию микроконтроллера, задаваемую при его программировании, а также инициализационный код, выполняемый после сброса микроконтроллера (см. Программу 17.3).
Программа 17.3. Инициализационный код
include "p16f627a.inc
SDOH equ 0
SCK equ 1
BUZ equ 2
SDOL equ 3
GREEN equ 5
YELLOW equ 6
RED equ 7
PAUSE equ 0
DIAG equ 1
STOP equ 2
SETT equ 3
GO equ 4
cblock 20h
MINUTE:1, SECOND:1, JIFFY:1, NUMBER:1, NEW_SEC:1
DATA_OUT_L:1, DATA_OUT_H, COUNT:1, TEMP:1, TIME_OUT:1
Pause:1, _work:1, _status:1
endc
__config _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _LVP_OFF & _MCLRE_ON
org 2100h; Область EEPROM
de d’10’; Значение по умолчанию — 10 мин
RESET org 0; Вектор сброса
goto MAIN
org 4; Вектор прерывания
goto ISR
MAIN bsf STATUS,RP0; Переключаемся в 1-й банк
movlw b’11100000’; RA4:0 — выходы
movwf TRISA
movlw Ь’11100000’; RB7:5 — выходы; RB4:0 — входы
movwf TRISB
movlw b’00000101’; Таймер 0: внутр. такт. сигнал,
movwf OPTION_REG; предделитель 1:64. Подтяжка вкл.
bcf STATUS,RP0; Возвращаемся в 0-й банк
clrf Pause; Обнуляем флаги паузы
clrf NEW_SEC; и секунды
clrf TMR0
bcf INTCON,T0IF
bsf INTCON,T0IE;Разрешаем прерывание от Таймера 0
bsf INTCON,GIE;Разрешаем все прерывания
btfss PORTB,SETT;Проверяем кнопку УСТ.
call SET_TIME;ЕСЛИ нажата, ТО устанавливаем интервал
btfss PORTB,DIAG;Проверяем кнопку ДИАГ.
call DIAGNOSTIC;ЕСЛИ нажата, ТО выполняем самодиагностику
Конфигурирование кристалла
С помощью директивы __config задается состояние конфигурационных ячеек в слове конфигурации кристалла. Сторожевой таймер отключен, генератор работает с внешним кварцевым резонатором, также задействован вход внешнего сброса
При прошивке микроконтроллера в ячейку EEPROM с адресом h’00’ заносится число 10. Это означает, что интервал счета только что запрограммированного микроконтроллера составляет 10 мин. Данное значение впоследствии можно изменять посредством процедуры установки интервала.
Эта ячейка расположена в адресном пространстве специальной области памяти по адресу h’2100’, а для указания значения, заносимого в эту область памяти на этапе программирования, используется директива de, как было описано на стр. 549.
Выполнение программы
Код, выполняемый при каждом сбросе микроконтроллера, используется для инициализации рабочего окружения.
Вектора
По адресу вектора сброса (h’000’) расположен переход к основной программе MAIN, а по адресу вектора прерывания (h’004’) — команда перехода к процедуре обработки прерывания.
Конфигурирование портов
Линии PORTA[4:0] и PORTB[7:5] конфигурируются как выходы, а остальные линии портов используются как входы.
Конфигурирование Таймера 0
Коэффициент деления предделителя задается равным 64, в качестве источника тактовых импульсов Таймера 0 используется системный тактовый сигнал. Также разрешается прерывание от Таймера 0.
Выбор текущего процесса
Для выбора процесса, которому следует передать управление, проверяется состояние кнопок ДИАГ. и УСТ. Если ни одна из кнопок не нажата, то осуществляется переход к процессу MAIN.
Если в момент сброса микроконтроллера нажата кнопка ДИАГ., то управление передается в подпрограмму DIAGNOSTIC, код которой приведен в Программе 17.4. Задачей процесса диагностики является тестирование различных периферийных устройств, подключенных к процессору, с целью проверки целостности цепей и исправности собственно устройств.
Программа 17.4. Процедура самодиагностики
; *****************
; * ФУНКЦИЯ: Проверяет состояние кнопок и включает соотв. *
; * ФУНКЦИЯ: СИД или звуковой излучатель. Поочередно включает*
; * ФУНКЦИЯ: по одному сегменту на каждом индикаторе *
; * РЕСУРСЫ: Подпрограмма SPI_WRITE *
; * РЕСУРСЫ: Переменные TEMP, DATA_OUT_H, DATA_OUT_L *
; * ВХОД: Кнопка ДИАГ. нажата *
; * ВЫХОД: Кнопка ДИАГ. отпущена *
; *****************
DIAGNOSTIC
movlw b’11111110’; Формируем начальное значение
movwf TEMP; маски управления индикаторами
D_LOOP movlw b’11111111’; Выключаем все СИД и пищалку
movwf PORTB
bsf PORTA,BUZ
; Сканируем кнопки
btfss PORTB,PAUSE; ЕСЛИ нажата кнопка ПАУЗА,
bcf PORTB,GREEN; TO включаем зеленый СИД
btfss PORTB,STOP; ЕСЛИ нажата кнопка СТОП,
bcf PORTB,YELLOW; ТО включаем желтый СИД
btfss PORTB,SETT; ЕСЛИ нажата кнопка УСТ.,
bcf PORTB,RED; ТО включаем красный СИД
btfss PORTB,GO; ЕСЛИ нажата кнопка ПУСК,
bcf PORTA,BUZ; ТО включаем пищалку
; Теперь по очереди включаем все сегменты на обоих индикаторах
movf TEMP,w; Берем маску
movwf DATA_OUT_L; Копируем в регистры последовательной
movwf DATA_OUT_H; передачи
call SPI_WRITE; Передаем ее
btfsc PORTB,DIAG; ЕСЛИ кнопка ПАУЗА отпущена,
return; ТО выходим из процедуры самодиагностики
clrf NEW_SEC; Сбрасываем флаг секунды
; Теперь сдвигаем маску для индикаторов и ждем 1 секунду
bcf STATUS,С; Сбрасываем бит переноса
btfsc TEMP,7; Проверяем старший бит маски
bsf STATUS,С; ЕСЛИ 1, ТО устанавливаем флаг переноса
rlf TEMP,f; Вдвигаем его в регистр
D_LOOP2 movf NEW_SEC,f; Ждем секунду
btfsc STATUS,Z; ЕСЛИ флаг не равен нулю, ТО пропускаем
goto D_LOOP2; ИНАЧЕ пробуем снова
goto D_LOOP; Повторяем процедуру
Кнопки
Поочередно проверяются все пять кнопок, подключенных к порту В. При замыкании кнопки включается либо один из СИД, либо звуковой излучатель. Таким образом, проверяется состояние кнопок и соответствующих органов индикации. Разумеется, работоспособность кнопки ДИАГ. проверяется по переходу системы в режим диагностики, а работоспособность кнопки СБРОС — по запуску процесса инициализации.
Если число органов управления в устройстве больше числа органов индикации, то можно либо включать определенные комбинации индикаторов, либо задействовать для этой цели один или более сегментов 7-сегментного индикатора.
СИД и звуковой излучатель
Устройства вывода статической информации проверяются совместно с кнопками, как описано выше. Разумеется, отсутствие свечения у СИД или звука у излучателя может быть обусловлено неисправностью как входного, так и выходного узла. Какой именно из узлов неисправен, достаточно легко выясняется при помощи вольтметра или логического пробника. Помните также, что светодиоды должны светиться во время установки временного интервала.
Дисплей
Каждый из индикаторов дисплея проверяется путем поочередного включения одного из сегментов с периодом в одну секунду. Это реализуется формированием значения с «бегущим нулем» (b’11111110’ —> Ь’11111101’ —> … —> b’01111111’), которое передается подпрограммой SPI_WRITE при каждом ненулевом значени регистра NEW_SEC. Данный регистр инкрементируется в обработчике прерывания от Таймера 0 при каждом инкрементировании счетчика секунд и сбрасывается в процедуре диагностики. То есть он играет роль храпового механизма, обеспечивая вывод каждого нового символа не ранее чем через секунду.
Процесс установки интервала запускается в том случае, если при выходе микроконтроллера из состояния сброса кнопка УСТ. оказывается замкнутой. Данный процесс предназначен для того, чтобы оператор мог изменить содержимое ячейки EEPROM с адресом h’00’ на любое значение от 1 до 99. В этой ячейке хранится начальное значение, используемое основным процессом для определения длительности процедуры счета.
В этой подпрограмме, код которой приведен в Программе 17.5, сначала в счетчик секунд записывается число 99, которое затем декрементируется с периодом в одну секунду в соответствии с логикой работы обработчика прерывания. Содержимое регистра SECOND передается в подпрограмму вывода на дисплей каждый раз, когда обработчик прерывания записывает в регистр-флаг NEW_SEC ненулевое значение, т. е. каждую секунду. В подпрограмме DISPLAY регистр NEW_SEC обнуляется, благодаря чему обновление дисплея происходит опять же только раз в секунду. Кроме того, каждую секунду проверяется состояние кнопки УСТ. — при ее размыкании состояние счетчика секунд сохраняется в EEPROM в секции UPDATE вызовом подпрограммы низкого уровня EE_PUT из Программы 15.2 (стр.547).
Программа 17.5. Процедура установки временного интервала
; *****************
; * ФУНКЦИЯ: Медленно считает от 99 до 00. При отпускании
; * кнопки УСТ. в EEPROM заносится новое значение интервала
; * РЕСУРСЫ: Пп DISPLAY, EE_PUT, ISR; переменная TIME_OUT *
; * ВХОД: Кнопка УСТ. нажата *
; * ВЫХОД: Обновляется содержимое EEPROM по адресу 00 *
; *****************
SET_TIME movlw d’99’; Начинаем счет со значения 99
movwf SECOND
movlw b’00000000’; Включаем все СИД
movwf PORTB
SET_LOOP movf SECOND,w; Берем значение счетчика секунд
call OUTPUT; и выводим его на дисплей
btfsc PORTB,SETT; Проверяем, не надо ли прекратить счет?
goto UPDATE; ЕСЛИ да, ТО обновляем EEPROM и выходим
movf SECOND,w; Берем отображаемое число
movwf TIME_OUT; Делаем временную копию
SLOOP movf NEW_SEC,f; Проверяем флаг секунды
btfsc STATUS,Z; ЕСЛИ не ноль, ТО пропускаем
goto S_LOOP; ИНАЧЕ проверяем снова
goto SET_LOOP; Повторяем
UPDATE movf TIME_OUT,w; Берем значение
movwf EEDATA; Инициализируем EEPROM
clrf EEADR
call EE_PUT; Пишем в EEPROM
return; и возвращаемся в основную программу
Полная блок-схема алгоритма работы фоновой программы приведена на Рис. 17.3. На данной схеме показана процедура выбора требуемого процесса после сброса, а также подробная структура основной фоновой процедуры. Хотя эта блок-схема кажется довольно сложной, ее можно разбить на пять фаз, код которых приведен в Программе 17.6.
Рис. 17.3. Блок-схема основной фоновой процедуры
Программа 17.6. Основная фоновая процедура
movlw b’11000000’; Включаем зеленый СИД
movwf PORTB
bsf PORTA,BUZ; Выключаем звук
; Считываем начальное значение из EEPROM
clrf EEADR; Адрес в EEPROM — 00
call EE_GET; Считываем начальное значение
movwf MINUTE
movlw d’59’; Начальное значение секунд
movwf SECOND; равно 59
clrf JIPPY
DISPLAY movf MINUTE,w; Берем значение минут
call OUTPUT; Выводим его на дисплей
; Фаза 2-минутной готовности ------------
; За две минуты до конца включаем звук на одну секунду и включаем
; желтый светодиод
TWO movf MINUTE,w; Счетчик минут =2?
addlw -2
btfss STATUS,Z
goto ONE; ЕСЛИ нет, ТО проверим след, фазу
movlw b’10100000’; Включаем желтый СИД
movwf PORTB
bcf PORTA,BUZ; Включаем пищалку
TWO_LOOP movf NEW_SEC,f; Проверяем флаг NEW_SEC
btfsc STATUS,Z; ЕСЛИ не ноль, ТО пропускаем
goto TWO_LOOP; ИНАЧЕ проверяем снова
bsf PORTA,BUZ; Выключаем пищалку через 1 секунду
goto REPEAT; Выводим интервал на дисплей
; Фаза 1-минутной готовности —
; За одну минуту до конца включаем звук на две секунды и включаем красный светодиод
ONE movf MINUTE,w; Счетчик минут = 1?
addlw -1
btfss STATUS,Z
goto ZERO; ЕСЛИ нет, ТО проверим след, фазу
movlw b’01100000’; Включаем красный СИД
movwf PORTB
bcf PORTA,BUZ; Включаем пищалку
ONE_LOOP movf NEW_SEC,f; Проверяем флаг NEW_SEC
btfsc STATUS,Z; ЕСЛИ не ноль, ТО пропускаем
goto ONE_LOOP; ИНАЧЕ проверяем снова
clrf NEW_SEC; Сбрасываем флаг NEW_SEC
UN_LOOP movf NEW_SEC,f; Проверяем флаг NEW_SEC
btfsc STATUS,Z; ЕСЛИ не ноль, ТО пропускаем
goto UN_LOOP; ИНАЧЕ проверяем снова
bsf PORTA,BUZ; Выключаем пищалку через 2 секунды
goto REPEAT; Выводим интервал на дисплей
; Фаза тайм-аута —
; Когда счетчик минут становится равным нулю, включаем пищалку
; до тех пор, пока не будет нажата кнопка СТОП
ZERO movf MINUTE,f; Счетчик минут =0?
btfss STATUS,Z
goto REPEAT; ЕСЛИ нет, ТО повторим проверку через
bcf PORTA,BUZ; Включаем пищалку
ZERO_LOOP
btfsc PORTB,STOP; Проверяем кнопку СТОП
goto ZERO_LOOP; и продолжаем, пока не будет нажата
FINI movlw b’11100000’; Выключаем индикаторы
movwf PORTB
bsf PORTA,BUZ; и пищалку
movlw b’11111111’; Код для очистки индикаторов
movwf DATA_OUT_L
movwf DATA_OUT_H
call SPI_WRITE; Очищаем оба индикатора
sleep; и ждем следующего сброса
REPEAT btfss PORTB,STOP; Проверяем кнопку СТОП
goto FINI; ЕСЛИ нажата, ТО прекращаем работу
movf SECOND,f; Ждем обнуления счетчика секунд,
btfss STATUS,Z; т. е. наступления следующей минуты
goto REPEAT; ЕСЛИ нет, ТО ждем дальше
clrf NEW_SEC; ИНАЧЕ ждем еще секунду
R_LOOP movf NEW_SEC,f; Проверяем флаг NEW_SEC
btfsc STATUS,Z; ЕСЛИ не ноль, ТО пропускаем
goto R_LOOP; ИНАЧЕ проверяем снова
goto DISPLAY; Повторяем вывод на дисплей
Преамбула
Если в момент сброса не нажата ни кнопка УСТ., ни кнопка ДИАГ., то управление переходит к основной программе, обозначенной меткой MAIN_PROC. В этой секции осуществляется считывание значения отсчитываемого периода из ячейки EEPROM с адресом h’00’ и инициализация счетных регистров. Зеленый СИД включается, а остальные световые индикаторы и звуковой излучатель выключаются.
Обратный отсчет
В фазе обратного отсчета осуществляется периодический ВЫВОД на дисплей значения счетчика минут — обновление дисплея осуществляется в прерывании. Зеленый СИД остается во включенном состоянии до тех пор, пока на дисплее не появится число
Всегда, за исключением времени обработки нажатия кнопки СТОП, на дисплей выводится значение счетчика минут. В секции REPEAT проверяется значение счетчика секунд, и, если он равен нулю, цикл повторяется, т. е. период повторения равен одной минуте. Использование более простого решения, заключающегося в непрерывном обновлении 7-сегментных индикаторов, привело бы к ухудшению изображения, поскольку данные, постоянно передаваемые по последовательному интерфейсу, могли бы вызывать кратковременную засветку лишних сегментов, которые должны быть выключены. Кроме того, период повторения цикла, равный одной минуте, упрощает кратковременное включение звукового излучателя при равенстве счетчика минут двум и единице.
Две минуты до конца
Когда на дисплее появляется число
Одна минута до конца
Когда на дисплее высвечивается
Тайм-аут
Когда счетчик минут становится равным нулю, на дисплей выводится
После ассемблирования исходного кода программы и, быть может, даже после симуляции (см. Рис. 8.7 на стр. 265) полученный шестнадцатеричный код можно загрузить в память программ. На первом этапе достаточно реализовать только программу диагностики и связанные с ней подпрограммы, чтобы проверить правильность монтажа устройства. Подробности процесса программирования в значительной степени зависят от используемого программатора и его программного обеспечения.
На снимке экрана, показанном на Рис. 17.4, изображено окно ИСР MPLAB при работе с программатором PICSTART Plus® компании Microchip (см. Рис. 17.5). К персональному компьютеру программатор подключается через последовательный порт RS-232, а установление соединения осуществляется из пункта меню Picstart Plus. Окно, показанное в правой части экрана, позволяет оператору задать требуемые значения битов конфигурации (см. левое нижнее окно). После этого оператор может выполнить команды Blank out (очистить), Read from (считать), Program (запрограммировать) или Verify (верифицировать). Последняя команда позволяет убедиться, что содержимое EEPROM или памяти программ идентично коду, сгенерированному в текущем проекте. Обращаю ваше внимание, что верификацию можно выполнить только в том случае, если защита кода выключена. Если же при программировании контроллера была включена защита кода, то после завершения программирования нельзя будет выполнить ни команду Verify, ни последующие команды Program.
На среднем окне отображается состояние процесса программирования или верификации. В данном случае сообщается, что была запрограммирована память программ до ячейки с адресом h’03FF’ и что верификация прошла успешно. Весь этот процесс занимает меньше минуты, а размер нашей программы оказался равен 254 словам памяти программ.
При использовании микроконтроллеров PIC, имеющих в обозначении букву «F», процесс программирования можно повторить несколько тысяч раз без ухудшения параметров FLASH-памяти программ. Микроконтроллеры с индексом «С»[197], такие как PIC16C74, имеют память программ EPROM-типа. Если в корпусе микросхемы имеется кварцевое окошко (см. фотографию на стр. 15), то перед повторным программированием содержимое памяти программ необходимо стереть, подвергая кристалл действию ультрафиолетового излучения в течение примерно 20 мин. Хотя модели с кварцевым окошком необходимы для разработки устройств на базе микроконтроллеров С-серии, они все-таки достаточно дороги.
Рис. 17.4. Программирование микроконтроллера из ИСР MPLAB 5-й версии
Рис. 17.5. Фирменный программатор PICSTART Plus компании Microchip
Поэтому в производстве используются более дешевые варианты, называемые однократно-программируемыми (One-Time Programmable — OTP), поскольку их стереть невозможно. Изделия с кварцевым окошком в корпусе отличаются суффиксом «JW» в обозначении. Например, PIC16C74B—20/JW является микроконтроллером PIC16C74B в керамическом корпусе с окошком, a PIC16C74B—4/Р — однократно-программируемым исполнением того же микроконтроллера с максимальной частотой 4 МГц в 40-выводном корпусе типа DIP. Удостоверьтесь, что вы заказываете правильное устройство!
Устройство, схему и программное обеспечение которого мы только что разработали, представляет собой достаточно простой пример, в котором мы попытались собрать воедино различные методики, изучавшиеся нами на протяжении всей книги. Если вы решите изготовить это устройство, то на Web-сайте книги к вашим услугам имеются исходные файлы (в том числе и вариант на языке Си), сравнение с аналогичной конструкцией на микроконтроллере 68000 фирмы Motorola, а также много других идей для экспериментирования. Удачи!