Приключения ржавого 1602 (HD44780 A00)
Как то выдрал из старого принтера HP экранчик, и решил вспомнить былые времена примитивных поделок с символьными экранчиками.
Определение распиновки ноунейма без опознавательных знаков.
Где земля и питание понятно и без глубокого анализа. А вот с расположением шины данных возникли проблемы. Порядок то нарушен не был, но расположены они были наоборот с 14 по 4й, а не с 4-го по 14й. Помогла отметка первого вывода бескорпусной микросхемы, распиновка микросхемы полностью совпадала с datasheet, хоть и была бескорпусной. Таким образом экран запустился, а я и не ожидал запустить ноунейм из коробки с ломом плат без документации.
В итоге распиновка следующая: контрастность, +5V, GND, D8-D0, E, R/W, RS.
распиновка Data Image CM160261
Кодировки
Даже разработчик драйвера (Toshiba) задумывал поддержку разных символов. В нулевом наборе был японский, а уже во втором была кириллица. Но т.к. китайцы обычно не особо то заморачиваются, в первом попавшемся экране наверняка будет японский ROM. И не потому что западному покупателю или китайцам так сильно нужны были японские иероглифы, а потому что ROM с японским идет под номером 0.
Но не все так плохо, все очень плохо. Те экраны с русским что раньше я покупал за 250 рублей, сегодня и у местных и в чип и дипе стоят 1000. Экраны с алиэкспресс с буковкой 'C' в названии, намекающей на кириллицу также, с учетом доставки, стоят 1000.
В этой ситуации выгодно выделить экраны на контроллере ST7070 с интерфейсом SPI и кириллицей. Мы же с вами прекрасно понимаем, что отдавать больше 100 за какое то старье нет смысла, а за 1000 можно купить два тача 320×240 или ремонтный full hd от galaxy, и еще 160×80 от умного браслета в придачу.
Поэтому ноунейм за 100 и кольцевой буффер, а если вы сошли с ума, то либо Winstar в чип и дипе (WH1602 например), его поддерживает моя библиотека, либо ST7070 с алиэкспресс.
Скучный абзац про код библиотеки
Всем известна библиотека для символьных экранов, идущая в комплекте проекта avr-libc. Я, как всегда, решил написать еще одну. Считаю такой подход с написанием еще одной либы рядом с уже существующей нормальным. Код библиотеки.
На первом уровне функция установки байта (полубайта) на шинеsetBus(uint8_t byte, uint8_t isHalf)
. Данные о портах и пинах каждого из выводов шины упакованы в массив, а в функции цикл, который их устанавливает. Кроме байта данных в функцию передается параметр, отвечающий за четырехбитный режим. Если isHalf=1
, то будут выставлены только младшие четыре бита на старших четырех битах шины. После установки байта функция щелкает сигналом Enable с небольшой задержкой. Т.к. килогерцовый драйвер из 80х может не успеть считать данные у быстро переключающегося мегагерцового микроконтроллера.
Далее функция setBus обертывается в функцию lcdTrans(uint8_t byte).
Код, отвечающий за выбор восьми или четырех битной шины, уже внутри нее, она же и учитывает сброс сигнала Read/Write, выставляя его на запись.
Далее, опираясь на функцию отправки даннных в драйвер, легко реализовать функции установки команды и записи байта RAM драйвера. Это функции lcdCommand(uint8_t cmd)
и lcdWriteRamm(uint8_t data)
соответственно. Они, учитывая состояние сигнала Register Select, отправляют данные по шине и ожидают окончания выполнения команды. Причем, если сигнал RW не подключен, то функция банально ждет нужное количество микросекунд. А если RW подключен, то используется циклический опрос драйвера с проверкой бита Busy. Так же имеется функция чтения байта параллельной шины uint8_t lcdRead(void)
, работает как запись, только наоборот.
Далее идет стандартная функция lcdInit()
, проходящая по всему списку команд инициализации. Важно ее писать хоть немного вчитываясь в datasheet, а не как обычно. Т.к. не учтенные задержки долго выполняющихся команд могут привести к более долгой инициализации или вовсе срабатыванию через раз. Также важны правильные пляски с бубном для установки драйвера в четырехбитный режим.
Обертки стандартных команд так же в наличии. Можно очистить экран lcdClr(),
выбрать режим курсора lcdCursor(uint8_t state)
и установить указатель на нужное знакоместо lcdPos(uint8_t x, uint8_t y).
Для удобства выбора режима курсора, его состояния упакованы в enum CURSOR_OFF, ...
, координаты x, y считаются от 0.
Замеряя количество операций считывания во время проверки бита Busy, я выяснил, что все команды выполняются в период, меньший чем 200 мс. За исключением команды очистки экрана, она выполняется 3 миллисекунды. Моя программа учитывает этот момент, для очистки экрана установлена задержка 5 мс, все остальные команды выполняются с задержкой 50 мкс. Для успокоения я так же выставил задержку на полупериод сигнала enable в 1 мкс. Таким образом, символьный дисплей не задержит вашу основную прошивку даже если вы сэкономили GPIO на сигнале RW.
Из стандартных еще есть функция lcdCustomSymbol(uint8_t code, const uint8_t *symbol).
Копирует в драйвер пользовательский символ, параметры это адрес (0–7) и восьмибайтный массив с символом.
С настройками разобрались, теперь можно пользоваться экраном, есть две базовых функции lputc(char c) и lputs(char *c)
Для вывода символа и строки соответственно. Адекватно работать в этих функциях будут только ASCII символы (за исключением '\'), остальные можно скармливать через escape последовательности.
Кроме базовых функций есть функции удобные. В былые времена в местном магазине радиодеталей можно было купить экран марки Winstar с поддержкой кириллицы. Для таких экранов есть функция, luputs()
, через которую в linux можно выводить и текст на русском. Внутри базовый парсер многобайтных юникод-последовательностей (UCS). Функция довольно компактная, т.к. перевод кодировки осуществляется через таблицу.
Так же для экранов без поддержки кириллицы есть функция с программной. Она подгружает по мере надобности русские символы в кольцевой буффер. Если выводить на экран настройки, а не тексты любимых песен и приветствия, то буффера из восьми символов хватает. Но если случится переполнение, то скорее всего повредится только первое слово, в нем перезапишется часть букв.
Выглядит такая функция (cyrillicParser(uint8_t c)
) как банальный switch case, который либо загружает нужную букву и тут же её выводит, либо просто выводит на экран английскую букву, соответствующую русской. Символы загружаются через функцию loadSymbol(uint8_t code, const uint8_t *symbol)
, она хранит коды уже загруженных символов, и в случае повтора, просто возвращает его адрес. Если же символ отсутствует, то она загружает его в n+1 ячейку памяти и так же возвращает его адрес.
Так же в библиотеке реализован полноценный printf.
Была попытка и в динамическую индикацию, не совсем удачная, кстати. Хранится в функции showImage(const uint8_t (*img)[8], uint16_t time_s).
Алгоритм простейший, стереть предыдущую четверть, заполнить следующую, подождать.
Весь остальной скарб хранится в заголовочниках. В файле hd44780_stuff.h все константы. Это макросы команд, времена задержек, а также массив с кодовой таблицей для экранов WH1602. UCS коды символов и сами кириллические символы для программной кириллицы хранятся в файле ru_font.h.
Инструкция пользователя
Библиотека лежит в папке hd44780 и предназначена для микроконтроллера stm32f103. Но работает она в паре с моей библиотекой для stm32, для сборки примера нужно скопировать и её. Сам репозиторий лучше не клонировать, в нём я храню все скачанные datasheet, он занимает слишком много места. Сборка стандартная, и make clean и make в linux.
Перед сборкой её нужно настроить, выставить порты и пины шины экрана в hd44780.h. Допускается, что разные биты могут быть на разных портах. Далее необходимо, если вы не подключаете RW, раскомментировать макрос RW_DISABLE
, указать длину экрана в LCD_SIZE
. А также раскомментировать один из двух макросовLCD_4/8BIT
, выбрав 4 или 8 проводковую шину. Важно заметить, что четырехпроводная шина использует порты D4_PIN - D7_PIN
. Также для поддержки кириллицы на экранах с крокозябрами нужно раскомментировать макрос PROGRAM_RU
. Поддерживаются двустрочные экраны, для четырехстрочных код не дописан за неимением таковых.
Настройка закончена, можно вызывать lcdInit()
и выводить русский текст через luputs()
или lprintf()
.
Если в случае с программной кодировкой вам не хватило символов, можно воспользоваться лайфхаком. Например большая буква Y похожа на У, буква g на письменную д, r на г, и т.д. Всех их можно активизировать, раскомментировав макросы в файле ru_font.h.
Програмный русский текст разной степени убогости
Бонусом для владельцев winstar есть скрипт, переводящий русский текст в текст с escape последовательностями для этого дисплейчика. Бинарник консольной программы и её make лежат в папке. Работает просто, запускаете в терминале декодер, пишете текст, жмете enter, получаете переведенный текст.
Растровый режим
Я в курсе что есть такие же растровые, но они стоят 500р. Да и вывод картинки в качестве заставки не помешает. Но вышло неудачно. Сам по себе экран слишком медленный, что бы потянуть динамическую индикацию. То есть при выводе изображения 4 частями яркость падает во много раз, а не в 4 раза. Хотя если выкрутить контраст, изображение разобрать можно. Для регулировки контраста кинул временный ШИМ прям в main, на порт PA1.
Для вывода вашего изображения приготовьте монохромную картинку разрешением 80×16 и конвертируйте её с помощью программы convert пакета imagemagick convert -format pbm yourFile.bmp out.h
Далее положите файл в папку с конвертером pbm_to_symbols, и запустите скрипт pbmto1602. Полученный заголовочник img1602.h подключите к вашему коду и передайте указатель на этот массив в функцию showImage(const uint8_t (*img)[8], uint16_t time_s)
. Функция тупая, работает на задержках, но имеет настройку времени отображения (параметр time_s
).
не спрашивайте у меня кто это
Дальнейшее развитие проекта
Ну вот, поднял (в прямом и переносном смысле) из старого хлама какое то барахло. Написал нудный доклад, о том, как оно работало. Теперь в лучших традициях провинциального ВУЗа стоит поговорить о дальнейшем развитии. В интернетике все подключают эти древние экраны с раритетной шиной через такой же древний расширитель портов (комбо старья). Но он вовсе не дешевый, стоит в два раза дороже чем stm8. В моих проектах и расточительная параллельная шина не помеха. Но как вы думаете, имеет ли смысл изобретать переходник для этих экранов на stm8 на продажу (или если я его сделаю, его кто нибудь повторит?). Команды и текст в stm8 будут приходить по uart, а он сам уже будет и генерировать программные кодировки и регулировать контраст и яркость подсветки.
Или все таки пора положить это старьё обратно откуда взял и начать ставить цветные экраны?
И стоит ли настолько подробно комментировать код, или абзац про код библиотеки не нужен?