MicroPython для начинающих — #8 Графика на OLED-дисплее

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 кОм

Необходимые компоненты для OLED

Шина 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):

Схема подключения — урок 8

Установка библиотеки

Необходимо установить библиотеку 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)      # Запись в буфер

Несколько строк данных

Следующий блок кода задаёт общее время задержки в секундах и выводит несколько строк на экран.

Графика на OLED — рисование линий

Первая строка позиционируется по координатам (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 — пиксели

Код:

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 — пересекающиеся линии

Код:

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 — множество линий

Код:

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 — прямоугольники

Код:

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 — эллипс

Код:

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 — полигоны

Код:

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, путешествия и фотография.