Содержание
Привет! Будучи любителем – энтузиастом электроники, я уверен, что всё что мы делаем – радиоэлектронные игрушки – самоделки, или большие проекты, все это от любопытства и лени. Любопытство стремится понять и постичь необъятное, не познанное, разобраться, как оно там работает, чего делает, как двигается. А лень изобретает, чтобы такого придумать, чтобы не вставать, не подходить, не поднимать, не запачкаться или еще чего ни будь важное.
Так как видеть информацию, это лучше, чем разбираться чего там сейчас должно произойти в нашем устройстве, или уже произошло, или происходит, мы обязательно захотим получить эту самую полезную информацию от наших микроконтроллеров, датчиков, или прочих устройств. А получать, я во всяком случае, хочу какие-либо сообщения, вроде вопросов, предупреждений, напоминаний, смайликов, звездочек, сердечек и тому подобное.
Для тех, у кого тоже возникло подобное желание, – вот краткое руководство по подключению и проверке маленьких и не дорогих дисплеев OLED.
Далее речь пойдет об одной из широко доступных для радиолюбителей моделях OLED дисплеев, управляемых чипом SSD1306, с размером экрана 0,96-дюймов и разрешением 128*64 или 128*32 точки. Эти дисплеи идеально подходят для не больших радиолюбительских конструкций и самоделок.
Шаг 1: Основные понятия
Шаг 2: Комплектующие
Шаг 3: Подключение дисплея
Шаг 4: Сканер I2C
Каждое устройство на шине I2C имеет шестнадцатеричный адрес, поменять нельзя, он вшит намертво, каждый ответственный производитель должен где-то на корпусе или в инструкции указать его. Бывают модули с переключателями и перемычками, которыми можно изменить адрес, но… если устройства дешевые, до безобразия, то производитель может и не удосужиться заниматься такой мелочью, поэтому его придется определять самостоятельно.
Всего на шине может использоваться до 127 адресов – 119 для устройств и 8 адресов служебных. Общение ведется по этим адресам. Есть главный, он же Master, а есть ведомый, он же Slave, – Мастера запрашивают, ведомые отвечают, все просто.
Поскольку на нашем OLED-дисплей используется протокол связи I2C, а адрес может быть и не указан, мы сами попробуем узнать этот самый адрес.
Это можно сделать, загрузив коротенький скетч на свою плату Arduino с подключенным OLED. НО!
Не торопитесь сразу заливать скетч в Arduino! Давайте для начала загрузим «драйвера», т.е. подключим библиотеки, а для этого сразу перейдем к «Шагу №5», а затем вернемся и продолжим.
Шаг 4: Продолжение:
Шаг 5: Загрузка и подключение библиотек
Шаг 6: Тестирование дисплея
Откройте файл Adafruit_SSD1306.h в текстовом редакторе и найдите строки:
Должно получиться так:
Если снова ошибка – необходимо проверить правильность соединений.
После окончания загрузки вы увидите тестовую анимацию на экране, это означает, что вы успешно настроили свой OLED дисплей.
Когда вы вдоволь насладитесь сей анимацией, можете переходить к следующему шагу.
Шаг 7: Пишем свое собственное сообщение
Для написания собственного сообщения, сначала создадим новый скетч в среде программирования ArduinoIDE.
В заголовке мы подключаем 4 библиотеки:
Затем пишем протокол сброса:
В VOID SETUP указываем шестнадцатеричный адрес нашего дисплея 0x3C, который мы узнали на «Шаге №4».
Затем, инициализируем дисплей и очищаем его:
Далее в VOID LOOP пишем основной код, то есть наше сообщение, которое хотим отобразить на дисплее.
Для этого описываем размер текста, цвет текста, позицию курсора, и наконец, выводим сообщение с помощью команды println:
OLED дисплеи на сегодняшний день являются самыми совершенными типами дисплеев, и причин тому много: широкие углы обзора, низкое энергопотребление, малая толщина дисплеев и отсутствие необходимости во внешней подсветке. Из недостатков можно выделить ограниченный срок службы дисплея и относительно высокую стоимость. К тому же большинство дисплеев, построенных по технологии OLED применяются в серийно выпускаемых устройствах, а для радиолюбителей доступны лишь некоторые модели дисплеев, ассортимент которых не так уж широк. Одной из доступных для радиолюбителей моделей OLED дисплеев является 0,96-дюймовый монохромный дисплей с разрешением 128*64 пикселя, управляемый чипом SSD1306.
Дисплей был куплен в магазине Banggood, ссылка на товар. Есть они и на AliExpress. Дисплей поставляется в виде модуля, размеры модуля приведены на изображении ниже (замечу что не все размеры на чертеже проставлены правильно). Толщина самого дисплея составляет всего 1,45 миллиметра, толщина модуля с учетом smd-компонентов расположенных на задней стороне печатной платы около 4 миллиметров. Размер модуля 27*27 миллиметров, размер самого дисплея 26,7*19,3 миллиметра, при этом активное рабочее поле дисплея составляет всего 10,9*21,75 миллиметра, пиксели расположены с шагом 0,17 миллиметра, размер пикселя же всего 0,154*0,154 миллиметра.
Чип SSD1306 поддерживает 5 протоколов связи:
- I2C;
- 3-проводной SPI;
- 4-проводной SPI;
- 8-bit 68XX parallel;
- 8-bit 80XX parallel.
В данном модуле используется протокол I2C, но встречаются модули с протоколом SPI и даже с переключением протоколов.
Данная статья является не дословным изложением «датшита» на дисплей. Я рассмотрю основные команды для работы с дисплеем, приведу пример работы с ним. Для тех кто делает первые шаги в программировании микроконтроллеров (для «ардуинщиков») в конце статьи будет приведено описание библиотеки для работы с данным дисплеем. Все примеры так же выполнены в среде ArduinoIDE, ибо статья рассчитана не столько на матерых программистов микроконтроллеров (которые и без моей помощи смогут разобраться в даташите и написать библиотеку), сколько на новичков. К тому же, на мой взгляд важнее понять как работать с дисплеем, нежели увидеть реализацию в какой-то определенной среде программирования. Кроме того, существуют готовые библиотеки для других компиляторов, включая популярную библиотеку U8glib. Их я рассматривать не буду.
Как я уже упоминал, дисплей имеет разрешение 128*64 точки, но ввиду особенностей чипа SSD1306 невозможно получить доступ к каждой точке отдельно. Что бы передать дисплею значение одной точки достаточно одного бита, но данные передаются байтами. Как вы помните, в одном байте – 8 бит, и байта достаточно для изменения значения сразу 8 точек одновременно (в данном случае эти 8 точек расположены в виде вертикального столбца). Поэтому всё поле дисплея по горизонтали разбито на страницы, в каждой странице 8 строчек. Столбцы же остаются столбцами. Итого 8 страниц и 128 столбцов, а значит для изменения значений всех 8192 пикселей на дисплее необходимо передать 1024 байта.
Получается, что одним байтом мы можем изменить значения столбца из 8 пикселей, и информация в столбец записывается сверху вниз. Что бы зажечь левый верхний пиксель дисплея, необходимо в первый столбец первой страницы (не забывайте что счет начинается с 0) записать значение 0х01.
Кроме самих данных, которые необходимо вывести на дисплей, необходимо так же отдавать команды дисплею. Что бы дисплей понял, какой байт является командой, а какой данными, предварительно посылается 0x00 для команд и 0x40 для данных. Причем посылать этот указатель надо каждый раз перед отправкой команд или данных. Команды могут состоять как из одного байта, так и из нескольких. И каждый байт команды посылается отдельно.
Перед вводом данных, необходимо выбрать область дисплея, в которую будет выводиться информация. Для этого необходимо подать команду выбора страниц, затем двумя командами задать начальное и конечное значения. Если информация должна выводиться в одной странице, то начальное и конечное значения должны совпадать. Аналогично и для столбцов. После выбора столбцов и страниц можно начинать передавать данные, и после ввода одного байта в память дисплея счетчик столбцов (или страниц, в зависимости от настроек. Но поскольку мы пишем слева направо сверху вниз, то я буду рассматривать вариант вывода данных на дисплей именно так – в каждую страницу информация записывается слева на право, при заполнении страницы данные выводятся на следующую страницу, которая находится ниже.) переключается на следующее значение.
Как я уже упоминал, для нас естественно писать слева направо сверху вниз и данные мы будем выводить так же. В даташите это называется горизонтальной адресацией.
Но предусмотрен и другой вариант: вывод данных сверху вниз слева направо (вертикальная адресация).
Так же предусмотрен совсем экзотический вариант: вывод данных в одной странице. Если данных вводится больше, чем вмещает страница (более 128 байт), то остальные данные игнорируются (постраничная адресация).
Перед выводом информации на дисплей необходимо произвести инициализацию дисплея. Процесс инициализации подробно описан в даташите, и даже в виде блок-схемы (извиняюсь за ватермарк, не знаю как избавиться от него в файлах pdf).
А теперь приведу пример скетча (написан в среде ArduinoIDE, но это не важно). Следите за комментариями, там подробные описания всех функций.
Зачастую при работе с дисплеями, в которых поле дисплея разбито на страницы, используют «двойную буферизацию» – информацию о состоянии каждого пикселя записывают в массив, а затем одновременно переносят информацию из массива в память экрана. При этом массив будет занимать 1024 байта в памяти микроконтроллера.
Список всех команд конечно же содержится в datasheet-е на дисплей, в том числе команды скроллинга, которые я не стал рассматривать. Даташит достаточно обширный, на 65 страниц. В нем конечно же не рассматриваются примеры работы с буфером.
И наконец, рассмотрим готовую библиотеку для ардуино.
Я нашел несколько библиотек для работы с данным дисплеем, и у каждой из них свои особенности:
- OzOLED – поддерживает вывод изображений (bitmap), текста и цифр, а так же инверсию и скроллинг. Не поддерживает графические примитивы и содержит один шрифт. Проста в освоении, но мало функциональна.
- U8glib – пожалуй самая универсальная библиотека для различных типов дисплеев, умеет многое, но сложна в освоении. Это связано в первую очередь с тем, что сразу не понятно, какая функция будет работать с определенным дисплеем (например, как поведет себя функция выбора цвета RGB на монохромных дисплеях).
- OLED_I2C – самая удобная на мой взгляд библиотека. Поддерживает вывод изображений, текста и чисел, графических примитивов, имеет в своем составе несколько шрифтов, в том числе есть вариант библиотеки с русским и украинским шрифтами (количество шрифтов можно расширить) и имеет двойную буферизацию изображения (что одновременно и хорошо и плохо).
Итак… Библиотека OLED_I2C , довольно простая библиотека. Открываем keywords.txt и видим перечисление всех функций библиотеки. Так же открываем OLED_I2C.h чтобы посмотреть, какие аргументы может принимать та или иная функция.
Создадим каркас скетча.
Замечу, что не обязательно в начале каждого скетча настраивать яркость дисплея, по умолчанию яркость настраивается на максимум. Яркость дисплея можно установить в диапазоне 0-255. При этом 0 не означает что дисплей совсем не будет светиться, я вообще не заметил большой разницы в яркости дисплея.
Функция clrScr очищает на самом деле не дисплей, а буфер. А функция update переносит информацию из буфера в дисплей. У функции clrScr есть противоположная функция fillScr, которая зажигает все точки дисплея.
Предполагается, что исходным состоянием пикселя является не активное состояние, то есть пиксель погашен. В активном состоянии пиксель светится. Таким образом вся информация на дисплей выводится «белым по черному» (мой дисплей реально черно-белый, но встречаются черно-голубые и даже черно-желто-голубые). Но можно выводить информацию наоборот, «черным по белому». При этом у каждой функции, что вводит данные в буфер, есть инверсная функция. Я буду рассматривать их в паре. Кроме того сам дисплей поддерживает инверсию цвета, «черным по белому» можно выводить информацию всегда.
Начнем с графических примитивов. Все описания функций даны в комментариях.
Данный скетч демонстрирует все доступные графические примитивы и их инверсные аналоги (в скетче я не упомянул лишь одну функцию – invPixel(int x, int y), которая изменяет цвет пикселя на противоположный). Стоит уточнить, что начало координат находится в левом верхнем углу. Координата Х располагается вдоль длинной части дисплея и принимает значения от 0 до 127, координата У вдоль короткой (значения от 0 до 63).
Если ввести команду инверсии экрана сразу, например, в функции setup(), то все графические примитивы будут выводиться сразу черным по белому без неприятного моргания экрана.
Теперь задача посложнее – выведем изображение на экран. Для этого используем функцию drawBitmap. Рассмотрим пример из библиотеки, демонстрирующий вывод изображения.
Пример содержит два файла: собственно сам скетч OLED_I2C_Bitmap.ino и файл в котором хранятся изображения graphics.c. Я приведу немного урезанный пример, что бы не загромождать статью.
Содержимое файла graphics.c:
И содержимое самого скетча:
Можно вставить массив с изображением непосредственно в скетч, тогда скетч будет выглядеть так:
Теперь о том, как создать такой массив из изображения. Сперва изображение нужно создать, я сделал это в обычном «пейнте».
В свойствах изображения задал размер изображения и перевел программу в черно-белый режим.
Изображение сохранил в виде монохромного bmp-файла. Не обязательно переводить программу в монохромный режим, да и сохранить можно даже в jpeg, но результат перевода изображения в массив может быть непредсказуемым.
Далее необходимо использовать программу LCDAssistant. Что бы открыть изображение в меню выбираем File – Load image появившемся диалоговом окне выбираем необходимое изображение. Настройки в программе оставляем по умолчанию, размер устанавливается автоматически, остается ввести только имя массива в Table name. Далее File – Save output, в диалоговом окне вводим имя с расширением файла (я рекомендую расширение txt) и сохраняем файл. Из полученного файла необходим только массив, переносим его в скетч, не забывая поместить его в const uint8_t name[] PROGMEM=<…>; .
Несколько замечаний по выводу изображений на экран:
Отрицательные значения начала координат изображения (левый верхний угол изображения) допустимы, при этом изображение будет отображаться не полностью;
Отрицательные значения размера изображения не допустимы, изображение в этом случае не будет выведено на дисплей;
Необходимо указывать верные размеры изображения в последних двух аргументах функции. Если указать меньшую высоту, то изображение будет выведено не полностью (это можно использовать для эффекта плавного появления изображения). Если не верно указать ширину, то вместо изображения можно получить кашу из пикселей.
9
И наконец расскажу о выводе текста и цифр.
Шрифты содержатся в файле библиотеки DefaultFonts.c и представляют собой массив, так же как массив изображения. По сути в одном шрифте содержатся много изображений каждого символа шрифта.
Рассмотрим пример программы, выводящей текст, целые и дробные числа. Объяснение по каждой функции даны в комментариях.
Библиотека пригодится и тем, кто программирует микроконтроллеры в настоящих взрослых IDE, так как из библиотеки можно почерпнуть готовые алгоритмы как по работе с дисплеем, так и по работе с буфером.
Пожалуй на этом всё. Если описывать все возможности дисплея (например, скроллинг изображения), рассмотреть подробно работу с буфером, ввод в этот буфер графических примитивов, изображения и текста, то размер статьи будет превышать даже объем даташита. Для изучения всех этих возможностей рекомендую «поковырять» библиотеку OLED_I2C.
Рассмотрим подключение данного OLED дисплея. Вкратце: малюсенький, изображение на экране двухцветное, сверху желтое, остальное голубое. Пользоваться библиотекой от Adafruit одно удовольствие, но учтите, что памяти под бибилиотеку занимает много. Нужно же где то хранить таблицу символов.
Подключение:
- Соединяем проводками дисплей с ардуиной:
- GND — GND;
VCC — V3.3 (можно и в 5V);
SCL — A5;
SDA — A4; - Распаковаем библиотеки Adafruit_GFX и Adafruit_SSD1306 в папку Library
- Открываем пример в верхнем меню Файл -> Образцы -> Adafruit_SSD1306 -> ssd1306_128x64_i2c
- Загружаем в ардуину и наслаждаемся
Однако потом обнаруживается, что не поддерживаются русские буквы. Поискав чуток нашел библиотеку с кирилицей, но набор команд сильно ограничен, только вывод текста и числовых переменных, без выбора размера шрифта.