Все любители электронных самоделок когда-нибудь приходят к желанию отобразить работу своей поделки самоделки на экране в виде текста или графики. Самый бюджетный способ сделать это — обратиться к алфавитно-цифровому дисплею типа LCD1602 или LCD2004, общение с которыми происходит либо по параллельному интерфейсу, либо через переходник-конвертор в I2C. Второй способ — использовать графический дисплей, их множество, например SSD1306 с размером матрицы 128x64 пикселя. Он есть в двух вариантах, с интерфейсом SPI или I2C. Я буду взаимодействовать с таким дисплеем через I2C интерфейс (он же TWI).

Рисунок 1.- графический LCD дисплей SSD1306 
с матрицей 128x64 пикселя
Рисунок 1.- графический LCD дисплей SSD1306 с матрицей 128x64 пикселя
  1. Сначала подаем команду старт

  2. Передаем адрес дисплея SLA+W

  3. Дальше идет m слов содержащих всевозможные настройки дисплея

  4. Передается признак того, что дальше пойдут графические данные (1 байт) и собственно данные n байт.

Передача каждого байта устанавливает статус ACK.

Рисунок 2.- формат пакета данных для дисплея
Рисунок 2.- формат пакета данных для дисплея

Инициализация модуля

Вот последовательность действий для инициализации графического дисплея

  1. Отправляем дисплей в SLEEP MODE (команда 0xAE SSD1306_DISPLAYOFF)

  2. Установка делителя частоты (команда 0xD5 SSD1306_SETDISPLAYCLOCKDIV) параметр 0x80

  3. Устанавливает отношение мультиплексирования (команда A8 SSD1306_SETMULTIPLEX) параметр 0x3F

  4. Устанавливаем смещение дисплея (команда 0xD3 SSD1306_SETDISPLAYOFFSET) параметр 0x00

  5. устанавливаем начальную линию (команда 0x40 SSD1306_SETSTARTLINE|0x00)

  6. разрешаем charge pump (команда 0x8d SSD1306_CHARGEPUMP) параметр 0x14

  7. устанавливаем горизонтальный режим памяти (команда 0x20 SSD1306_MEMORYMODE) параметр 0x00.

  8. устанавливаем remapping столбцов seg0←→seg127 (команда 0xA0 SSD1306_SEGREMAP | 0x00)

  9. устанавливаем направление сканирования строк (команда 0xС8 SSD1306_COMSCANDEC)

  10. Устанавливаем конфигурацию выводов COM-сигналов в соответствии с компоновкой оборудования OLED - панели (команда 0xDA SSD1306_SETCOMPINS) параметр 0x12

  11. Устанавливаем контраст (команда 0x81 SSD1306_SETCONTRAST) параметр 0xCF

  12. Установка предзаряда (команда 0xD9 SSD1306_SETPRECHARGE) параметр 0xF1

  13. Установка Vcomp deselect level (команда 0xDB SSD1306_SETVCOMDETECT) параметр 0x40

  14. Возобновление отображения содержимого оперативной памяти (команда 0xA4 SSD1306_DISPLAYALLON_RESUME)

  15. Отображение в нормальном виде команда 0xA6 SSD1306_NORMALDISPLAY

  16. Включение дисплея в нормальном режиме команда 0xAF SSD1306_DISPLAYON

Ниже на рисунке 3 приведена ассемблерная подпрограмма инициализации дисплея

Рисунок 3.- Подпрограмма инициализации дисплея
Рисунок 3.- Подпрограмма инициализации дисплея

LCD_Command — это макрос следующего вида:

Рисунок 4.- Исходный код макроса отправки коммады в LCD
Рисунок 4.- Исходный код макроса отправки коммады в LCD

Этот макрос сделан для удобства использования подпрограммы _LCD_Command. Дело в том, что параметры передаются в нее через регистры tmp1 и tmp2 и запись LCD_Command par1, par2 более естественна, чем три строчки из макроса. Стоит заметить, что и макрос и вызов подпрограммы с передачей параметров занимают одинаковое число тактов процессора и объем ROM.

Что же представляет собой подпрограмма _LCD_Command мы посмотрим на рисунке 5.

Рисунок 5.- код подпрограммы _LCD_Command

Как видно, это подпрограмма посылает управляющее слово (см рисунок 2) по I2C. Подпрограмма использует 3 регистра tmp0tmp1 и tmp2. В двух последних передаются параметры для подпрограммы ControlByte и DataByte.

Подпрограммы i2c_starti2c_stopi2c_send рассмотрены в этой статье.

В нашей реализации подпрограмма инициализации дисплея занимает 152 байта. Этот размер можно уменьшить, если обратить внимание на то, что ControlByte у всех команд, отправляемых в экран равен COMMAND. Как минимум, можно переписать макросы таким образом, чтобы лишний раз не обновлять регистр tmp1.

Очистка экрана (заливка определенным паттерном)

Что такое очистка экрана — это всего лишь заполнение видеопамяти символами определенного качества, в нашем случае это заполнение каждого сегмента страницы видеопамяти графического модуля нулями. Нижеприведенный код подпрограммы будет актуален только при использовании MEMORY_MODE = 0x00 или 0x01.

Рисунок 6.- исходный код подпрограммы очистки экрана
Рисунок 6.- исходный код подпрограммы очистки экрана

Мы передаем по I2C DATA байты в количестве 128x64 = 8192. Каждый переданный байт определяет тип очистки. Если передается 0 — экран очищается, если 0xFF— заливается цветом. Подпрограмма состоит из двух вложенных циклов по страницам памяти (8) и по количеству сегментов (128).

Для удобства использования подпрограмма также была завернута в макрос (рисунок 7)

Рисунок 7.- Макрос очистки экрана.
Рисунок 7.- Макрос очистки экрана.

Позиционирование

Рассмотрим теперь позиционирование на экране. Несмотря на то, что у нас дисплей 128x64 пикселя, адресовать номер страницы памяти и номер столбца. Каждая страница памяти это 8 строк. Поэтому, если мы хотим зажечь, допустим, 15 пиксель в 10 строке, мы должны выбрать 1 страницу памяти, в ней выбрать 15 сегмент и по этому адресу записать 4 (бинарное 0000 0100).

Выбор номера страницы памяти осуществляется командами B0-B7, или B0 | y.

Чтобы установить номер столбца, стачала нужно записать в шину последнюю (младшую) шестнадцатиричную цифру числа, затем первую (старшую).

Таким образом, позиционирование выльется в посылке 3 посылки по шине TWI (рисунок 8)

Рисунок 8.- Макрос позиционирования
Рисунок 8.- Макрос позиционирования

В этом макросе мы устанавливаем маркер текущего положения курсора в позицию (x,y), а также присваиваем соответствующие значения переменным LCD_X, LCD_Y.

Еще один неопределенный макрос SETMEM как раз и занимается сохранением данных в памяти.

Рисунок 9.- макрос сохранения числа в память SRAM
Рисунок 9.- макрос сохранения числа в память SRAM

Комментарии (15)


  1. Digitalrex
    15.07.2021 13:12

    Чисто риторический вопрос, без негатива, а для чего писать драйвер на ассемблере, если частота обновления самого дисплея, т.е. отрисовка пикселей с RAM дисплея, в районе 30 герц:) Но а за программирование на ассемблере, конечно, лайк.


    1. physicist2018 Автор
      16.07.2021 03:41

      Для начала, чтобы разобраться в том, что происходит в процессе работы прошивки. Второй момент-та же оптимизация.


  1. Andy_Big
    15.07.2021 13:24
    +1

    Простите, но смахивает на не слишком качественный перевод, да еще и с техническими ошибками.


    1. physicist2018 Автор
      16.07.2021 03:41

      Прощаю, но вы не правы)


  1. rcepro
    15.07.2021 14:27

    напоминает меня раннего, года так 2004. тоже пытался разобрать протокол работы с дисплеями от сотовых телефонов. Скопилось много после ремонта сотовых, а выбросить было жалко.


  1. Karlson_rwa
    15.07.2021 22:27

    Код картинками? Вы серьезно?


    1. dcoder_mm
      15.07.2021 23:00

      Это чтобы Copilot не смог распарсить и украсть.


    1. physicist2018 Автор
      16.07.2021 03:44

      Серьёзно. Разберусь с подсветкой кода в редакторе-будут статьи без картинок.


    1. DaemonGloom
      16.07.2021 08:27

      К сожалению, Хабр не умеет подсвечивать синтаксис ассемблера.


    1. Zuy
      16.07.2021 08:33

      Так эту ботву на ассемблере кроме автора никто ни читать ни использовать не будет, так что, можно было хоть на клингонском написать. Времена форума Microchip.ru давно прошли :)


      1. physicist2018 Автор
        16.07.2021 16:49

        Вы настолько знаете интересы читателей, что говорите за всех сразу? Такие заявления безосновательны).

        А по поводу микрочипа-да, АВР устаревает, но это не означает, что их не используют.

        Изучить ассемблер стоит если не для написания прошивок, то хотя бы для понимания, как работает устройство.

        Си прекрасен, но есть баги, которые вы будете долго ловить и не факт, что поймаете, без знаний ассемблера и того, как компилятор си генерит код.

        Да и размер прошивки на ассемблере можно сделать меньше.


        1. Zuy
          16.07.2021 19:18

          Просто интересно, когда вы говорите "изучить ассемблер" вы какой-то конкретный имеете ввиду? Если у меня в работе одновременно Arm, ppc и C2000 нужно ли все их ассемблеры изучать...

          Я перешел с ассемблера на С в embedded разработке лет 20 назад. За все эти 20 лет, был только один баг, который пришлось ловить в ассемблере. Как-то по месту разобрался.

          Понимание, что такое ассемблер для эмбедера конечно нужно, но врядли имеет смысл сейчас париться так, чтобы прямо драйверы на нем писать или логику приложения.

          Насчет microchip я в спомнил не в смысле что он устаревает. Примерно в районе 2000 года, был крайне популярный формум на сайте Microchip.ru. Тогда еще не было С на микроконтроллерах и все примеры кода для PIC постились на асме, прямо как у вас в статье. Только, конечно, текстом, картинки на dialup долго было качать.


          1. physicist2018 Автор
            29.07.2021 17:22

            Тот, для какого устройства вы пишите. ))


        1. Andy_Big
          20.07.2021 00:45

          И к AVR микрочип тогда не имел никакого отношения, кстати :)


  1. physicist2018 Автор
    29.07.2021 17:21

    >>на асме много не напишешь

    На чем угодно много не напишешь, если не знаешь. Я не агитирую за асм, но показываю как это делал. Конечно, на си из коробки можно больше чем на асм, но кто мешает вам создать нужные макросы и их юзать? Получатся те же яйца что и на gcc, только все будет собрано ровно так, как ты написал и ожидаешь, а не так, как "умный gcc" соптимизировал.

    >>его никто не прочтет кроме автора

    Ха-ха, на си говно кодеров хватает, особенно в embedded. Правильная документация должна быть. Со временем, все становится привычкой и мозг думает на асм. Ну и про макросы не стоит забывать.