Цветной дисплейный модуль 128x128

Дисплейный модуль 128x128

Данный проект является результатом эволюции дисплейного модуля на LCD Nokia 5110 и МК ATMega8. Экран 5110 вполне себе удобен и его возможностей более чем достаточно для многих конструкций. Но после того, как мною было собрано некоторое количество тестеров микросхем и ZX-магнитофонов, обнаружилась одна неприятная проблема - качество дисплеев Nokia5110, продаваемых на ebay и aliexpress. А точнее, полное отсутствие этого качества. Китайцы продают явно не новые экраны - минимум половина из них приходит с глубокими царапинами, некоторые не работают вообще, некоторые - только наполовину (когда картинка отсутствует сверху или снизу). Но это только полбеды. Хуже всего то, что у некоторых экземпляров теряется контакт между матрицей и платой. В результате чего контрастность изображения падает, а на экране могут появляться артефакты. Причём, если немного прижать матрицу к плате, то картинка восстанавливается, но со временем (измеряемым от нескольких минут до нескольких дней) она снова пропадёт. Т.е., для полного выявления проблемы требуются дни и недели.

В качестве альтернативы 5110 в китайских интернет-магазинах часто предлагается цветной экран с диагональю 1.44" и разрешением 128х128 под управлением контроллера ST7735. Вот такой:

128x128 1.44 inch LCD 128x128 1.44 inch LCD

По размеру экрана он получается чуть меньше, чем 5110, но зато пикселей имеет в четыре раза больше. И, к тому же, он цветной и умеет отображать 64К цветов. Т.е., для хранения информации об одном пикселе вместо одного бита требуется 16 бит. Следовательно, содержимое экрана будет занимать 32 Кб (128*128*2), что существенно больше, чем 504 байта (84*48/8) для монохромного 5110. Это приводит к принципиальному отличию в работе с дисплеем - теперь не получится завести экранный буфер в котором будет формироваться картинка, а потом разом показываться чтобы избежать мерцания. Теперь для избежания мерцания придётся писать код так, чтобы не вызывать лишних операций отрисовки.

Т.к. объёмы обрабатываемых данных с цветным дисплеем будут в десятки раз больше, то вместо ATMega8 на 16МГц имеет смысл использовать более быстрый и ёмкий микроконтроллер. Из бюджетных AVR-ок это может быть ATMega328 на 20МГц. Основное значение тут играет скорее память, чем максимальная частота - большее разрешение экрана ведёт к желанию использовать шрифты большего размера, а их нужно где-то хранить и восьми килобайт тут будет маловато.

Цветной экран может питаться от напряжения 5В (т.к. содержит на плате собственный стабилизатор 3.3В) и толерантен к 5В сигналам, что сильно упрощает схему модуля, убирая из неё стабилизатор и перобразователь уровня на полевом транзисторе. Кроме дисплея и микроконтроллера с обвязкой в схеме осталось 5 кнопок, пищалка и пара транзисторных ключей для управления биппером и светодиодной подсветкой (кстати, в отличии от 5110, если подсветка выключена, то разглядеть что-то на экране не получится).

Схема модуля 128x128 1.44 inch LCD

Печатная плата модуля:

Печатная плата модуля 128x128 1.44 inch LCD

Также развёл компактный вариант платы без кнопок и пищалки, размеры и расположение крепёжных отверстий точно такие же как у модуля 5110 - для перевода старых девайсов (в первую очередь ZX-магнитофонов) на новые экраны.

Печатная плата мини-модуля 128x128

И, наконец, сам модуль в сборе:

Собранный дисплейный модуль 128x128 1.44 inch LCD

Пару слов по монтажу печатной платы: ATMega328 на 20МГц оказалась гораздо более чувствительной к несмытому флюсу, чем ATMega8 на 16МГц (я использовал флюс ФТС). Экспериментируя с собранной но ещё не промытой платой заметил, что биппер иногда начинает периодически самопроизвольно пищать. Причиной, как выяснилось, является то, что из-за несмытого флюса пины микроконтроллера, подключенные к клавиатуре, и подтянутые к +5В (установкой соответствующих бит в PORTC) эту подтяжку "теряют" (из-за токов утечки по остаткам флюса на "землю"), что в результате воспринимается как нажатие на кнопки клавиатуры с последующей подачей звукового сигнала. После промывки и просушки платы проблема полностью устранилась.

Дисплеи продаются с уже припаянными штырьками разъёма PLS. Рекомендую при установке дисплея на плату модуля этот разъём отпаять и заменить более гибкими проводками. Сам дисплей при этом можно закрепить на плате двусторонним скотчем.

Исходники прошивки живут на гитхабе

После заказа нескольких партий дисплеев разных версий и у разных продавцов было замечено, что они не совсем одинаковы - у некоторых система координат получается как бы смещённой - пиксел, выведенный в позицию (0, 0) отображается не в верхнем левом углу экрана, а чуть правее и/или ниже. При этом после вызова метода очистки экрана на нём остаются строки и/или столбцы заполненные случайным "мусором". Ничего страшного в этом нет, просто перед компиляцией прошивки надо открыть файл конфигурации config.h и прописать в нём смещения

#define LCD_DELTA_X					2
#define LCD_DELTA_Y					1

Если есть "мусорные" столбцы, то надо подсчитать их количество и указать его в LCD_DELTA_X. Количество "мусорных" строк, соответственно, задаётся в LCD_DELTA_Y. После того, как эти смещения будут правильно настроены, картинка встанет на своё место.

Также в файле config.h можно настроить скорость UART (UART_BAUD_RATE), яркость посдсветки дисплея, чувствительность клавиатуры, громкость биппера и время автовыключения подсветки дисплея. Но можно оставить всё как есть, т.к. все эти параметры можно будет изменить потом, вызывая соответствующие методы модуля.

Для компиляции прошивки я, как обычно, использовую свою утилиту. А для заливки прошивки - связку usbasp + avrdude. Фьюзы и прошивка записываются командой:

avrdude -P usb -c usbasp -b 57600 -p ATmega328P -qq -U lfuse:w:0xff:m -U efuse:w:0xfc:m -U hfuse:w:0xd3:m

Для того, чтобы поиграться с модулем, проще всего подключить его к компьютеру через переходник usb-to-uart и использовать библиотеку на питоне. Для этого понадобится интерпретатор питона (версии 2.7) и библиотека работы с последовательным потртом pyserial. Установить последнюю можно командой

python -m pip install pyserial

Модуль работает по интерфейсу UART используя простой двоичный протокол обмена - управляющая программа сначала передаёт однобайтовый код команды, затем, её аргументы. На некоторые команды модуль посылает свой ответ (например на запрос состояния клавиатуры read_keyboard()или команду sync()). Протокол обмена реализован в классе Dislplay.

Примеры работы с дисплеем можно посмотреть тут. Сначала создаётся объект Display:

dev = Display('/dev/tty.wchusbserial14230', 57600)

где конструктор получает имя последовательного порта и скорость обмена (должна совпадать со скоростью, указанной в config.h). Узнать список доступных в системе последовательных портов, можно запустив скрипт serial_list.py.

Функции работы с дисплеем можно разделить на следующие группы:

Настройка параметров модуля

    set_backlight_time(val16)
    set_backlight_level_high(level)
    set_backlight_level_low(level)
    set_keyboard_beep(enable)
    set_beeper_volume(volume)

Эти методы служат для изменения различных параметров модуля (значения по умолчанию которых заданы в config.h)

Управление биппером

  beep(freq, duration)

На момент написания этой статьи тут только один метод - воспроизведение звука частотой freq (задаваемой в Герцах, uint16) длительностью duration (задаваемой в сотых долях секунды, uint8). В будущем сюда просится второй метод - play_melody для воспроизведения последовательности звуков (пока не реализовано).

Рисование

Самая большая группа методов отвечает за рисование графических примитивов, текста и готовых элементов управления. Часть методов служит для настройки параметров рисования (цвета рисования и заливки, межсимвольный и межстрочный интервалы для текста, используемый шрифт):

     set_string_interval_x(interval)
     set_string_interval_y(interval)
     set_color(color)	
     set_background(background)
     set_font(font)

Рисовать можно от графических примитивов, до готовых элементов управления. Некоторые методы рисования примитивов существуют в двух экземплярах - с использованием цветов по умолчанию и с их явным указанием. Для рисования символов и строк есть группа методов draw_ и группа методов fill_. Последние закрашивают фон под выводимым символами текущим цветом фона, что часто является необходимым.

    fill_screen(color)
    fill_rect_by(x, y, w, h, color)
    fill_rect(x, y, w, h)
    draw_pixel(x, y)
    draw_line_by(x0, y0, x1, y1, color)
    draw_line(x0, y0, x1, y1)
    draw_line_to(x, y)
    draw_rect_by(x, y, w, h, color)
    draw_rect(x, y, w, h)
    draw_rect_thick_by(x, y, w, h, tx, ty, color)
    draw_rect_thick(x, y, w, h, tx, ty)
    draw_circle_by(x, y, r, color)
    draw_circle(x, y, r)
    fill_circle_by(x, y, r, color)
    fill_circle(x, y, r)
    draw_char(ch)
    draw_char_xy(x, y, ch)
    draw_string(s)
    draw_string_xy(x, y, s, anchor)
    fill_char(ch)
    fill_char_xy(x, y, ch)
    fill_string(s):
    fill_string_xy(x, y, s, anchor)
    draw_progress_bar(x, y, w, h, progress)
    draw_vertical_scrollbar(x, y, width, height, item_index, number_of_items, interval)

Прочие методы

    sync(val)
    read_keyboard()
    get_version()
    start_bootloader()

Из оставшихся методов следует особо выделить метод sync(uint8). Он передаёт в порт один байт, затем дожидается ответа и возвращает его. В ответ модуль возвращает прочитанный байт, т.е., метод sync(val) должен вернуть значение val. Этот метод имеет простое назначение: во-первых, он используется при установлении связи с модулем чтобы убедиться, что эта связь успешно установлена. Во-вторых, он может использоваться для синхронизации с дисплеем. Модуль обрабатывает команды асинхронно, т.е., при получении он сначала помещает их в буфер (размер которго, кстати, определяется макросом UART_RX_BUFFER_SIZE), затем, по мере появления свободного времени, выполняет. Т.е., например, после завершения вызова метода fill_circle_by(...) на экране ещё совсем не не обязательно, появится круг, но запрос на его отрисовку будет поставлен в очередь задач. И если разом заслать модулю тысячу команд, то буфер переполниться, и модуль будет этому совсем не рад. В данном случае вызов метода sync() позволяет дождаться того, пока модуль закончит обработку всей свой очереди.

Метод read_keyboard() читает состояние клавиатуры. Если нажатых клавиш нет, он вернёт None. Иначе - массив из пяти элементов с длительностями удержания клавиш нажатыми.

Метод start_bootloader() передаёт управление загрузчику микроконтроллера. Нужен для обновления прошивки модуля без программатора (если в микроконтроллере этот загрузчик есть).

Реализацию этих методов под AVR GCC для управления модулем с AVR-устройства можно посмотреть в исходниках тестера микросхем. По мере того, как станет понятно, какие методы нужны я добавлю все библиотеки в репозиторий модуля дисплея.

Фьюзы:

Фьюзы дисплейного модуля 128x128 1.44

Рейтинг: 
0
Голосов еще нет