Вы находитесь на странице: 1из 19

4/1.

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


на индикацию (из 24С64А, через ПИК), в русифицированный ЖКИ модуль на
основе HD44780, осмысленный, русскоязычный текст объемом до 8192-х
символов. Принцип вывода на индикацию текстов на русском языке.

В предыдущих разделах, работа велась по "разведывательному" ("накопительному") принципу,


смысл которого заключается в высказывании: "Не зная брода, не лезь в воду".
"Разведка" свою задачу выполнила, вплоть до точного "замера глубин, скорости течения
и погодных условий".
Теперь смело можно "штаны мочить", не опасаясь "безвременной гибели" от
собственной бестолковости, а то ведь часто получается то, что желания опережают
возможности, а от этого добра ждать не приходится.
"Привязываюсь" к наиболее актуальным и распространенным, практическим нуждам,
что, опять же, есть банальная расстановка приоритетов.
И записать байты в EEPROM память 24Схх, и считать байты из EEPROM памяти 24Схх
можно в любом программаторе, поддерживающем этот тип м/схем, но компьютер, как
говорится, "в карман не положишь", да и "слишком жирно это будет".
В большинстве случаев, при помощи программатора, в 24Схх, предварительно (до
подключения 24Схх к ПИКу), записываются некие байты данных, которые, после
подключения 24Схх к ПИКу, с целью реализации заданного конструктором алгоритма
работы устройства, только считываются ПИКом из 24Схх.
Естественно, есть и более "навороченные" случаи, когда в разрабатываемом устройстве
имеет место быть и чтение и запись, но, без четкого уяснения более простого,
"бросаться на эту амбразуру" не стоит: как минимум, можно заполучить “бардальеро”, а
то и "пулю-дуру схлопотать".
Думаю, что Вы согласитесь со мной в том, что любая "гибель", включая и "гибель
смертью храбрых", как-то не очень прельщает, да и не умно это: "штучку" ценой в 10
рублей за рубль не купишь, только, однозначно, нервную систему "изнасилуешь".
А раз это так, то, для начала, при помощи компьютера и программатора, нужно записать
в 24Схх нечто, под что, в дальнейшем будет "ваяться" устройство (и соответственно,
программа).
Так как речь идет о большом массиве байтов, то проще всего, через ПИК, вывести на
индикацию (а в более “навороченных” случаях, на распечатку, как в кассовых
аппаратах) какой-нибудь осмысленный текст большого объема, заранее "зашитый" в
EEPROM память 24Схх.
Кстати, практическая потребность в этом довольно-таки значительна, и существует
весьма обширный класс устройств ("регистраторы событий", "оформители документов",
информирующие устройства, кодовые замки с большим количеством пользователей и
т.п.), в которых сказанное частично или полностью используется.
В данном случае, из подручных средств, я "сваял" информирующее устройство типа
"бегущей строки".
"Бег" происходит в 1-й (верхней) строке ЖКИ модуля 16х2 (HD44780).
Упрощенный принцип работы устройства состоит в "переправке" (при помощи
PIC16F84A) предварительно записанных, в 24С64А, байтов, на индикацию в ЖКИ
модуль.
По такому же принципу (без учета нюансов), можно выводить на индикацию осмысленный
текст и в более габаритные индикаторные устройства.
Чтобы говорить о какой-то "переправке" байтов, нужно иметь эти байты в наличии.

1
В данном случае, числовое значение каждого байта, записанного в EEPROM память
24С64А, соответствует одному визуально отображаемому символу.
Таким образом, в ячейки памяти 24С64А, должны быть записаны числа, с учетом
кодировки, применяемой в таблице знакогенератора HD44780 (о том, как прошить
24Схх сказано в “Самоучителе…”).
Так как "англицкими" символами я уже "сыт по горло", то буду "ваять" бегущую строку
на великом и могучем, русском языке, а заодно, раз уж повод подвернулся, расскажу о
"механике" вывода на индикацию русских символов (для русифицированных ЖКИ
модулей).
В части касающейся HD44780, для удобства, я свел кодировки русскоязычных символов
в одну картинку:

А вот что я зашил в 24С64А:

2
Сразу обращаю Ваше внимание на то, что кодировка русскоязычных символов таблицы
знакогенератора HD44780 не соответствует стандартной кодировке русскоязычных
символов, применяемой в компьютерах (вернее, частично соответствует), и поэтому,
при помощи компьютера, переводить русские символы в бинарный вид, нельзя.
Вернее, технически, сделать-то это можно (нет проблем), но получится "абракадабра"
(не читаемый текст), корректировать которую - себе дороже станет.
Проще взять лист бумаги, написать на нем требуемый текст, и, в соответствии с
приведенной выше таблицей, под/над каждым символом написать его 16-ричный код.
После этого, открываете PonyProg, а в PonyProg открываете какой-нибудь
"слабосильный" HEX файл (чтобы FF было побольше. Я открывал Multi.hex).
Включаете редактирование буфера и вручную, с бумажки (см. выше), "настукиваете"
соответствующие 16-ричные коды.
Процедура, конечно же, довольно-таки муторная, но ничего не поделаешь.
Радует только то, что она одноразовая: разок "попыхтел", зашил все это "добро" в
24С64А, и оно ("добро") будет в нем храниться годы.
Конечно же, можно осуществить перекодировку и в создаваемой программе, но зачем
нужны лишние, тормозные процедуры, исполняемые в каждом цикле вывода строки на
индикацию, когда можно обойтись без них?
На рис. 2 Вы видите то, что я "настучал" в PonyProg, начиная с самого верха (с ячейки с адресом
00h).
Справа, на сером фоне, Вы видите текст, который соответствует этой прошивке, в

3
указанной выше кодировке (см. рис. 1).
Звездочками помечены символы "пусто" (адрес 20h).
Реально же, в PonyProg, справа от прошивки (в раскодировщике), Вы будете видеть не
это, а не читаемый текст.
Это и подтверждает сказанное выше.
Для того чтобы Вам, вручную, не "набивать" прошивку 24С64А, я, в том же PonyProg,
сохранил результат своих усилий, как HEX файл и, на всякий случай, еще и как BIN
файл.
Они прилагаются к этой статье.
В программе PonyProg, открываете любой из этих файлов и записываете эту прошивку в
24С64А.
Только учтите, что созданный, таким способом (в PonyProg), HEX файл прошивки, в
MPLAB открыть нельзя (уйдет в отказ), так как он создан не из ASM файла и не с
помощью
MPLAB.
Замечания по поводу объема выводимого на индикацию текста:
Я не стал "забивать" текстом весь объем памяти 24С64А, а использовал только малую ее
часть.
Если есть желание, то можете это сделать самостоятельно, а заодно и "руку на
кодировке набъете".
Это исключительно полезно, так как позволяет, на сознательном и бессознательном уровне,
прочувствовать сам процесс “трансформации” чисел.
Только учтите, что в опубликованной ниже программе, я поставил "ограничитель"
количества считываемых байтов.
При необходимости наращивания объема текста (вплоть до заполнения всего объема
памяти), выводимого в бегущую строку, нужно увеличить значения констант этого
"ограничителя".
Данные константы задают некую числовую “границу”, при пересечении которой, рабочая
точка программы выходит из группы команд бегущей строки.
В данном случае, рабочая точка программы закольцовывается в “мертвом, вечном кольце” на
все время включения питания (бегущая строка останавливается на надписи “СТОП-
МАШИНА!”).
В этом смысле, программу можно считать одноцикловой, но ничто не мешает сделать ее
многоцикловой, а также добавить в нее дополнительные функции или изменить
существующие функции (или и то, и другое).
А вот теперь прикиньте, сколько всего можно "засунуть" в 512 Кбитную или 1 Мбитную
(а есть и больше) память и какую “свистопляску” можно организовать (а в программе-то
задействовано всего 149 команд)!
Итак, буду считать, что м/схема 24С64А прошита и, тем самым, подготовлена к работе в
устройстве бегущей строки.
Пока, присваиваю ей “статус запчасти” и откладываю в сторону.
Принципиальную схему устройства я "сваял" в таком виде:

4
Сразу обращаю Ваше внимание на одну принципиальную особенность: я не стал
подключать линии SDA и SCL к свободным выводам порта B, а подключил их к выводам
порта А.
Это связано с тем, что, хотя и ПИК "общается" с ЖКИ модулем по 4-разрядному
интерфейсу, но на выводах RB0 ... RB3, при записи байта в ЖКИ модуль, будут
присутствовать уровни битов байта, выводимого на индикацию, которые нарушают
"общение" ПИКа с внешней памятью.
Конечно же, можно "поиграть" с перестройками направлений их работы, но зачем
нужен этот совершенно лишний, программный "геморрой"?
Подключил линии SDA и SCL к выводам порта А, и "дело в шляпе".
К свободным выводам порта В, лучше всего подключать кнопки с нормально
разомкнутыми контактами (если в этом есть необходимость) и организовывать опрос их
состояний тогда, когда рабочая точка программы находится вне ПП вывода данных на
индикацию (в ПП опроса клавиатуры).
Это достаточно легко реализуется. Так, например, сделано в моем ЧМ/ЦШ
(bf_fr_12.asm).
С этим подключением связана еще одна особенность: в том случае, который Вы видите
на картинке, требуется только один внешний, подтягивающий резистор (на линии
SDA).
Если же линию SDA подключить к выводу RA3, а линию SCL - к выводу RA4, то и
линию SCL также придется "подтягивать" к +5в (потребуется 2 подтягивающих
резистора).
Это объясняется тем, что выходной каскад защелки вывода RA4 является "белой
вороной" в том смысле, что не имеет внутренней нагрузки (каскад с открытым стоком).
В этом случае, обязательно необходимо нагрузить его (подтянуть к +5в.) внешним
резистором.
Кнопка "Сброс бегущей строки на начало" это кнопка с нормально разомкнутыми
контактами.
Простейший фильтр нижних частот, подключенный к ней, значительно уменьшает

5
количество импульсов "дребезга контактов", возникающих при коммутации.
При кратковременном нажатии на эту кнопку, происходит сброс программы на начало,
после чего бегущая строка также будет выводиться со своего начала.
Итак, ЖКИ модуль в наличии, прошитая м/схема памяти в наличии, теперь дело только
за ПИКом.
С его помощью, нужно "организовать любовь и согласие" между 24С64А и ЖКИ
модулем.
Требуется составить для него программу, которая будет управлять всем этим
"хозяйством".
Опять расставляю приоритеты.
Что, на данном этапе работы, самое главное?
Нужно однозначно определиться с принципом "ваяния" бегущей строки.
Имеется ЖКИ модуль, с количеством знакомест в строке равным 16-ти.
Можно ли, после вывода на индикацию символа, сдвинуть дисплей на одно знакоместо,
с последующей записью, в освободившееся знакоместо, следующего символа и т.д.?
Безусловно, можно, применив команду сдвига дисплея, но при этом, существует одна
особенность, "губящая на корню все дело".
В ячейках DD RAM, обслуживающих строку, происходит кольцевой сдвиг, что в
переводе на русский, означает: после полного заполнения всех ячеек DD RAM строки
(40 штук) и наличии последующих сдвигов, ранее записанные символы не будут
"гибнуть", а будут повторно (по кольцу) выводиться на индикацию.
Что получится?
А получится самый натуральный "бардак".
Я специально, на практике, проверил сказанное, и получил этот "бардак" во всей своей
"рваной" красе.
Стоп - машина! Этого "счастья" не нужно.
Без кавычек, это слово можно применять только в тех случаях, когда организуется
количество сдвигов, обеспечивающее работу только в пределах одного кольца вывода
данных на индикацию.
Что значит 40 сдвигов?
Капля в море. Бегущую строку нужно “двигать” сотни и тысячи раз.
Без периодической очистки дисплея (установка счетчика адреса в 0 и запись во все
ячейки DD RAM символа "пусто"), не обойтись.
Именно с помощью этого можно обеспечить работу в пределах одного кольца, и
соответственно, вывод в бегущую строку любого количества символов, с гарантией
отсутствия "кольцевых бяк".
Вопрос: "Когда выгоднее всего осуществлять очистку дисплея"?
Ответ: после вывода на индикацию 16-ти символов (заполнения строки) и задержки,
необходимой для “комфортного высвечивания" показаний.
В этом случае, направление заполнения строки символами не имеет значения, так как
это заполнение можно сделать высокоскоростным (очень быстрым и визуально не
воспринимаемым), что и имеет место быть.
После быстрого вывода на индикацию всех 16-ти символов, "высвечиваться" будет сразу
вся строка, причем, в течение интервала времени, достаточного для комфортного,
визуального ее восприятия.
При таком "раскладе", команды сдвига дисплея вообще не нужны, так как
последовательное заполнение строки символами будет происходить за счет
автоинкремента/автодекремента.
Иными словами, речь идет об уже описанном ранее способе заполнения строки 16-ю
символами, примененном в программах bf_fr_ ... asm (вот Вам и основа), но с

6
определенной спецификой.
Ну хорошо, вывел на индикацию в строку 16 символов (“пачку”), а дальше-то что?
Если после вывода на индикацию прерыдущей "пачки" символов, инкрементировать значение
текущего, адресного запроса к соответствующей ячейке памяти м/схемы 24С64А, то в
следующей "пачке" символов, в крайнее левое знакоместо будет выведен не первый, а
второй (относительно предыдущей "пачки" символов) символ, а в крайнее правое
знакоместо, будет выведен "новый" символ, считанный, из памяти 24С64А, так же, как и
предыдущие 15 символов “пачки”.
Проще говоря, первые 15 символов “пачки” сместятся влево на одну позицию
(относительно предыдущей пачки. Крайний левый символ этой “пачки” “гибнет), а в
крайнее правое знакоместо будет выведен на индикацию “новый” символ.
В соответствии со сказанным, выведенный на индикацию, в правое знакоместо, символ,
прежде чем "погибнуть" ("уйти" из левого знакоместа “в небытие”), последовательно
"побывает" во всех знакоместах строки (будет считан из 24С64А и выведен на
индикацию 16 раз).
Вот Вам и бегущая строка.
Таким образом, речь идет о том же самом сдвиге, но только обеспеченном не за счет
применения команды сдвига, а за счет повторного вывода (сопровождаемого “адресной
коррекцией”) на индикацию следующей "пачки" символов.
Осталось только определиться с моментом очистки (сброса) дисплея.
Лучше всего это сделать либо непосредственно перед переходом на новый цикл вывода
на индикацию "пачки" символов, либо сразу же после этого перехода (цикл начинается
с команды очистки дисплея).
Я применил последнее.
Итак, принцип формирования бегущей строки определен и совершенно однозначен.
Можно двигаться дальше.
А далее, имеет место быть "гибрид слона и трепетной лани" (это и есть обычная работа
конструктора по "стыковке" имеющихся, в его распоряжении, "деталек"), который
породила моя буйная, аналитическая фантазия:

;********************************************************************************
; Stroka.asm ВЫВОД ИЗ EEPROM ПАМЯТИ 24С64А/32А НА ИНДИКАЦИЮ В ЖКИ МОДУЛЬ
; БЕГУЩЕЙ СТРОКИ ОБЪЕМОМ ДО 8192 СИМВОЛОВ.
;********************************************************************************
; "Практикум по конструированию устройств на PIC контроллерах".
; Автор: Корабельников Евгений Александрович
; http://ikarab.narod.ru karabea@lipetsk.ru
; Эта программа входит в состав 4-го раздела.
;********************************************************************************
; "Мастер" - PIC16F84A.
; "Помощник" - 24C64A/32А.
; Устройство индикации - ЖКИ модуль на основе HD44780. Работа по 4-разрядному
; интерфейсу.
; Вывод RA3 подключен к линии такта (SCL).
; Вывод RA4 подключен к линии данных (SDA).
; Кварц 4 Мгц. (1м.ц.=1мкс.).
; Объем программы: 149 команд.
;********************************************************************************
LIST p=16F84A ; Установка типа микроконтроллера.
__CONFIG 03FF5H ; Бит защиты выключен, WDT включен,
; стандартный XT - генератор.
;================================================================================

7
; Определение положения регистров специального назначения.
;================================================================================
OptionR equ 01h ; Регистр OptionR - 1-й банк.
PC equ 02h ; Счетчик команд.
Status equ 03h ; Регистр Status.
TrisA equ 05h ; Регистр TrisA - 1-й банк.
TrisB equ 06h ; Регистр TrisB - 1-й банк.
PortA equ 05h ; Регистр управления защелками порта A.
PortB equ 06h ; Регистр управления защелками порта В.
;================================================================================
; Определение названий и положения регистров общего назначения.
;================================================================================
Temp equ 0Ch ; Регистр обработки данных.
Count_Bit equ 0Dh ; Счетчик битов байта.
Read equ 0Eh ; Регистр обработки данных при чтении байта.
Count_Bait equ 0Fh ; Счетчик количества считываемых байтов.
SecL equ 10h ; Младший разряд счетчика паузы.
SecH equ 11h ; Старший разряд счетчика паузы.
Mem equ 12h ; Регистр буферной памяти.
AdrL equ 15h ; Регистр младшего байта адреса.
AdrH equ 16h ; Регистр старшего байта адреса.
;================================================================================
; "Привязка" названий битов регистров специального назначения к номерам битов.
;================================================================================
RP0 equ 5 ; Бит выбора банка.
C equ 0 ; Бит флага переноса-заёма.
SCL equ 3 ; Бит регистра PortA, который формирует такт.
SDA equ 4 ; Бит регистра PortA, который формирует данные
Z equ 2 ; Бит флага нулевого результата.
RW equ 0 ; Бит №0 регистра PortA (вывод RA0-линия RW).
RS equ 1 ; Бит №1 регистра PortA (вывод RA1-линия RS).
E equ 2 ; Бит №2 регистра PortA (вывод RA2-линия E).
BF equ 7 ; Бит №7 регистра PortB.
;================================================================================
; Определение места размещения результатов операций.
;================================================================================
F equ 1 ; Результат направить в регистр.
W equ 0 ; Результат направить в аккумулятор.
;================================================================================
org 0 ; Начать выполнение программы с нулевого
; адреса PC.
goto START ; Переход на начало программы.
;********************************************************************************

;********************************************************************************
; "РАБОЧАЯ ЧАСТЬ" ПРОГРАММЫ.
;********************************************************************************
; "Рабочая" инициализация ЖКИ модуля.
;================================================================================
LCD_INIT movlw b'00101000' ; Установка: 4-разрядный интерфейс, 2 строки,
; 5х7 точек.
call ENTER_BF ; "Плавающая" задержка со стробом под команду
; 00101000.
;----> Возврат по стеку из ПП ENTER_BF.

8
movlw b'00101000' ; Установка: 4-разрядный интерфейс, 2 строки,
; 5х7 точек.
call ENTER_BF ; "Плавающая" задержка со стробом под команду
; 00101000.
;----> Возврат по стеку из ПП ENTER_BF.

movlw b'00001100' ; Установка: дисплей включен, видимое


; отображение курсора выключено.
call ENTER_BF ; "Плавающая" задержка со стробом под команду
; 00001100.
;----> Возврат по стеку из ПП ENTER_BF.

return ; Возврат по стеку.


;================================================================================
; ПП "плавающей" задержки на основе анализа состояния флага занятости BF
; (вариант для 4-разрядного интерфейса).
;================================================================================
ENTER_BF movwf Mem ; Переправка старшего п/байта регистра W на
movwf PortB ; линии RB4...7 (состояния линий RB0...3 не
; важны).
;------------------------------------------
; Запуск в работу старшего п/байта (строб).
;------------------------------------------
nop ; Задержка в 1 м.ц.
bsf PortA,E ; Установка на линии Е "1".
nop ; Задержка в 1 м.ц.
bcf PortA,E ; Установка на линии Е "0".
;------------------------------------------
swapf Mem,W ; Смена п/байтов с сохранением результата
; операции в W.
movwf PortB ; Переправка младшего п/байта регистра W на
; линии RB4...7 (состояния линий RB0...3 не
; важны).
;------------------------------------------
; Запуск в работу младшего п/байта (строб).
;------------------------------------------
nop ; Задержка в 1 м.ц.
bsf PortA,E ; Установка на линии Е "1".
nop ; Задержка в 1 м.ц.
bcf PortA,E ; Установка на линии Е "0".
;-------------------------------------------------
; Проверка состояния флага занятости BF.
;-------------------------------------------------
; Подготовка к проверке.
;------------------------
bsf Status,RP0 ; Переход в 1-й банк.
movlw b'11110000' ; Запись в W "11110000"
movwf TrisB ; RB4...7 работают на вход, а RB0...3 работают
; на выход.
bcf Status,RP0 ; Переход в 0-й банк.

bcf PortA,RS ; Установка на линии RS "0" (режим команд).


bsf PortA,RW ; Линия RW в "1" (режим чтения данных).
nop ; Задержка в 1 м.ц.
bsf PortA,E ; Установка на лини Е "1".

9
POVTOR nop ; Задержка в 1 м.ц.
;-----------------------
; Сама проверка.
;-----------------------
btfsc PortB,BF ; Проверка состояния флага занятости BF.
goto POVTOR ; Если BF=1, то продолжение задержки до тех
; пор, пока BF не установится в "0" (программа
; исполняется далее).
;-----------------------
; Завершение процедуры.
;-----------------------
clrf PortA ; Сброс в "0" всех защелок порта А
; (RW=0, RS=0, E=0).
bsf Status,RP0 ; Переход в 1-й банк.
clrf TrisB ; Все выводы порта В работают на выход.
bcf Status,RP0 ; Переход в 0-й банк.

return ; Возврат по стеку.


;================================================================================
; Подготовительные операции.
;================================================================================
START clrf PortA ; Сброс защелок порта A в "0"
; (RS=0, RW=0, E=0).

bsf Status,RP0 ; Переход в 1-й банк.


clrf TrisB ; Все выводы порта В работают на вход.
clrf TrisA ; Все выводы порта А работают на выход.
movlw b'11111101' ; Предделитель с Кдел.=32 подключен к WDT,
movwf OptionR ; остальное не существенно.
bcf Status,RP0 ; Переход в 0-й банк.

clrf AdrL ; Установка


clrf AdrH ; нулевого адреса.

call LCD_INIT ; Условный переход в процедуру "рабочей"


; инициализации.
;----> Возврат по стеку из ПП LCD_INIT.
;================================================================================
; Начало цикла вывода на индикацию 16-ти байтов данных, считываемых из 24С64А.
;================================================================================
CYCLE movlw b'00000001' ; Установка: очистка дисплея со сбросом
; данных, установка курсора в начало 1-й
; строки.
call ENTER_BF ; "Плавающая" задержка со стробом под команду
; 00000001.
;----> Возврат по стеку из ПП ENTER_BF.
;------------------------------------------
; Формирование старт-условия.
;------------------------------------------
bsf PortA,SCL ; SCL=1.
bsf PortA,SDA ; SDA=1.

movlw .16 ; Запись в регистр Count_Bait количества


movwf Count_Bait ; байтов, считываемых из 24С64А за один цикл.
;********************************************************************************

10
; ПРОЦЕДУРА ЧТЕНИЯ
;********************************************************************************
; Формирование старт-условия.
;================================================================================
bcf PortA,SDA ; SDA=0.
bcf PortA,SCL ; SCL=0.
;================================================================================
; "Администраторская" ПП (без названия), "рулящая" очередностью обработки байтов.
;================================================================================
; Задание числового значения режимного байта (для записи).
;---------------------------------------------------------
movlw B'10100000' ; Запись в регистр Temp режимного байта (1010-
movwf Temp ; код функциональности, 000–адрес м/схемы,
; 0–режим записи).
call BAIT_WR ; Условный переход в ПП передачи байта
; BAIT_WR.
;-----> Возврат по стеку из ПП BAIT_WR.
;---------------------------------------------------------
; Задание числового значения старшего байта адреса.
;---------------------------------------------------------
movf AdrH,W ; Запись в регистр Temp
movwf Temp ; младшего байта адреса.
call BAIT_WR ; Условный переход в ПП передачи байта
; BAIT_WR.
;-----> Возврат по стеку из ПП BAIT_WR.
;---------------------------------------------------------
; Задание числового значения младшего байта адреса.
;---------------------------------------------------------
movf AdrL,W ; Запись в регистр Temp
movwf Temp ; младшего байта адреса.
call BAIT_WR ; Условный переход в ПП передачи байта
; BAIT_WR.
;-----> Возврат по стеку из ПП BAIT_WR.
;================================================================================
; Формирование стоп-условия.
;================================================================================
bcf PortA,SDA ; SDA=0.
bsf PortA,SCL ; SCL=1.
bsf PortA,SDA ; SDA=1.
nop ; Задержка 1 м.ц.
;--------------------------------------------------------------------------------
; Формирование старт-условия.
;================================================================================
bcf PortA,SDA ; SDA=0.
bcf PortA,SCL ; SCL=0.
;---------------------------------------------------------
; Задание числового значения режимного байта (для чтения).
;---------------------------------------------------------
movlw B'10100001' ; Запись в регистр Temp режимного байта (1010-
movwf Temp ; код функциональности, 000–адрес м/схемы,
; 1–режим чтения).
call BAIT_WR ; Условный переход в ПП передачи байта
; BAIT_WR.
;-----> Возврат по стеку из ПП BAIT_WR.
;********************************************************************************

11
; Подпрограмма приема байта.
;********************************************************************************
SNOVA_2 movlw .8 ; Запись в счетчик битов количества
movwf Count_Bit ; битов в одном байте.

bsf Status,RP0 ; Переход в 1-й банк.


bsf TrisA,SDA ; Вывод SDA работает на вход.
bcf Status,RP0 ; Переход в 0-й банк.
;================================================================================
; Прием текущего бита.
;================================================================================
SNOVA_1 bcf Status,C ; Сброс в 0 флага переноса-заема (С).

bsf PortA,SCL ; SCL=1

btfsc PortA,SDA ; На линии SDA 0 или 1?


bsf Status,C ; Если 1, то С=1.
bcf PortA,SCL ; Если 0, то С=0 (см. выше), а SCL=0.

rlf Read,F ; Циклический сдвиг влево. Этим обеспечивается


; обработка битов байта, начиная с бита
; старшего разряда (№7...№0).
decfsz Count_Bit,F ; Декремент счетчика битов (Count_Bit-1=...).
goto SNOVA_1 ; Если результат не=0, то переход на чтение
; следующего бита.
;================================================================================
; Формирование сигнала АСК (вырабатывает "мастер").
;================================================================================
; Начало формирования сигнала АСК.
;-----------------------------------------------------
bcf PortA,SDA ; SDA=0

bsf Status,RP0 ; Переход в 1-й банк.


bcf TrisA,SDA ; Вывод SDA работает на выход.
bcf Status,RP0 ; Переход в 0-й банк.
;-----------------------------------------------------
; Формирование импульса 9-го такта под сигнал АСК.
;-----------------------------------------------------
movlw .1 ; Анализ содержимого счетчика байтов,
subwf Count_Bait,W; с целью формирования сигнала NO ACK
btfsc Status,Z ; после считывания последнего байта.
goto $+4 ; Если байт не последний, то переход
; на команду movf Read,W.
bsf PortA,SCL ; SCL=1
nop ; Задержка на 1 м.ц.
bcf PortA,SCL ; SCL=0
;********************************************************************************
; Вывод символа на индикацию.
;********************************************************************************
movf Read,W ; Запись считанного байта в регистр W.

bsf PortA,RS ; Вывод содержимого регистра W


call ENTER_BF ; на индикацию.
;----> Возврат по стеку из ПП ENTER_BF.
;----------------------------------------------------------------------

12
; Декремент содержимого счетчика считываемых байтов.
;----------------------------------------------------------------------
decfsz Count_Bait,F; Декремент с ветвлением.
goto SNOVA_2 ; Если результат не=0, то читается следующий
; байт.
; Если результат =0, то программа исполняется
; далее.
;================================================================================
; Задержка, обеспечивающая "штатный" поворот жидких кристаллов и их фиксацию
; (для удобства визуального контроля при чтении текста бегущей строки).
;================================================================================
movlw .255 ;
movwf SecL ;
movlw .255 ;
movwf SecH ; Стандартный, 2-байтный
WR clrwdt ; вычитающий счетчик с
goto $+1 ; "врезкой" в него
decfsz SecL,F ; команд clrwdt и goto $+1.
goto WR ;
decfsz SecH,F ;
goto WR ;
;----------------------------------------------------------------------
; Подготовка к переходу на считывание байта из следующей ячейки 24С64А.
;----------------------------------------------------------------------
incf AdrL,F ; Увеличение на 1 текущего адреса.
movlw .0 ; Вычитание нуля из
subwf AdrL,W ; содержимого регистра AdrL.
btfsc Status,Z ; Результат вычитания равен или нет нулю?
incf AdrH,F ; Если =0, то AdrH+1.
; Если не=0, то программа исполняется далее.
;----------------------------------------------------------------------
; Сверка с заданным числом циклов вывода на индикацию
; 16-ти байтов данных (в данном случае - 450 циклов).
;----------------------------------------------------------------------
movlw .1 ; Если в AdrH лежит число отличное
subwf AdrH,W ; от 1, то переход в ПП CYCLE.
btfss Status,Z ; Если в AdrH лежит число =1, то производится
goto CYCLE ; анализ содержимого регистра AdrL.
movlw .192 ; Если в AdrL лежит число отличное
subwf AdrL,W ; от 192, то переход в ПП CYCLE.
btfss Status,Z ; Если в AdrL лежит число =192, то
goto CYCLE ; программа исполняется далее.
;================================================================================
; Формирование стоп-условия.
;================================================================================
bsf PortA,SCL ; SCL=1.
bsf PortA,SDA ; SDA=1.
;================================================================================
; Завершение полного цикла программы.
;================================================================================
clrwdt ; Сброс WDT.
goto $-1 ; Уход в "мертвое", "вечное кольцо" до
; выключения и последующего включения питания.
;********************************************************************************
; Подпрограмма передачи байта.

13
;********************************************************************************
BAIT_WR movlw .8 ; Запись в счетчик битов количества
movwf Count_Bit ; битов в одном байте.
;================================================================================
; Передача текущего бита
;================================================================================
SNOVA rlf Temp,F ; Циклический сдвиг влево. Этим обеспечивается
; обработка битов байта, начиная с бита
; старшего разряда (№7...№0).
bcf PortA,SDA ; SDA=0.

btfsc Status,C ; Что "ушло" в бит С?


bsf PortA,SDA ; Если в бите С 1, то SDA=1.
bsf PortA,SCL ; SCL=1. (Если в бите С 0, то предыдущая
; команда не исполняется. Вместо нее –
; "виртуальный" NOP).
bcf PortA,SCL ; SCL=0.
decfsz Count_Bit,F ; Декремент счетчика битов (Count_Bit-1=...).
goto SNOVA ; Если результат не=0, то переход на обработку
; следующего бита.
;********************************************************************************
; Группа команд формирования импульса 9-го такта и анализа состояний флага АСК.
;********************************************************************************
; Начало формирования импульса 9-го такта.
;--------------------------------------------------------------------------------
bsf PortA,SCL ; Формируется строб (перепад от 0 к 1)
; импульса 9-го такта.
;--------------------------------------------------------------------------------
; Подготовка к анализу состояний флага АСК (перестройка направления работы
; вывода RA3).
;--------------------------------------------------------------------------------
bsf Status,RP0 ; Переход в 1-й банк.
bsf TrisA,SDA ; Вывод SDA работает на вход.
bcf Status,RP0 ; Переход в 0-й банк.
;================================================================================
; Анализ состояний флага АСК ("плавающая" задержка).
;================================================================================
ACK btfsc PortA,SDA ; На линии SDA 0 или 1 ?
goto ACK ; Если 1, то снова анализ (задержка до
; появления 0).
;--------------------------------------------------------------------------------
; Конец формирования импульса 9-го такта.
;--------------------------------------------------------------------------------
bcf PortA,SCL ; Если 0, то формируется спад импульса
; 9-го такта.
;--------------------------------------------------------------------------------
; Обратная перестройка направления работы вывода RA3.
;--------------------------------------------------------------------------------
bsf Status,RP0 ; Переход в 1-й банк.
bcf TrisA,SDA ; Вывод SDA работает на выход.
bcf Status,RP0 ; Переход в 0-й банк.

return ; Возврат по стеку в "администраторскую" ПП.


;********************************************************************************
end ; Конец программы.

14
Работа программы Stroka.asm

В подготовительных операциях ПП START, все выводы портов настраиваются на работу "на


выход", подготавливается к работе WDT и, по умолчанию, предварительно, выбирается
адрес 1-й ячейки памяти 24С64А (00h), начиная с которой, из внешней памяти, будут
считываться байты данных.
Далее осуществляется переход по стеку в процедуру "рабочей" инициализации ЖКИ
модуля.
Команду очистки дисплея я из нее убрал и "врезал" ее в начало цикла вывода на
индикацию 16-ти байтов данных ("пачки").
Это объясняется тем, что очистка дисплея должна производиться не один раз, а
многократно, то есть, на каждом "витке" этого цикла.
После завершения процедуры "рабочей" инициализации ЖКИ модуля и возврата по
стеку, начинается упомянутый выше цикл.
В его начале, дисплей очищается (команда 0000 0001 - в счетчике адреса ЖКИ модуля
устанавливается нулевой адрес, и во все ячейки записывается символ "пробел"), что есть
подготовительная операция.
Далее, подготавливается к работе "связка мастер - помощник", то есть, формируется
старт-условие.
Далее, определяется количество байтов, считываемых (и выводимых на индикацию) из
24С64А за один цикл работы ПП CYCLE (16 штук - количество знакомест в строке
дисплея).
Теперь можно читать.
"Механизм" процедуры чтения описан ранее. Повторяться не буду.
Расскажу только о специфике.
В связи с большим объемом символов, выводимых на индикацию и наличием
"ограничителя" (подробнее о нем - ниже), на регистрах общего назначения AdrL и
AdrH, организован 2-х разрядный счетчика адреса (не путать со счетчиком адреса ЖКИ
модуля).
Соответственно, при формировании адресного запроса, именно их содержимое и
переписывается (последовательно) в регистр W.
Далее, вплоть до записи считанного байта в регистр W (movf Read,W), отрабатывается,
описанная в предыдущем разделе, процедура чтения.
Повторяться не буду.
После этого, считанный из 24С64А байт данных, стандартным образом, выводится на
индикацию в ЖКИ модуль (см. “Самоучитель…”).
После этого (после возврата по стеку из ПП ENTER_BF), содержимое счетчика байтов
(Count_Bait) декрементируется и, если результат декремента не равен 0, то считывается
следующий байт.
Предположим, что считались (и вывелись на индикацию) все 16 байтов "пачки" (это
происходит быстро).
После этого обязательно нужно сформировать паузу для того, чтобы выведенные на
индикацию 16 символов, "глаза помозолили" (вспомните про инерционность жидких
кристалов).
Это и имеет место быть.
Изменяя длительность задержки, можно изменять скорость движения бегущей строки.
В данном случае, задана такая скорость, которая обеспечивает комфортное чтение.
Далее, с целью последующего считывания и вывода на индикацию следующей "пачки"
из

15
16-ти байтов, смещенной влево на одну позицию (относительно предыдущей "пачки"),
осуществляется инкремент содержимого младшего байта адреса (incf AdrL,F).
Так как, в данном случае, имеются 2 байта адреса, то, при смене .255 на .0 в младшем
байте адреса, нужно обеспечить инкремент старшего байта адреса.
Стандартную, программную организацию такого взаимодействия Вы и видите.
Далее, имеет место быть то, что выше я назвал "ограничителем".
Если его не будет, то считывание (и вывод на индикацию) никогда не остановится (пока
включено питание).
Соответственно, в данном случае, после осмысленного текста, вплоть до считывания
байта из последней ячейки внешней памяти, будут считаны и выведены на индикацию
символы черных квадратов (кодировка - FFh), и выводиться они будут долго и упорно.
Поэтому, как только число считанных и выведенных на индикацию байтов достигает
какой-то вполне определенной величины, я "отфутболил" рабочую точку программы
(через стоп-условие) в процедуру завершения полного цикла программы.
Как только это происходит, движение бегущей строки прекращается на словах "СТОП-
МАШИНА!".
При помощи группы команд "ограничителя", организуется процедура сверки
количества считанных байтов с числом .448.
448 = 464 (общее количество символов текста, выводимого в бегущую строку) - 16.
Это число разлагается так: 00000001 11000000, что соответствует числам .1 (старший
байт) и .192 (младший байт).
С этими числами, последовательно, и происходит сверка.
Если количество считанных байтов меньше чем 448, то рабочая точка программы
"отфутболивается" на начало цикла считывания и вывода на индикацию следующей
"пачки" из 16-ти байтов (goto CYCLE).
При изменении количества выводимых в бегущую строку символов, нужно
соответствующим образом изменить и величины констант "ограничителя".
Можно понапридумать много кое-чего, например, подключить к выводам порта В
(RB0...3) 4 кнопки (или диодный шифратор до 15 кнопок) и "привязать" к ним
различные сообщения или включить в работу 2-ю строку и т.д.
Это уже, как говорится, дело техники. Желающие могут попробовать.
Я же, "выдаю основу", от которой можно "плясать".
Раз уж зашла речь о русифицированных ЖКИ модулях на основе HD44780, то
"раскручу" эту тему до конца.

Методика работы с кодировками русских символов

Например, Вы имеете русифицированный ЖКИ модуль (устройство, позволяющее это


определить, я Вам предоставил ранее), собрали на нем, например, мой ЧМ/ЦШ
(программа bf_fr_12.asm), но хотите вывести на индикацию надписи не на английском,
а на русском языке.
Объясняю принцип перехода.
Есть 2 "компактных" способа формирования текста, выводимого на индикацию:
1. Текст формируется в "рабочей" части программы, с использованием директивы DT.
Так сделано, например, в программе bf_fr_3.asm (см. ПП TEXT_1...3).
2. Текст формируется с использованием директивы DE, то есть, на стадии прошивки, в
EEPROM память ПИКа, записываются числа, соответствующие кодировке текста,
"прописанного" в директиве DE.
При выводе на индикацию, в ЖКИ модуль, английских символов, оба этих варианта
проходят "на ура", так как таблица его знакогенератора, в части касающейся символов,

16
применяемых в английском языке, составлена в соответствии со стандартной
кодировкой ASCII или ANSI.
Пояснение: кодировка ASCII есть "чисто английская" кодировка символов (русским
духом вообще не пахнет), а кодировка ANSI ("дочь") это та же кодировка ASCII ("мать"),
но "адаптированная под Россию": первая ее половина - "чисто английская", а во второй
уже русским духом пахнет.
Так вот, в части касающейся русских символов, кодировка таблицы знакогенератора
ЖКИ, на основе HD44780, "не вписывается" даже в кодировку ANSI.
Вопрос: "Что делать с этим "аппендицитом"?
Ответ: вырезать к чертовой матери.
Вопрос: "Каким образом"?
Ответ: путем банального "обмана" (“через задний проход”).
Например, работаем с ПП TEXT_1 программы bf_fr_3.asm, в которой используется
директива DT.
Нужно вывести на индикацию не надпись " Please wait... ", а надпись, например, " Вывод
текста ".
Открываем окно ROM и ищем разложение на команды подпрограммы TEXT_1.
Команды этого разложения расположены под порядковыми номерами с 33 по 49.
Выделяем их мышью и копируем в буфер обмена (Edit - Copy).
Закрываем окно ROM и вставляем скопированное (Edit - Paste) в "район" ПП TEXT_1
(предварительно, там нужно организовать пустую строку).
Наводим в скопированном порядок (убираем лишнее, подгоняем {выравниваем}
столбцы по тексту программы).
После этого, "материнская" ПП TEXT_1 (та, в которой используется директива DT) из
текста программы удаляется:
;--------------------------------------------------------------------------------
; Это, после организации разложения на команды, нужно убрать из текста программы.
;--------------------------------------------------------------------------------
TEXT_1 addwf PC,F ; Приращение PC на величину
; содержимого W.
dt " Please wait... " ; Запись в W адресного запроса к
; содержимому одной из ячеек
; знакогенератора.
;--------------------------------------------------------------------------------
Что получается?
А получается то, что "материнская" ПП TEXT_1 заменена на ее разложение на команды,
и объем программы от этого не изменился.
Дальше все очень просто: в соответствии с рис. 1, заменяем "старые" (английские)
кодировки символов на "новые" (русские).
Вот как это выглядит:
;================================================================================
; Разложение на команды директивы dt после замены надписи “ Please wait... “
; на надпись “ Вывод текста “.
;================================================================================
TEXT_1 addwf PC,F ; Приращение PC на величину содержимого W.
retlw 0x20 ; “пробел”
retlw 0x20 ; “пробел”
retlw 0x42 ; “В”
retlw 0xC3 ; “ы”
retlw 0xB3 ; “в”
retlw 0x6F ; “о”
retlw 0xE3 ; “д”

17
retlw 0x20 ; “пробел”
retlw 0xBF ; “т”
retlw 0x65 ; “е”
retlw 0xBA ; “к”
retlw 0x63 ; “с”
retlw 0xBF ; “т”
retlw 0x61 ; “а”
retlw 0x20 ; “пробел”
retlw 0x20 ; “пробел”
;================================================================================

В случае, если используется прямое обращение к таблице знакогенератора (без применения


директивы), например, при выводе на индикацию надписи "Hz", еще проще: 48h
меняется на A1h ("H" на "Г"), а 7Ah меняется на E5h ("z" на "ц").
На счет пробела (символ "пусто"): пробел, он и в Африке пробел.
В случае применения директивы DE (см. программу bf_fr_12.asm), несколько сложнее,
так как ее разложения на команды в окне ROM Вы не найдете.
Это приводит к тому, что, путем "манипуляций" с текстом "рабочей" части программы,
осуществить смену кодировок нельзя.
Каков выход?
А он очень простой.
Шьете в ПИК HEX файл программы, например, все той же bf_fr_12.asm (или другой
программы, в которой используется директива DE).
Если HEX файл программы bf_fr_12.asm в ПИК уже зашит ранее, то этой прошивки
делать не нужно.
Не выключая программатора, в соответствующих ячейках EEPROM памяти ПИКа,
меняете "старые" значения чисел на "новые", а после этого, производите цикл записи в
EEPROM память.
Можете потом произвести цикл чтения из EEPROM памяти и убедиться в том, что все
записалось верно.
И "на закуску": при использовании ЖКИ модуля SC1602BULT-SH-HS-G, Василий
Макаревич столкнулся с трудностями, связанными с регулировками по входу Vо (3-й
вывод ЖКИ модуля).
Выход из положения: верхний (по схеме) вывод подстроечного резистора (22 Ком)
отключается от +5в и подключается к выводу этого же подстроечного резистора,
подключенному к Vо.
Замечания по процессу составления программы:
В принципе, этот процесс прост и незамысловат, но при одном условии – “детальки” должны
быть в наличии, причем, “отполированные пастой Гои”.
Если это есть, то задача сводится только к “подгонке” одной “детальки” под другую, с учетом
специфики устройства.
Естественно, эта специфика должна быть четко определена, и именно с этой работой, в данном
случае, связаны основные трудозатраты.
Думаю, что Вы согласитесь со мной в том, что, в данном случае, трудозатраты
минимизированы, так как не нужно “лезть в дебри” ранее отработанных процедур, а
нужно только “влепить” их туда, где их законное место и, в случае необходимости,
“навести мелкий марафет”.
Конечно же, не все так просто. Для того, чтобы, в будущем, имела место быть такая
“минимизация”, нужно, в настоящем, “попыхтеть” как следует.
Банальная поговорка “Трудно в учении, но легко в бою” как нельзя лучше отражает смысл
сказанного.

18
А “механизатору широкого профиля” жалую трактор.
Не жалко доверить, так как сейчас имеет место быть не “буржуйский дебил”, а мыслящая
личность с задатками “стахановца”.
Работай, родимый, приноси пользу людям и не будь в обиде: для тебя же, “подлеца”, стараюсь.
Потом спасибо скажешь и за выпоротый зад и за науку типа “упасть – отжаться”.

"Практикум по конструированию устройств на PIC контроллерах" http://ikarab.narod.ru E-mail: karabea@lipetsk.ru

19

Вам также может понравиться