Микроконтроллеры STM32: работа с OLED-экранчиками на базе SSD1306 по I2C и SPI
26 марта 2018
Ранее в статье Микроконтроллеры STM32: работа с экранчиком 1602 по I2C мы научились выводить текст на HD44780-совместимый ЖК-индикатор с I2C-адаптером на базе чипа PCF8574. Эти индикаторы хороши тем, что они не дороги, имеют подсветку и позволяют выводить крупный текст. Но при этом они не могут похвастаться большой скоростью перерисовки, широким углом обзора, или возможностью выводить графическую информацию. Плюс к этому, они довольно громоздки, что в определенных задачах может быть неудобно. Поэтому сегодня мы поговорим о популярных OLED-дисплеях на базе чипа SSD1306, лишенных названных недостатков.
Fun fact! В этом блоге OLED-экранчик с I2C-интерфейсом на базе SSD1306 ранее уже упоминался в статье Используем джойстик от Sega Genesis в проектах на Arduino.
Такие дисплеи бывают разных размеров. Дисплей с диагональю 0.96 дюймов стоит на eBay около 3$, а аналогичный дисплей с диагональю 1.3 дюйма — около 7$. Еще есть совместимые дисплеи на базе чипа SSD1309 с диагональю 1.54 (ценой ~13.5$) и 2.42 дюйма (от 20$). Как правило, дисплеи отображают картинку 128x64 пикселей, но также встречаются и «полоски» 128x32 пикселя, и другие форматы. Пиксели могут быть белыми или синими. Еще встречаются дисплеи, у которых верхние 1/4 пикселей желтые, а нижние 3/4 — синие. Дисплеи на базе SSD1309 бывают белыми, синими, зелеными и желтыми.
Есть также аналогичные дисплеи на базе чипа SH1106. Они лишь частично совместимы с SSD1306, поскольку поддерживают только страничную адресацию пикселей. В связи с этим некоторые библиотеки, написанные для SSD1306, могут с ними не работать. При этом внешне дисплеи на базе SH1106 неотличимы от дисплеев на базе SSD1306. Стоит ли говорить, что в сети можно найти много сообщений о «сломанных библиотеках» и «бракованных дисплеях».
SSD1306 умеет работать по нескольким протоколам, в том числе по SPI и I2C. I2C удобнее использовать, когда хочется поменьше проводов и скорость передачи данных не критична. SPI требует больше проводов, но позволяет передавать данные намного быстрее. Притом, с точки зрения FPS нет большой разницы, что использовать. Например, STM32F103C8T6 легко показывает по I2C более 25 FPS, чего на практике должно быть более, чем достаточно. Для сравнения, по SPI на том же микроконтроллере можно получить до 54 FPS. Но не ясно, зачем это может быть нужно. Субъективно, куда важнее FPS’ов тот факт, что SPI позволяет больше времени тратить не на передачу данных, а какие-то полезные вычисления.
Fun fact! На самом деле, SSD1306 поддерживает два протокола SPI, так называемые 3-wire SPI и 4-wire SPI. Команды и данные у этого чипа передаются в виде байт, и еще один бит, так называемый D/C, нужен для того, чтобы отличить команды от данных. Так вот, отличие протоколов состоит в том, что в 3-wire SPI данные передаются блоками по 9 бит — бит D/C плюс один байт, а 4-wire SPI использует обычный 8-и битный SPI, плюс дополнительный провод для передачи бита D/C. Далее под SPI я буду понимать исключительно 4-wire SPI, так как чаще всего почему-то пользуются именно им.
Соответственно, дисплеи обычно продаются в виде I2C- или SPI-модулей, имеющих пины с шагом 2.54 мм. Но также продаются и дисплеи без какой-либо обвязки, имеющие только шлейф, идущий непосредственно к чипу. Интересно, что SPI-модули обычно имеют шелкографию, объясняющую, как перепаять их в I2C-модуль или модуль, работающий по 3-wire SPI. Например, я вполне успешно перепаял вот такой SPI-модуль от WaveShare, чтобы он работал по I2C. Увы, чтобы переделанный модуль работал корректно, ему приходится подавать на пин Reset сначала низкое напряжение, а затем высокое, что не требуется в обычных I2C-модулях. Но в остальном он работает без нареканий.
На приведенном фото слева изображен экранчик с диагональю 1.3", белыми пикселями и работающий по I2C. Справа от него — похожий экранчик, но с диагональю 0.96" и работающий по SPI. На фото не очень хорошо видно, но у этого экранчика 3/4 пикселей, находящихся ближе к пинам, выводятся через строчку. Я не уверен, то ли это брак, то ли экранчик таким и задуман, например, для снижения энергопотребления. По крайней мере, смотрится вполне симпатично. Заметьте также, что изображение у него выведено перевернутым на 180 градусов. Это уже было сделано намерено в коде прошивки, протокол SSD1306 такое позволяет. Оба экранчика подключены к отладочной плате Nucleo-F411RE.
Следующие два экранчика подключены к плате Blue Pill на базе МК STM32F103. Первый экранчик 0.96" — этот тот самый модуль от WaveShare, который я перепаял обратно, чтобы он снова работал по SPI. Верхние 1/4 писелей у этого дисплея желтые, а нижние 3/4 — синие. За ним идет экранчик, также имеющий диагональ 0.96", но с белыми пикселями и работающий по I2C. Кстати, все четыре дисплея могут питаться как от 3.3 В, так и от 5 В.
Было протестировано несколько библиотек для работы с такими экранчиками. В итоге, больше всего мне понравилась 4ilo/ssd1306-stm32HAL. Позже я выяснил, что ее Google и выдает первой по запросу «stm32 ssd1306 library». Библиотека отличная, заводится с пол-оборота, содержит всего 150 строк кода, работает без нареканий. К сожалению, она умеет работать только по I2C. Поэтому поддержку SPI мне пришлось дописать самостоятельно, ну и заодно отрефачить кое-что по мелочи. Ссылку на получившуюся в итоге библиотеку ищите в конце поста.
В ходе работы над библиотекой я узнал, что дисплей 1.3" почему-то немного иначе адресует пиксели, чем дисплей 0.96". Чтобы на нем все отображалось корректно, мне пришлось переопределить ширину экрана:
C_DEFS = \
-DUSE_HAL_DRIVER \
-DSTM32F411xE \
-DSSD1306_USE_I2C -DSSD1306_WIDTH=130 \
-DSTM32F4 # for ssd1306 library
Основной же код был написан так, чтобы он ничего не выводил в первом и втором столбце пикселей, потому что этот экранчик их не отображает. В результате получилось следующее:
#include <string.h>
void ssd1306_TestFonts() {
ssd1306_Fill(Black);
ssd1306_SetCursor(2, 0);
ssd1306_WriteString("Font 16x26", Font_16x26, White);
ssd1306_SetCursor(2, 26);
ssd1306_WriteString("Font 11x18", Font_11x18, White);
ssd1306_SetCursor(2, 26+18);
ssd1306_WriteString("Font 7x10", Font_7x10, White);
ssd1306_UpdateScreen();
}
void ssd1306_TestFPS() {
ssd1306_Fill(White);
uint32_t start = HAL_GetTick();
uint32_t end = start;
int fps = 0;
char message[] = "ABCDEFGHIJK";
ssd1306_SetCursor(2,0);
ssd1306_WriteString("Testing...", Font_11x18, Black);
do {
ssd1306_SetCursor(2, 18);
ssd1306_WriteString(message, Font_11x18, Black);
ssd1306_UpdateScreen();
char ch = message[0];
memmove(message, message+1, sizeof(message)-2);
message[sizeof(message)-2] = ch;
fps++;
end = HAL_GetTick();
} while((end - start) < 5000);
HAL_Delay(1000);
char buff[64];
fps = (float)fps / ((end - start) / 1000.0);
snprintf(buff, sizeof(buff), "~%d FPS", fps);
ssd1306_Fill(White);
ssd1306_SetCursor(2, 18);
ssd1306_WriteString(buff, Font_11x18, Black);
ssd1306_UpdateScreen();
}
void ssd1306_TestAll() {
ssd1306_Init();
ssd1306_TestFPS();
HAL_Delay(3000);
ssd1306_TestFonts();
}
Полную версию кода вы найдете на GitHub. Узнать больше о чипе SSD1306 можно из его даташита [PDF].
Библиотека была протестирована только на микроконтроллерах семейства STM32F1 и STM32F4, так как других у меня под рукой попросту нет. Если вас не затруднит проверить ее на других микроконтроллерах и сообщить о результатах в комментариях, я был бы крайне признателен. Также прямо сейчас у меня нет возможности проверить, работает ли библиотека с экранчиками на базе SH1106 и SSD1309. Если вы можете протестировать это, мне также крайне хотелось бы узнать о результатах. Наконец, отмечу, что я с радостью приму патчи, добавляющие в библиотеку, скажем, вывод геометрических фигур, русские шрифты или поддержку 3-wire SPI.
Дополнение: Проверил, библиотека работает с SSD1309 и SH1106.
Дополнение: Также вас могут заинтересовать посты Работа с цветными OLED-экранчиками на базе SSD1351 и Превращаем VGA-монитор в «большой OLED-экранчик» с помощью iCEstick.
Метки: STM32, Электроника.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.