Библиотека framebuf — это то, что, при разработке на MicroPython, даёт нам возможности по работе с основными графическими элементами. Например — с текстовыми символами, с прямоугольниками, да и с отдельными пикселями. Это позволяет создать множество интересных изображений. Но весьма полезно оснастить MicroPython ещё и возможность рисования закрашенных треугольников, кругов и колец.

Закрашенные круги

В этом руководстве речь пойдёт о реализации этих полезных мелочей. Здесь же будут продемонстрированы возможности недорогого цветного дислея, состыкованного с Raspberry Pi Pico.

Материалы


Дисплей и Raspberry Pi Pico

Для того чтобы воспроизвести то, о чём я расскажу, вам понадобятся следующие комплектующие и программы:

  • Программируемый микроконтроллер Raspberry Pi Pico.
  • Цветной LCD-дисплей Waveshare разрешением 160×80 пикселей с диагональю 0,96 дюйма, выполненный в форм-факторе модуля для Raspberry Pi Pico.
  • USB-кабель.
  • Редактор Thonny.

Шаг 1. Дисплей


Изображение, выведенное на дисплее

У выбранного мной дисплея имеются разъёмы, соответствующие пинам Pi Pico. Для соединения двух устройств достаточно лёгкого нажатия. Правда, не стоит нажимать на экран, давление надо прикладывать только к плате дисплея. Нужно, кроме того, правильно его ориентировать — так, чтобы джойстик находился бы с той стороны, где у Pi Pico расположен USB-разъём. Джойстик и кнопки, которыми оснащён дисплей, будут использованы в наших проектах в роли простых устройств ввода информации.

Обычно, когда рекламируют подобные дисплеи, на них выводят цветные фотографии. Наш дисплей вполне на такое способен. Снимок экрана, приведённый в начале раздела, это подтверждает. Ниже я расскажу о том, как это делается. Правда, большая часть этого материала посвящена не тому, как выводить на дисплей готовые изображения, а тому, как создавать и выводить что-то своё.

Шаг 2. Изучение документации


Справка по плате дисплея

Дисплей

Документацию по дисплею можно найти здесь. На неё стоит взглянуть.

Компания Waveshare предлагает пользователям пример программы с включённым в её состав драйвером дисплея, а не отдельную библиотеку для работы с дисплеем. (Мне нравится такой подход, так как он избавляет от необходимости постоянной возни с библиотеками при смене периферийных устройств).

Вот — моя, немного дополненная, версия этого примера. Она выводит линии и текст, поддерживает работу джойстика и кнопок. Драйвер — это самая важная часть программы. Его код нужно включать в состав всех программ, которые работают с дисплеем.

Шаг 3. Рисование кругов


Теорема Пифагора

Если нарисовать в круге радиус, а так же — вертикальные и горизонтальные линии, формирующие прямоугольные треугольники, можно воспользоваться теоремой Пифагора для того чтобы найди длину стороны a для любого значения i. Если нарисовать горизонтальные линии длины a в обоих направления от вертикального диаметра — они коснутся обеих сторон круга по его границе. Рисование последовательности таких линий, для каждой пиксельной строки, позволит закрасить круг.

def circle(x,y,r,c):
  lcd.hline(x-r,y,r*2,c)
  for i in range(1,r):
    a = int(math.sqrt(r*r-i*i)) # Теорема Пифагора!
    lcd.hline(x-a,y+i,a*2,c) # Нижняя половина
    lcd.hline(x-a,y-i,a*2,c) # Верхняя половина
    lcd.display()
    utime.sleep(0.1)

Если же нарисовать лишь конечные точки линий на границе круга — получится кольцо.

def ring(x,y,r,c):
  lcd.pixel(x-r,y,c)
  lcd.pixel(x+r,y,c)
  lcd.pixel(x,y-r,c)
  lcd.pixel(x,y+r,c)
  lcd.display()
  utime.sleep(0.1)
  for i in range(1,r):
    a = int(math.sqrt(r*r-i*i)) # Теорема Пифагора
    lcd.pixel(x-a,y-i,c)
    lcd.pixel(x+a,y-i,c)
    lcd.pixel(x-a,y+i,c)
    lcd.pixel(x+a,y+i,c)
    lcd.pixel(x-i,y-a,c)
    lcd.pixel(x+i,y-a,c)
    lcd.pixel(x-i,y+a,c)
    lcd.pixel(x+i,y+a,c)
    lcd.display()
    utime.sleep(0.1)

Шаг 4. Треугольники



Разобьём треугольник на две части горизонтальной линией. Закрасим его, двигаясь от верхнего угла вниз и от нижнего угла вверх

Рисование границ треугольника — задача очень простая. Это — всего лишь три прямых линии.

def triangle(x1,y1,x2,y2,x3,y3,c): # Рисуем границы треугольника
  lcd.line(x1,y1,x2,y2,c)
  lcd.line(x2,y2,x3,y3,c)
  lcd.line(x3,y3,x1,y1,c)

А вот рисование закрашенного треугольника — это уже гораздо более сложная задача. Подробности об этом можно почитать здесь.

Предлагаемый метод похож на тот, который мы использовали для закрашивания кругов — для закрашивания треугольника рисуют прямые линии, касающиеся его границ.

Эту задачу легче решить в том случае, если речь идёт о треугольнике, имеющем вертикальную верхнюю или нижнюю сторону. Если такой стороны у треугольника нет, то исходный треугольник можно разделить на две части горизонтальной линией и закрасить получившиеся треугольники по отдельности. Всё это звучит довольно-таки просто, но для практической реализации этого механизма нужна серьёзная математика. Правда, что хорошо, для того, чтобы пользоваться математикой, понимать её не обязательно. А именно, я создал команду, обращение к которой вызывает выполнение довольно-таки сложных действий.

c = colour(0,255,0)
tri_filled(x1,y1,x2,y2,x3,y3,c)
lcd.display()

Полный код можно найти здесь, а результаты работы кода показаны в видеофрагменте, приведённом в следующем разделе. В коде программы содержится несколько команд utime.sleep(0.1), предна��наченных для того, чтобы замедлить работу кода до такого уровня, чтобы можно было бы наблюдать за происходящим.

Шаг 5. Замедленная видеосъёмка процесса вывода треугольника, круга и кольца


Замедленная видеосъёмка

Шаг 6. Эксперименты с дисплеем


Эксперименты с дисплеем

Обычно я, когда покупаю и исследую новый дисплей, а потом пишу об этом, включаю в статью раздел, в котором экспериментирую с этим дисплеем (эти эксперименты я называю «Workout-примерами»). Делаю я это для того, чтобы помочь разработчикам понять, подойдёт ли он для их проектов. Вот — такой раздел для дисплея, описываемого в этом материале. Он выполняет всё то, что выполняют другие исследованные мной дисплеи. Это позволяет сравнивать скорость вывода информации и качество формируемой картинки. Здесь я использую джойстик и кнопки, вывожу тригонометрические графики и нечто вроде столбчатых диаграмм, показываю текст разных размеров, градиенты, а теперь — и круги с треугольниками.

Надеюсь, вам понравилось то, что вы видели.

Программа, в которой всё это реализовано, получилась довольно длинной — 828 строк кода. Но её удалось уложить хотя бы в такой размер только благодаря тому, что этому миниатюрному экрану не нужен огромный фрейм-буфер. Это — реальный плюс экранов, состоящих из небольшого количества пикселей, если речь идёт о необходимости работы с большими объёмами графического кода.

Из кода этой программы, там, где выводятся круги и треугольники, убраны все задержки. Её скорость меня впечатлила, но в её распоряжении имеется очень небольшой буфер для копирования данных через SPI при каждой перерисовке экрана.

Шаг 7. Вывод фотографий



Фотография, выведенная на дисплее

На предыдущем рисунке показана фотография, которую я сделал в Катманду, в храмовом центре Сваямбунатх.

В исходном виде это был .RAW-файл, сформированный фотоаппаратом Canon. Для вывода фотографии на дисплее мне понадобилось выполнить следующие действия:

  1. Я преобразовал файл в формат JPG, обрезал картинку, выдержав соотношение сторон, соответствующее разрешению в 160x80 пикселей, а после этого изменил размер изображения до 160x80 пикселей. Я это делал в Photoshop, но то же самое можно сделать и в бесплатном редакторе Gimp.
  2. Я преобразовал файл в новый RAW-формат, где, вместо 3-байтового цвета используется 2-байтовый цвет.
  3. Файл был скопирован на Pi Pico с Raspberry Pi 4 с использованием rshell.
  4. Изображение было выведено на экран с использованием соответствующей программы и подходящего драйвера экрана.

Как видите, немалая часть этой работы заключается в предварительной подготовке изображения. Если вы хотите сделать что-то подобное — всё необходимое можно найти здесь.

Шаг 8. Компьютерная графика основана на математике


В этом проекте использовалось следующее:

  1. Базовая арифметика: счёт, вычисления, работа с процентными значениями, сравнение величин.
  2. Теория множеств: мэппинг и маскировка битов.
  3. Представление чисел в разных системах счисления при работе с битами и байтами: в двоичной, шестнадцатеричной и десятичной.
  4. Геометрия: круги, треугольники, прямоугольники, линии, точки.
  5. Координатная геометрия: рисование точек и отрезков.
  6. Теорема Пифагора.
  7. Тигонометрия: радианы, синус, косинус.
  8. Списки и суффиксы/указатели: байтовые массивы.
  9. Теория вероятностей: случайные числа.

Применяете ли вы Raspberry Pi Pico в своих проектах?