Второй OLED в моей коробке был вытянутым: 0.91 дюйма, узкая полоска 128×32, купленная для щитка на даче, где высота полки — четыре сантиметра. Я не глядя залил на него дашборд, собранный для 128×64: шапка встала, крупная цифра встала, а подвал с временем исчез. Не помигал, не вылез за край — его просто не существует: я напечатал строку в пиксели y = 50..57, которых у стекла нет. Модуль честно показал верхнюю половину кадра и промолчал про остальное.
Грабля элементарная, но прячется она за тем, что компилятор молчит: конструктору безразлично, какое стекло подключено. Библиотека рисует в буфер тех размеров, что вы ей задали, а контроллер показывает столько строк, сколько у него физически есть. Расхождение двух чисел видно только глазами — Serial при этом чист, и вы час ищете сбой в проводке.
Здесь разберём компактный модуль по-взрослому: где живут его 512 байт буфера, как перестроить сетку с восьми строк на четыре, что менять в скетче для 128×64 по пунктам и когда вытянутый экран вообще лучший выбор. База уже на руках: арифметику пикселей и байт мы считали в первом уроке про OLED, а сетку символов и курсор — в уроке про текст и setCursor.
Половина высоты: арифметика 128×32
Начнём с памяти, потому что именно тут компактный модуль внезапно выигрывает. Кадр 128×32 — это 4096 пикселей, по биту на пиксель — ровно 512 байт буфера. Против 1024 у старшего брата — минус половина, а от всей SRAM Uno — четверть вместо половины. На плате, где рядом живут датчики, буферы Serial и ваши переменные, 512 освобождённых байт — не косметика: это десяток переменных long или солидный журнал событий, который раньше не помещался.
Сетка текста сжимается вдвое по вертикали, и это надо принять сразу. При size(1) — клетка 6×8 — по горизонтали остаются те же 21 символ, по вертикали ровно четыре строки: y = 0, 8, 16, 24. Size(2) даёт 10 символов и 2 строки — крупный шрифт занимает половину высоты экрана, и это надо закладывать в композицию заранее. Ось y живёт в диапазоне 0–31: setCursor(0, 32) на настоящем модуле — уже печать за нижним краем, содержимое молча не рисуется.
Шина тоже дышит легче. Кадр — 512 байт, каждый байт по I2C едет девятью битами, значит на 100 кГц передача занимает около 46 мс против 92 у 128×64 — считалку кадра мы строили в уроке про буфер и display(). Разгон до 400 кГц сжимает и эти полкадра до ~12 мс. Для частоты обновления вывод простой: на 128×32 запас по кадрам в секунду вдвое выше при прочих равных.
И тихий бонус наперёд: светящаяся площадь у компактного модуля вдвое меньше, значит, и потребление при той же по духу картинке — примерно вдвое ниже. Полную арифметику миллиампер и часов жизни мы считаем в уроке про питание и ресурс, там же — выключатель экрана для батарейных узлов.
Всё, что мы проходили про шрифты и выравнивание, переносится без изменений — клетка 6×8 одна и та же. Приём правого выравнивания x = 128 − 6×длина работает как работал, две клетки запаса по ширине остаются при нас. Меняется только высота листа: вместо восьми строк — четыре.
| Параметр | 128×64 | 128×32 |
|---|---|---|
| Буфер кадра в ОЗУ | 1024 байта — половина SRAM Uno | 512 байт — четверть SRAM |
| Строки size(1) | 8 строк, y = 0..56 | 4 строки, y = 0..24 |
| Крупный шрифт size(2) | 4 строки по 10 символов | 2 строки по 10 символов |
| Кадр по I2C на 100 кГц | ~92 мс | ~46 мс |
| Типичное применение | дашборды, меню, графики | табло-строка, статус щитка, узкие корпуса |
Перенос готового скетча я теперь делаю по чек-листу из четырёх пунктов, и первый — единственный обязательный: размеры в конструкторе. Adafruit_SSD1306 display(128, 32, &Wire, -1) — буфер совпадает со стеклом, и это закрывает девять бед из десяти. Второй пункт — ревизия курсоров: каждый setCursor с y больше 31 либо поднимается наверх, либо строка исчезает из интерфейса насовсем.
Третий пункт — компоновка. Дашборд из восьми строк в четыре целиком не утрамбовать, и пытаться — путь к каше. Я выбираю, что показывать честно: шапка, одно крупное значение, строка статуса. Четвёртый — тайминги: если расписание обновлений писалось под 92-миллисекундный кадр, интервалы можно ужать — шина теперь отдаёт полкадра за 46 мс, и запас у неё появился. Схему живого цикла без delay для этого берём из урока про millis.
Примеры из библиотеки тоже проверяйте на типоразмер: в Файл → Примеры → Adafruit SSD1306 лежат отдельные скетчи под каждый экран — в том числе ssd1306_128x32_i2c. Если вы работали только с примером под 128×64, значит, адаптировали вручную то, что авторы уже сделали за вас: конструктор, высоту раскладки, число строк заставки.
Отдельная история — скетчи из интернета. Если автор писал под 128×64, а у вас вытянутый модуль, не ищите в коде подвоха — его там нет. Меняйте конструктор, пересобирайте зоны по чек-листу, и всё встанет. Обратный перенос — с компактного на старший — тоже тривиален, но с одной ловушкой: нижняя половина экрана будет пустой, и это выглядит как недоделка, хотя код честен до пикселя.
#include <Wire.h>#include <Adafruit_GFX.h>#include <Adafruit_SSD1306.h> Adafruit_SSD1306 display(128, 32, &Wire, -1);long lastTik = 0;int tUlica = -12; void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.setTextColor(SSD1306_WHITE);} void loop() { if (millis() - lastTik >= 1000) { lastTik = millis(); display.clearDisplay(); // весь интерфейс живёт в y = 0..31 display.setTextSize(1); display.setCursor(0, 0); display.print("SHCHITOK 128x32"); display.setCursor(0, 8); display.print("--------------------"); display.setCursor(0, 16); display.print("Ulica: "); display.print(tUlica); display.print(" C"); display.setCursor(0, 24); display.print("Ryad 4: bufer 512 B"); display.display(); }}Когда 128×32 лучше — и грабля с уехавшим текстом
Вытянутый экран я ставлю в трёх случаях. Первый — узкий корпус: щиток, дверная коробка, кожух заводского прибора, где высота в дефиците. Второй — однострочные табло: статус поливалки, очередь в мастерской, бегущая строка — там восемь строк не нужны вовсе. Третий — батарейные узлы на Uno, где каждый байт SRAM на счету: 512 байт буфера против 1024 — половина выигрыша без смены библиотеки. Мнение с границей: если интерфейс задуман с меню из пяти пунктов или графиком, экономить высоту я не стал бы — вернётесь к переезду через месяц.
Теперь грабля из начала урока, переформулированная как диагноз. Симптом: шапка и верхние строки на месте, нижние отсутствуют, Serial молчит, скетч собирается. Проверка занимает минуту — откройте конструктор. Если там 64, а модуль вытянутый, это ваш случай: библиотека рисует буфер на 64 строки, контроллер показывает верхние 32, всё ниже — за краем мира. Симметричный вариант: конструктор 32 на модуле 128×64 — тогда нижняя половина стекла пустует при живом коде.
Вторая ипостась той же грабли — курсоры. Даже при верном конструкторе одна забытая setCursor(0, 50) от старого скетча уволокет строку за край. Я лечу это привычкой держать все y интерфейса в константах и менять их в одном месте. На экране из четырёх строк констант четыре — проверить их глазами быстрее, чем пересобирать скетч третий раз.
Про симулятор честно: его панель нарисована под 128×64, поэтому скетч ниже занимает верхние четыре строки и не трогает нижнюю половину — ровно то, что покажет настоящий модуль.
- Кадр 128×32 — 512 байт буфера: четверть SRAM Uno против половины у 128×64
- Сетка: size(1) — 21 символ × 4 строки (y = 0, 8, 16, 24); size(2) — 10 × 2
- Перенос скетча — чек-лист: конструктор (128, 32, ...), ревизия курсоров y > 31, перестройка зон, пересмотр таймингов
- Кадр по I2C — ~46 мс на 100 кГц: вдвое легче старшего брата, запас по частоте кадров вдвое выше
- Пустой низ экрана — три причины: неверный конструктор, y за 31, крупный шрифт у нижнего края
- 1. Сколько байт занимает буфер кадра OLED 128×32?
- 2. Какие строки занимает текст при size(1) на 128×32?
- 3. Сколько примерно идёт кадр 128×32 по I2C на 100 кГц?
- 4. Симптом: шапка на месте, нижние строки интерфейса отсутствуют, скетч собирается. Первая проверка?
- 5. Что произойдёт при setCursor(0, 32) на настоящем модуле 128×32?
Компактный модуль перестал быть тёмной лошадкой: 512 байт, четыре строки, полкадра по шине — и те же шрифты, те же приёмы выравнивания, что и на старшем экране из сравнения с LCD. Дальше по разделу — два сюжета, где компактность важна вдвойне: как обновлять экран, когда ОЗУ платы на исходе, и сколько миллиампер и часов жизни стоит у этого стекла каждая горящая строка.
И последняя мелочь в копилку: сохраните каркас из code-блока отдельным файлом рядом с каркасом для 128×64. Два скелета под два типоразмера закрывают всю линейку модулей 0.91–1.3 дюйма, которые встретятся в коробках и на барахолках, — и оба начинаются с одного и того же begin, меняются только числа в конструкторе и высота раскладки.
Домашнее задание: возьмите свой дашборд из урока про буфер и перенесите на 128×32 по чек-листу — конструктор, курсоры, зоны, тайминги. В симуляторе соберите вариант из четырёх строк, на плате проверьте его на вытянутом модуле, если он есть: раскладка совпадёт пиксель в пиксель. Отдельно выпишите из своего кода все setCursor и убедитесь, что ни один не вылезает за y = 31 — заодно у вас появится список констант зон, который окупается при каждом переезде.
Частые вопросы по теме урока
- Сколько байт занимает буфер кадра OLED 128×32?
- 128 × 32 / 8 = 512 байта — четверть SRAM Uno. Библиотека выделяет буфер по размерам из конструктора, поэтому размеры обязаны совпадать с модулем, иначе буфер и стекло разъезжаются. 512
- Какие строки занимает текст при size(1) на 128×32?
- По вертикали 32 пикселя, клетка 8 — четыре строки. Печать на y = 32 и ниже уходит за физический край стекла. Четыре: y = 0, 8, 16, 24
- Сколько примерно идёт кадр 128×32 по I2C на 100 кГц?
- 512 байт × 9 бит / 100 000 бит/с ≈ 46 мс. Разгон шины до 400 кГц сжимает полкадра до ~12 мс. Около 46 мс
- Симптом: шапка на месте, нижние строки интерфейса отсутствуют, скетч собирается. Первая проверка?
- Классика переноса: конструктор остался 128×64, а модуль вытянутый. Буфер рисует больше, чем стекло показывает, — и всё, что ниже, пропадает. Посмотреть размеры в конструкторе
- Что произойдёт при setCursor(0, 32) на настоящем модуле 128×32?
- Ось y живёт в диапазоне 0–31. За краем печать просто не рисуется и без сообщений в Serial: зоны считайте до кода, а не после первого запуска — на бумаге это десять секунд. Печать уйдёт за нижний край — текст не нарисуется