Рис. 5: Включение ЖКИ как блока внешней памяти
Сравнивая ее с диаграммой сигналов записи данных в контроллер ЖКИ (рис. 2), не трудно заметить, что сигналы E и WR имеют разную полярность. Различаются также временные соотношения этих сигналов с сигналами на шине данных. Поэтому узел формирования сигнала E на рис. 5 имеет дополнительный инвертор и дифференцирующую цепочку, укорачивающую импульс записи.
В качестве сигнала выбора регистра RS в рассматриваемой схеме используется еще один разряд адреса - A14. Таким образом, операции записи в контроллер ЖКИ команд или данных отличаются между собой только адресом. Например, данные можно записывать по адресу $C000, а команды - по адресу $8000.
Вывод R/W ЖКИ в предлагаемой схеме непосредственно присоединен к линии "земля", тем самым задается единственно возможное направление передачи информации от AVRмикроконтроллера на ЖКИ. При этом потеря возможности опрашивать BUSY-флаг и считывать данные из блоков памяти контроллера ЖКИ окупается экономией одного управляющего вывода AVR.
Рассмотренная схема требует минимальных программных затрат на обслуживание ЖКИ, но, очевидно, годится только для AT90S4414, AT90S8515 и megaAVR, которые допускают подключение внешней памяти.
На рис. 7 изображена схема подключения ЖКИ, которую можно использовать для AVRмикроконтроллеров, не имеющих возможности подключения внешней памяти данных. В этой схеме управляющие сигналы E и RS формируются программно на обычных линиях ввода/вывода AVR. В приведенном примере шина данных состоит из 4 разрядов. Каждый байт данных при этом, как упоминалось выше, передается за две последовательные
посылки, начиная со старшей тетрады.
Рис. 6: Запись данных во внешнюю память AVR-микроконтроллера
Рис. 7: Подключение ЖКИ при помощи 6 цифровых выводов
3. Программирование вывода информации на ЖКИ
Драйвером какого-либо аппаратного узла называют набор подпрограмм, которые учитывают все аппаратные особенности схемы включения этого узла и максимально облегчают работу с ним головной программе.
Простейшими составными "кирпичиками" драйвера ЖКИ могут быть подпрограммы, перечисленные в таблице 4 и приведенные на рис. 9 и рис. 10. Первые две из них ("icom_XXXX" и "idat_XXXX") для вывода в контроллер индикатора байта команды и байта данных реализуют рассмотренные ранее (рис. 2) временные диаграммы обмена сигналами между контроллером индикатора и AVR-микроконтроллером. Построение этих подпрограмм целиком зависит от схемы включения индикатора.
Таблица 4: Базовые подпрограммы вывода информации на ЖКИ
Назначение подпрограммы (пп)
Вывод байта-команды в контролер ЖКИ
Вывод байта данных в контроллер ЖКИ
Название пп для схемы на рис. 5

icom_8515

idat_8515
Название пп для схемы на рис. 7

icom_2313

idat_2313
Инициализация ЖКИ |
initlcd_8515 |
initlcd_2313 |
|
|
|
Подпрограмма инициализации контроллера ЖКИ "initlcd_XXX", посылая в индикатор последовательность команд при помощи подпрограммы "icom_XXXX", задает режим работы ЖКИ. Эта подпрограмма в дальнейшем при программировании реальной задачи может дополняться зависимыми от этой задачи фрагментами. Например, в подпрограмме инициализации удобно выполнить вывод постоянного (не изменяемого во все время работы программы) текста на дисплей. Здесь же мы будем задавать начальные значения управляющим битам (флагам), при помощи которых драйвер ЖКИ будет обмениваться статусной информацией с головной программой.
Для того, чтобы лучше понять работу перечисленных подпрограмм, рассмотрим программу "runstr" (рис. 11) вывода на ЖКИ бегущей строки. В этой программе показано, как, перепрограммируя начальные ячейки знакогенератора, можно при помощи нерусифицированного ЖКИ выводить на дисплей русские буквы. Обратите внимание на то, что выводимый текст хранится во flash-памяти программ в виде таблицы. При извлечении данных из этой таблицы указатель адреса приходится умножать на 2. Это происходит из-за того, что память программ, в которой размещается таблица выводимых символов, имеет 16-разрядные ячейки, в каждой из которых хранится по два ASCII-кода данных. Задавая начальный адрес таблицы при помощи директивы .org, мы задаем адрес 16-разрядной ячейки. Но для команды LPM извлечения байта данных из памяти программ требуется задать адрес байта, который, очевидно, в два раза больше адреса слова.
В программе "runstr" длительность задержек между выводами на индикатор задает сам процессор, подсчитывая количество холостых операций. При этом никакой полезной работы процессор не выполняет. Понятно, что если единственной целью программы является организация бегущей строки, то используемый в программе "runstr" способ задания программных задержек является вполне приемлемым.
Рассмотрим теперь случай, когда AVR-микроконтроллер предназначен для управления каким-либо объектом, а ЖКИ используется для отображения контролируемого параметра, например, частоты, количества импульсов и т. п. В такой реальной задаче программное обеспечение всегда строится с использованием системы прерываний AVRмикроконтроллера. И в этом случае вести отсчет временных интервалов в головной программе становится невозможным, так как не известно, когда и какие происходили прерывания и сколько на их обслуживание потребовалось времени. Поэтому в подобных случаях подсчет временных интервалов возлагают на таймер. А процессор при этом освобождается для решения более интеллектуальных задач.
Схема взаимодействия головной программы и отдельных программных модулей, обслуживающих ЖКИ по прерыванию от таймера, изображена на рис. 8. Тексты соответствующих программ "main", "initlcd" и "tim0_lcd" приведены на рис. 12.
Функцией основной программы "main", так же как и раньше, является задание режима работы ЖКИ в инициализирующей части программы. Во время инициализации, когда прерывания еще не разрешены, можно воспользоваться рассмотренными выше подпрограммами задержки.
В основном цикле головной программы задачей процессора является получение очередного отсчета и сохранение этого отсчета в промежуточном буфере ЖКИ "ind_buf", который организуется в RAM. Сохранение отсчета в RAM происходит приблизительно в 100 раз быстрее, чем его вывод на индикатор, так как при этом головной программе не нужно ждать после записи каждого байта.
Подпрограмма "tim0_lcd", которую можно считать основной частью драйвера ЖКИ, вызывается по прерыванию от таймера 0 с временным интервалом, достаточным для обработки информации внутри контроллера ЖКИ (в приведенном примере около 64 мкс). Задачей подпрограммы "tim0_lcd" является вывод в ЖКИ очередного байта данных из буфера "ind_buf".
Рис. 8: Взаимодействие головной программы и драйвера ЖКИ, работающего по прерыванию
Таким образом, головная программа "main" быстро помещает результат в буфер, а подпрограмма обработки прерывания "tim0_lcd" по мере готовности ЖКИ байт за байтом выводит результат из буфера на индикатор.
Следует иметь в виду, что при использовании прерываний и организации взаимодействия программ через буфер данных, в системе возникают несколько (в нашем случае два) параллельных асинхронных процесса: головная программа работает в своем ритме, зависящем от конкретной задачи, а периодичность подпрограммы обслуживания прерывания определяется быстродействием ЖКИ.
Из-за асинхронности программ возможна ситуация, когда обе программы одновременно начнут обращаться к буферу "ind_buf". Например, после того, как на индикатор выведена только часть результата, головная программа может обновить данные в буфере. И тогда последующие байты, выводимые на индикатор, будут относиться уже к новому отсчету, что, конечно, будет являться недопустимой ошибкой. Для того, чтобы таких ситуаций не возникало, необходимо решить задачу взаимоисключения доступа программ к промежуточному буферу. Для решения этой задачи используют специальные биты управления, которые называют семафорами или флагами. В рассматриваемой программе в качестве такого флага выбран бит PB.0 и принято, что если PB.0=0, то открыт доступ к буферу для головной программы; если PB.0=1, то открыт доступ к буферу для подпрограммы обслуживания прерывания. В самый первый раз флаг устанавливается при инициализации, разрешая доступ к буферу головной программе, а затем каждая
программа, завершив работу с буфером, переключает флаг в противоположное состояние.
В заключение хочется отметить, что в статье рассмотрены только некоторые принципы управления ЖКИ, которые могут быть полезными при разработке программного обеспечения для AVR-микроконтроллеров. В зависимости от конкретной задачи драйвер ЖКИ может быть значительно видоизменен и усовершенствован. Например, можно заставить мигать отдельные поля индикатора постоянно или в зависимости от состояния объекта управления; можно рассматривать буфер как две отдельные страницы памяти между которыми, по команде с клавиатуры, например, можно быстро переключаться; можно организовывать на ЖКИ систему меню и многое другое, что подскажет Вам Ваша фантазия.
Татьяна Кривченко
E-mail: tkr@efo.spb.su