MicroPython для начинающих — #8 Графика на OLED-дисплее
Добро пожаловать в восьмой урок серии MicroPython для начинающих от Тони Гудью, цель которой — улучшить ваши навыки программирования на MicroPython с одновременным знакомством с новыми компонентами и техниками кодирования — с использованием Raspberry Pi Pico!
В предыдущем уроке мы показали, как использовать SPI OLED-дисплеи. В этот раз мы рассматриваем I2C-дисплеи — а именно, отображение графики на них!
Мы теперь работаем с этими очень удобными, недорогими и простыми в использовании монохромными OLED-дисплеями 0.96» I2C 128x64 пикселя, использующими интерфейс I2C («eye-squared-C»), которому требуется всего два контакта GPIO для передачи данных. В этом руководстве мы покажем, насколько легко их запустить, и объясним, как они могут отображать текст и графику.
Код из этого урока также прекрасно работает с SPI SSD1315-дисплеем, рассмотренным в нашем предыдущем руководстве, и со многими SSD1306-дисплеями от Waveshare и Adafruit.
Что вам понадобится
Предполагается, что вы уже установили Thonny на свой компьютер и настроили Raspberry Pi Pico с актуальной прошивкой MicroPython (UF2). Если нет — обратитесь к руководству по началу работы с Raspberry Pi Pico.
Вам понадобится:
Raspberry Pi Pico с контактными штырьками
Макетная плата (breadboard) полного размера
Кабель Micro-USB (для питания и программирования Pico)
OLED-дисплей 0.96» I2C 128x64 пикселя
Соединительные провода (папа/папа и папа/мама, желательно разных цветов)
Дополнительно — потенциометр на 10 кОм
Шина I2C
Шина I nter- I ntegrated C ircuit (I2C) — это двухпроводной последовательный интерфейс, первоначально разработанный компанией Phillips Corporation в 1982 году.
В оригинальном описании использовалась некорректная терминология. Мы будем называть устройство, генерирующее сигнал CLOCK (SCL) — Pico, — основным устройством (MAIN), а другое устройство — дисплей — вспомогательным (SUB-NODE). Второй сигнал, SERIAL DATA (SDA), может работать в обоих направлениях, но при работе с дисплеем мы только отправляем данные с Pico на дисплей.
Pico имеет два контроллера шины I2C — 0 и 1. Каждое I2C-устройство имеет адрес, который используется для разграничения нескольких вспомогательных узлов, которые могут быть подключены параллельно к одним и тем же контактам GPIO.
Эти дисплеи обычно имеют адрес 0x3C, но некоторые, например от Adafruit, используют 0x3D. Подробнее о шине I2C можно прочитать на сайте circuitbasics.com.
Подключение дисплея
Контакты дисплея подписаны и могут быть подключены в разных местах. Мы будем использовать стандартные I2C-контакты Pico — GPIO 8 (SDA) и GPIO 9 (SCL) на контроллере шины 0.
VCC подключается к 3.3 В, а GND — к контакту GND.
Ползунок потенциометра подключается к ADC2 (GPIO 28), а его крайние выводы — к шинам 3.3 В и GND. В таблице показаны другие возможные варианты подключения:
I2C |
SDA |
SCL |
|---|---|---|
0 |
0 |
1 |
1 |
2 |
3 |
0 |
4 |
5 |
1 |
6 |
7 |
0 |
8 |
9 |
1 |
10 |
11 |
0 |
12 |
13 |
1 |
14 |
15 |
0 |
16 |
17 |
1 |
18 |
19 |
0 |
20 |
21 |
1 |
26 |
27 |
Ваша схема должна выглядеть примерно так (для создания этого изображения использовался Fritzing):
Установка библиотеки
Необходимо установить библиотеку micropython-ssd1306. Это та же библиотека, которую мы использовали в уроке №7.
Если вы ещё не установили её, перейдите по ссылке на урок №7 и следуйте инструкциям по скачиванию и установке библиотеки.
Как это работает
Библиотека ssd1306 создаёт небольшой буфер в памяти Pico, представляющий каждый пиксель экрана дисплея. Каждый раз, когда мы выполняем инструкцию отображения — например, рисуем линию или пишем текст на дисплее — мы вносим изменения в данные, хранящиеся в буфере.
Только при выполнении инструкции oled.show() экран обновляется, делая видимыми изменения, которые мы внесли в буфер. Пропуск этой важнейшей инструкции — наиболее распространённая ошибка начинающих пользователей дисплеев.
Библиотека ssd1306 использует мощную графическую библиотеку framebuf. Она предоставляет инструкции для рисования пикселей, текста и линий, а также прямоугольников, эллипсов и полигонов — как залитых, так и в виде контура. Полную документацию можно найти на сайте docs.micropython.org.
Тестовая программа для I2C-дисплея
Следующая программа проверяет правильность настройки вашего I2C-дисплея и выводит на экран надпись.
Используйте этот код для вашего I2C-дисплея в начале программы для всех последующих примеров. Включите все импорты в начало этого кода.
# Проверка I2C-соединений
# Tony Goodhew - 1st Jan 2024
import time
from machine import Pin, I2C, ADC
import ssd1306
import array # Необходим для полигонов
WIDTH = 128 # Ширина OLED-дисплея
HEIGHT = 64 # Высота OLED-дисплея
sda=machine.Pin(8) # Контакт данных
scl=machine.Pin(9) # Контакт тактового сигнала
i2c=machine.I2C(0,sda=sda, scl=scl, freq=400000) # Контроллер соединения 0
oled = ssd1306.SSD1306_I2C(128, 64, i2c) # Инициализация дисплея ssd1306
# ======== Настройка ssd1306 для I2C ========
# ====== Универсальный код начинается здесь ======
oled.fill(0) # Очистить экран (BLACK)
oled.text("Hello, World!",15,15,1) # Нарисовать текст в буфере — WHITE
oled.show() # Отобразить буфер
Графические функции в Framebuf
Текстовые строки
Наиболее полезная функция — oled.text(string, x, y, c), которая рисует текстовую строку на экране начиная с позиции (x, y) цветом c.
Начало координат экрана (0, 0) находится в верхнем левом углу; x увеличивается вправо, y — вниз.
Пиксель в правом нижнем углу находится на позиции (127, 63). Это монохромный дисплей только с двумя цветами: белый — 1, чёрный — 0. Встроенный шрифт 8x8 пикселей позволяет разместить 16 символов в строке.
Следующая процедура центрирует текстовую строку на строке, задавая значение x равным половине оставшегося пространства:
# ====== Универсальный код начинается здесь ======
def centre(msg, yy, cc): # Процедура центрирования текста на строке
space = WIDTH-(len(msg) * 8) # Символы шириной 8 пикселей
gap = int(space/2)
# text(string, x, y, colour)
oled.text(msg,gap,yy,cc) # Запись в буфер
Несколько строк данных
Следующий блок кода задаёт общее время задержки в секундах и выводит несколько строк на экран.
Первая строка позиционируется по координатам (x, y), остальные используют процедуру центрирования. Мы видим обычный текст, а также два числа — целое и с плавающей точкой — которые перед отображением нужно преобразовать в строки с помощью str().
Код:
delay = 3
# Время ожидания в секундах
oled.fill(0) # Заполнить чёрным
# ( text string, x, y, colour)
oled.text("Hello World!", 16, 10, 1) # Белый в (16,10)
centre("Centred",20,1)
integer = 1638465
centre(str(integer),30,1)
float = 3.142
centre(str(float),40,1)
oled.show() # Отправить буфер на дисплей
time.sleep(delay)
oled.fill(0)
Рисование пикселей и горизонтальных/вертикальных линий
В следующем разделе демонстрируется рисование пикселей, горизонтальных и вертикальных линий.
Код:
oled.text("pixels",5,20,1)
# oled.pixel(x,y,c)
oled.pixel(20,30,1) # (20,30)
oled.pixel(23,31,1) # (23,31)
oled.show()
time.sleep(delay)
oled.text("hline",0,0,1)
# oled.text(string,x,y,c)
oled.hline(5,15,100,1)
oled.show()
time.sleep(delay)
oled.text("vline",70,50,1)
# oled.vline(x,y,w,c)
oled.vline(120,5,55,1)
oled.show()
time.sleep(delay)
Пересекающиеся линии
Следующий код рисует пересекающиеся линии.
Код:
oled.fill(0)
oled.text("line",5,0,1)
# oled.line(x,y,x1,y1,c)
oled.line(0,12,127,63,1) # Слева сверху — справа снизу
oled.line(127,12,0,63,1) # Слева снизу — справа сверху
oled.show()
time.sleep(delay)
Множество прямых линий
Здесь мы рисуем несколько прямых линий, используя цикл для смещения конечных точек.
Код:
oled.fill(0)
oled.text("line",5,0,1)
oled.hline(0,63,128,1)
for i in range(0,64,8): # Рисует пики
oled.line(63,64-i,127-i,63,1)
oled.line(63,64-i,i,63,1)
oled.vline(63,64-i,i,1)
oled.show()
time.sleep(0.06)
oled.show()
time.sleep(delay)
Прямоугольники
В этом разделе демонстрируется работа с прямоугольниками. Мы задаём координаты верхнего левого угла, ширину и высоту. Прямоугольник можно заполнить, добавив дополнительное значение: f = True или 1 для заливки.
Код:
oled.fill(0)
oled.text("rect",5,0,1) # Прямоугольники
# oled.rect(x,y,w,h,c,[f])
oled.rect(0,10,50,20,1) # Контур
oled.rect(30,35,50,20,1,True) # Заливка: True = 1 и False = 0
oled.show()
time.sleep(delay)
Эллипс
Функция эллипса позволяет рисовать эллипсы и окружности. (x, y) — это центр, rx и ry — радиусы, c — цвет, f управляет заливкой. Если rx равно ry — получается окружность.
Код:
oled.fill(0)
oled.text("ellipse",5,0,1) # Эллипсы и окружности
# oled.ellipse(x,y,rx,ry,c,[f])
oled.ellipse(80,15,15,10,1) # Контур
oled.ellipse(83,45,35,10,1,1) # Заливка
oled.ellipse(20,30,15,15,1,True) # Залитая окружность (rx = ry)
oled.ellipse(20,30,10,10,0) # Контур окружности (rx = ry)
oled.show()
time.sleep(delay)
Полигоны (poly)
Функция poly немного сложнее, но очень мощная!
Сначала мы создаём массив (специальный список) целочисленных координат углов требуемой фигуры, используя библиотеку array. C — цвет, f управляет заливкой. Здесь x и y обычно устанавливаются в ноль, и полигон рисуется по фактическим заданным координатам. Ненулевые значения x и y смещают положение полигона, что позволяет легко перемещать его по экрану.
Код:
oled.fill(0)
oled.text("poly",5,0,1) # Полигоны — сложнее
# oled.poly((x, y, coords, c,[f])
# Сначала создаём целочисленный массив координат
hexagon = array.array('I',[30,10, 110,10, 126,30, 110,60, 30,60, 5,30]) # 6 пар
# Рисуем полигон
oled.poly(0,0, hexagon, 1, 1) # Залитый шестиугольник
triangle = array.array('I', [30,12,120,30,33,55])
oled.poly(0,0, triangle, 0, True) # Заливка
triangle = array.array('I', [37,18,100,30,45,44])
oled.poly(0,0, triangle, 1, ) # Контур
oled.show()
time.sleep(delay)
Что попробовать
Вставьте фрагменты кода из урока выше в Thonny, под кодом настройки дисплея, чтобы получить единую программу, и запустите её на своём устройстве.
Нарисуйте треугольник внутри четырёхугольника, внутри пятиугольника, внутри шестиугольника.
Нарисуйте небольшой залитый треугольник в верхнем левом углу экрана. Используя цикл, перемещайте треугольник вправо и вниз по экрану, каждый раз изменяя значения x и y при перерисовке. (В начале цикла очищайте экран с помощью oled.fill(0).)
Бонусная программа!
Поскольку пространство ограничено, мы предоставляем этот бонусный код, который иллюстрирует анимацию и расширенные текстовые возможности с использованием другого шрифта в четырёх размерах.
Код хорошо прокомментирован и должен быть лёгким для понимания. В нём есть вступительные последовательности и разделы, в которых: отскакивает мяч, отображается меняющийся ввод от потенциометра на линейном индикаторе и строится график косинуса.
Скачать код можно здесь: SSD1306_Animations.py
Об авторе
Эта статья написана Тони Гудью. Тони — бывший учитель информатики, начавший писать код ещё в 1968 году, когда это называлось программированием — он начал с FORTRAN IV на IBM 1130! Активный участник сообщества Raspberry Pi, его основные интересы сейчас — программирование на MicroPython, путешествия и фотография.