Все любители электронных самоделок когда-нибудь приходят к желанию отобразить работу своей поделки самоделки на экране в виде текста или графики. Самый бюджетный способ сделать это — обратиться к алфавитно-цифровому дисплею типа LCD1602 или LCD2004, общение с которыми происходит либо по параллельному интерфейсу, либо через переходник-конвертор в I2C. Второй способ — использовать графический дисплей, их множество, например SSD1306 с размером матрицы 128x64 пикселя. Он есть в двух вариантах, с интерфейсом SPI или I2C. Я буду взаимодействовать с таким дисплеем через I2C интерфейс (он же TWI).
Сначала подаем команду старт
Передаем адрес дисплея SLA+W
Дальше идет m слов содержащих всевозможные настройки дисплея
Передается признак того, что дальше пойдут графические данные (1 байт) и собственно данные n байт.
Передача каждого байта устанавливает статус ACK.
Инициализация модуля
Вот последовательность действий для инициализации графического дисплея
Отправляем дисплей в SLEEP MODE (команда 0xAE SSD1306_DISPLAYOFF)
Установка делителя частоты (команда 0xD5 SSD1306_SETDISPLAYCLOCKDIV) параметр 0x80
Устанавливает отношение мультиплексирования (команда A8 SSD1306_SETMULTIPLEX) параметр 0x3F
Устанавливаем смещение дисплея (команда 0xD3 SSD1306_SETDISPLAYOFFSET) параметр 0x00
устанавливаем начальную линию (команда 0x40 SSD1306_SETSTARTLINE|0x00)
разрешаем charge pump (команда 0x8d SSD1306_CHARGEPUMP) параметр 0x14
устанавливаем горизонтальный режим памяти (команда 0x20 SSD1306_MEMORYMODE) параметр 0x00.
устанавливаем remapping столбцов seg0←→seg127 (команда 0xA0 SSD1306_SEGREMAP | 0x00)
устанавливаем направление сканирования строк (команда 0xС8 SSD1306_COMSCANDEC)
Устанавливаем конфигурацию выводов COM-сигналов в соответствии с компоновкой оборудования OLED - панели (команда 0xDA SSD1306_SETCOMPINS) параметр 0x12
Устанавливаем контраст (команда 0x81 SSD1306_SETCONTRAST) параметр 0xCF
Установка предзаряда (команда 0xD9 SSD1306_SETPRECHARGE) параметр 0xF1
Установка Vcomp deselect level (команда 0xDB SSD1306_SETVCOMDETECT) параметр 0x40
Возобновление отображения содержимого оперативной памяти (команда 0xA4 SSD1306_DISPLAYALLON_RESUME)
Отображение в нормальном виде команда 0xA6 SSD1306_NORMALDISPLAY
Включение дисплея в нормальном режиме команда 0xAF SSD1306_DISPLAYON
Ниже на рисунке 3 приведена ассемблерная подпрограмма инициализации дисплея
LCD_Command — это макрос следующего вида:
Этот макрос сделан для удобства использования подпрограммы _LCD_Command. Дело в том, что параметры передаются в нее через регистры tmp1 и tmp2 и запись LCD_Command par1, par2 более естественна, чем три строчки из макроса. Стоит заметить, что и макрос и вызов подпрограммы с передачей параметров занимают одинаковое число тактов процессора и объем ROM.
Что же представляет собой подпрограмма _LCD_Command мы посмотрим на рисунке 5.
Рисунок 5.- код подпрограммы _LCD_Command
Как видно, это подпрограмма посылает управляющее слово (см рисунок 2) по I2C. Подпрограмма использует 3 регистра tmp0, tmp1 и tmp2. В двух последних передаются параметры для подпрограммы ControlByte и DataByte.
Подпрограммы i2c_start, i2c_stop, i2c_send рассмотрены в этой статье.
В нашей реализации подпрограмма инициализации дисплея занимает 152 байта. Этот размер можно уменьшить, если обратить внимание на то, что ControlByte у всех команд, отправляемых в экран равен COMMAND. Как минимум, можно переписать макросы таким образом, чтобы лишний раз не обновлять регистр tmp1.
Очистка экрана (заливка определенным паттерном)
Что такое очистка экрана — это всего лишь заполнение видеопамяти символами определенного качества, в нашем случае это заполнение каждого сегмента страницы видеопамяти графического модуля нулями. Нижеприведенный код подпрограммы будет актуален только при использовании MEMORY_MODE = 0x00 или 0x01.
Мы передаем по I2C DATA байты в количестве 128x64 = 8192. Каждый переданный байт определяет тип очистки. Если передается 0 — экран очищается, если 0xFF— заливается цветом. Подпрограмма состоит из двух вложенных циклов по страницам памяти (8) и по количеству сегментов (128).
Для удобства использования подпрограмма также была завернута в макрос (рисунок 7)
Позиционирование
Рассмотрим теперь позиционирование на экране. Несмотря на то, что у нас дисплей 128x64 пикселя, адресовать номер страницы памяти и номер столбца. Каждая страница памяти это 8 строк. Поэтому, если мы хотим зажечь, допустим, 15 пиксель в 10 строке, мы должны выбрать 1 страницу памяти, в ней выбрать 15 сегмент и по этому адресу записать 4 (бинарное 0000 0100).
Выбор номера страницы памяти осуществляется командами B0-B7, или B0 | y.
Чтобы установить номер столбца, стачала нужно записать в шину последнюю (младшую) шестнадцатиричную цифру числа, затем первую (старшую).
Таким образом, позиционирование выльется в посылке 3 посылки по шине TWI (рисунок 8)
В этом макросе мы устанавливаем маркер текущего положения курсора в позицию (x,y), а также присваиваем соответствующие значения переменным LCD_X, LCD_Y.
Еще один неопределенный макрос SETMEM как раз и занимается сохранением данных в памяти.
Комментарии (15)
Andy_Big
15.07.2021 13:24+1Простите, но смахивает на не слишком качественный перевод, да еще и с техническими ошибками.
rcepro
15.07.2021 14:27напоминает меня раннего, года так 2004. тоже пытался разобрать протокол работы с дисплеями от сотовых телефонов. Скопилось много после ремонта сотовых, а выбросить было жалко.
Karlson_rwa
15.07.2021 22:27Код картинками? Вы серьезно?
physicist2018 Автор
16.07.2021 03:44Серьёзно. Разберусь с подсветкой кода в редакторе-будут статьи без картинок.
Zuy
16.07.2021 08:33Так эту ботву на ассемблере кроме автора никто ни читать ни использовать не будет, так что, можно было хоть на клингонском написать. Времена форума Microchip.ru давно прошли :)
physicist2018 Автор
16.07.2021 16:49Вы настолько знаете интересы читателей, что говорите за всех сразу? Такие заявления безосновательны).
А по поводу микрочипа-да, АВР устаревает, но это не означает, что их не используют.
Изучить ассемблер стоит если не для написания прошивок, то хотя бы для понимания, как работает устройство.
Си прекрасен, но есть баги, которые вы будете долго ловить и не факт, что поймаете, без знаний ассемблера и того, как компилятор си генерит код.
Да и размер прошивки на ассемблере можно сделать меньше.
Zuy
16.07.2021 19:18Просто интересно, когда вы говорите "изучить ассемблер" вы какой-то конкретный имеете ввиду? Если у меня в работе одновременно Arm, ppc и C2000 нужно ли все их ассемблеры изучать...
Я перешел с ассемблера на С в embedded разработке лет 20 назад. За все эти 20 лет, был только один баг, который пришлось ловить в ассемблере. Как-то по месту разобрался.
Понимание, что такое ассемблер для эмбедера конечно нужно, но врядли имеет смысл сейчас париться так, чтобы прямо драйверы на нем писать или логику приложения.
Насчет microchip я в спомнил не в смысле что он устаревает. Примерно в районе 2000 года, был крайне популярный формум на сайте Microchip.ru. Тогда еще не было С на микроконтроллерах и все примеры кода для PIC постились на асме, прямо как у вас в статье. Только, конечно, текстом, картинки на dialup долго было качать.
physicist2018 Автор
29.07.2021 17:21>>на асме много не напишешь
На чем угодно много не напишешь, если не знаешь. Я не агитирую за асм, но показываю как это делал. Конечно, на си из коробки можно больше чем на асм, но кто мешает вам создать нужные макросы и их юзать? Получатся те же яйца что и на gcc, только все будет собрано ровно так, как ты написал и ожидаешь, а не так, как "умный gcc" соптимизировал.
>>его никто не прочтет кроме автора
Ха-ха, на си
говнокодеров хватает, особенно в embedded. Правильная документация должна быть. Со временем, все становится привычкой и мозг думает на асм. Ну и про макросы не стоит забывать.
Digitalrex
Чисто риторический вопрос, без негатива, а для чего писать драйвер на ассемблере, если частота обновления самого дисплея, т.е. отрисовка пикселей с RAM дисплея, в районе 30 герц:) Но а за программирование на ассемблере, конечно, лайк.
physicist2018 Автор
Для начала, чтобы разобраться в том, что происходит в процессе работы прошивки. Второй момент-та же оптимизация.