Как то выдрал из старого принтера HP экранчик, и решил вспомнить былые времена примитивных поделок с символьными экранчиками.

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

Где земля и питание понятно и без глубокого анализа. А вот с расположением шины данных возникли проблемы. Порядок то нарушен не был, но расположены они были наоборот с 14 по 4й, а не с 4-го по 14й. Помогла отметка первого вывода бескорпусной микросхемы, распиновка микросхемы полностью совпадала с datasheet, хоть и была бескорпусной. Таким образом экран запустился, а я и не ожидал запустить ноунейм из коробки с ломом плат без документации.

В итоге распиновка следующая: контрастность, +5V, GND, D8-D0, E, R/W, RS.

 распиновка Data Image CM160261
распиновка 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 и make clean в 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, а он сам уже будет и генерировать программные кодировки и регулировать контраст и яркость подсветки.

Или все таки пора положить это старьё обратно откуда взял и начать ставить цветные экраны?

И стоит ли настолько подробно комментировать код, или абзац про код библиотеки не нужен?