Библиотека Adafruit_GFX для Arduino предоставляет общий синтаксис и набор графических функций для всех ЖК- и OLED-дисплеев и светодиодных матриц. Данная библиотека позволяет легко адаптировать скетчи Arduino между разными типами дисплеев с минимальными усилиями.
Любые новые функции, улучшения производительности и исправления ошибок будут немедленно применены ко всему существующему полному ассортименту цветных дисплеев.
Adafruit_GFX всегда работает вместе с дополнительной библиотекой, уникальной для каждого конкретного типа дисплея. Такие библиотеки можно установить с помощью Arduino Library Manager. В меню «Скетч» Arduino выберите «Подключить библиотеку», затем «Управлять библиотеками…».
В окне диспетчера библиотек Arduino найдите тип драйвера дисплея (например, «SSD1325»), и в результатах можно будет найти соответствующую библиотеку Adafruit. Требуемые сопутствующие библиотеки, такие как Adafruit_GFX или Adafruit_BusIO, теперь устанавливаются автоматически. Если вы используете более старую версию Arduino IDE, вам придется искать и устанавливать эти дополнительные библиотеки вручную.
Вот некоторые библиотеки, которые работают вместе с Adafruit_GFX:
- RGBmatrixPanel для светодиодных матричных RGB-панелей 16×32 и 32×32.
- Adafruit_TFTLCD для 2,8 -дюймового сенсорного экрана TFT LCD и TFT Touch Shield для Arduino.
- Adafruit_HX8340B для 2,2-дюймового TFT-дисплея с microSD.
- Adafruit_ST7735 , для 1,8-дюймового TFT-дисплея с microSD.
- Adafruit_PCD8544 для монохромного ЖК-дисплея Nokia 5110/3310.
- Adafruit-Graphic-VFD-Display-Library , для графического VFD 128×64.
- Adafruit-SSD1331-OLED-Driver-Library-for-Arduino для 0,96-дюймового 16-битного цветного OLED-дисплея с microSD.
- Adafruit_SSD1306 для монохромных OLED-дисплеев 128×64 и 128×32.
И многие другие, за исключением некоторых очень ранних «старых» продуктов. Помните, что вам просто нужно найти тип драйвера дисплея в диспетчере библиотек Arduino, установить, а остальное теперь происходит автоматически.
Библиотеки написаны на C++ для Arduino, но их можно легко портировать на любой микроконтроллер, переписав низкоуровневые функции доступа к контактам.
Для ранних версий Arduino IDE
Гораздо более ранние версии программного обеспечения Arduino IDE требуют установки библиотек вручную; диспетчера библиотек Arduino еще не существовало. Если вы используете раннюю версию программного обеспечения Arduino, самое время обновить ее. Но, если вы вдруг не хотите обновляться, то нажмите на кнопку для прямой загрузки библиотеки GFX:
Система координат и единицы
Пиксели — элементы изображения, блоки, составляющие цифровое изображение, — адресуются по их горизонтальным (X) и вертикальным (Y) координатам. Система координат помещает начало координат (0,0) в верхний левый угол, при этом положительное значение X увеличивается вправо, а положительное значение Y увеличивается вниз. Это перевернутая по сравнению со стандартной декартовой системой координат математики, но это устоявшаяся практика во многих системах компьютерной графики (возврат к временам растровой ЭЛТ-графики, которая работала сверху вниз). Чтобы использовать высокий «книжный» формат, а не широкий «альбомный» формат, или если физические ограничения диктуют ориентацию дисплея в корпусе, также можно применить одну из четырех настроек поворота, указывающих, какой угол дисплея представляет левый верхний угол.
Кроме того, в отличие от математической декартовой системы координат, точки здесь имеют размерность — они всегда имеют ширину и высоту в один полный целочисленный пиксель.
Координаты всегда выражаются в пикселях; нет неявного масштаба для реальной меры, такой как миллиметры или дюймы, и размер отображаемой графики будет зависеть от шага точек или плотности пикселей этого конкретного дисплея. Если вы стремитесь к реальному измерению, вам нужно масштабировать свои координаты в соответствии с требованиями. Шаг точек часто можно найти в техническом описании устройства или путем измерения ширины экрана и деления количества пикселей по ширине на это измерение.
Библиотека безопасно «обрежет» любую графику, выходящую за края экрана. На самом деле иногда это делается специально, например, при отображении текста с прокруткой.
Для цветных дисплеев цвета представлены в виде 16-битных значений без знака. Некоторые дисплеи физически могут иметь больше или меньше битов, но библиотека работает с 16-битными значениями… с ними легко работать на Arduino, а также они обеспечивают согласованный тип данных на всех различных дисплеях. Основные компоненты цвета — красный, зеленый и синий — все «упакованы» в единую 16-битную переменную, где 5 старших битов соответствуют красному цвету, 6 средних битов — зеленому, а 5 младших битов — синему. Этот дополнительный бит назначен зеленому цвету, потому что наши глаза наиболее чувствительны к зеленому свету.
Для наиболее распространенных первичных и вторичных цветов у нас есть удобная шпаргалка, которую вы можете включить в свой собственный код. Конечно, вы можете выбрать любой из 65 536 различных цветов, но этот базовый список может быть самым простым для начала:
1 2 3 4 5 6 7 8 9 |
// Базовые цвета #define BLACK 0x0000 #define BLUE 0x001F #define RED 0xF800 #define GREEN 0x07E0 #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define WHITE 0xFFFF |
Для монохромных (одноцветных) дисплеев цвета всегда указываются просто как 1 (установить) или 0 (очистить). Семантика установки/очистки зависит от типа дисплея: в чем-то вроде светящегося OLED-дисплея «установленный» пиксель подсвечивается, тогда как в отражающем ЖК-дисплее «установленный» пиксель обычно темный. Могут быть исключения, но обычно вы можете рассчитывать на 0 (очистить), представляющий фоновое состояние по умолчанию, для только что инициализированного дисплея, каким бы он ни был.
Графические примитивы
Каждая библиотека дисплея для конкретного устройства будет иметь свои собственные конструкторы и функции инициализации. Они задокументированы в отдельных руководствах для каждого типа дисплея или часто очевидны в заголовочном файле конкретной библиотеки. В оставшейся части нашей статьи будут рассматриваться общие графические функции, которые работают одинаково независимо от типа дисплея.
Приведенные ниже описания функций являются просто прототипами — предполагается, что экранный объект объявляется и инициализируется по мере необходимости библиотекой для конкретного устройства. Посмотрите на пример кода с каждой библиотекой, чтобы увидеть ее в реальном использовании. Например, там, где мы показываем print(1234.56), ваш фактический код поместит имя объекта перед этим, например, это может быть screen.print(1234.56) (если вы объявили свой экранный объект с именем экрана).
Отображение пикселя (точки)
Прежде всего, это самый простой код указания координат пикселей. Вы можете прописать координаты X, Y с указанием цвета, которые отобразят на дисплее одну точку:
1 |
void drawPixel(uint16_t x, uint16_t y, uint16_t color); |
Отображение линии
Вы также можете отображать линии на дисплее с начальной и конечной точкой и цветом:
1 |
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color); |
Для горизонтальных или вертикальных линий существуют оптимизированные функции рисования линий, которые позволяют избегать угловых вычислений:
1 2 |
void drawFastVLine(uint16_t x0, uint16_t y0, uint16_t length, uint16_t color);<font></font> void drawFastHLine(uint8_t x0, uint8_t y0, uint8_t length, uint16_t color); |
Отображение прямоугольника
Также, можно нарисовать и заполнить цветом прямоугольники и квадраты, используя следующие параметры. Каждый принимает пару X, Y для верхнего левого угла прямоугольника, ширину и высоту (в пикселях) и цвет. drawRect() отображает только рамку (контур) прямоугольника — внутренняя часть не затрагивается — в то время как fillRect() заполняет всю область заданным цветом:
1 2 |
void drawRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t color);<font></font> void fillRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t color); |
Чтобы создать сплошной прямоугольник с контрастным контуром, сначала используйте fillRect(), а затем поверх него drawRect().
Отображение круга
Точно тоже применяемо для кругов, можете рисовать и заполнять. Каждая функция принимает пару X, Y для центральной точки, радиус в пикселях и цвет:
1 2 |
void drawCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);<font></font> void fillCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color); |
Отображение прямоугольников со скругленными углами
Для прямоугольников со скругленными углами также доступны функции отображения и заливки. Каждый начинается с X, Y, ширины и высоты (как обычные прямоугольники), затем указывается радиус угла (в пикселях) и, наконец, цвет:
1 2 |
void drawRoundRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t radius, uint16_t color);<font></font> void fillRoundRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t radius, uint16_t color); |
Вот дополнительный секрет: поскольку функции круга всегда отображаются относительно центрального пикселя, результирующий диаметр круга всегда будет нечетным числом пикселей. Если требуется круг четного размера (который поместит центральную точку между пикселями), этого можно добиться с помощью одной из функций прямоугольника со скругленными углами: передать одинаковые значения ширины и высоты, которые являются четными значениями, и радиус угла, равный ровно половине этого значения.
Отображение треугольников
Треугольники тоже имеют функции отображения и заполнения. Для каждого требуются свои параметры: координаты X, Y для трех угловых точек, определяющих треугольник, за которыми следует цвет:
1 2 |
void drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);<font></font> void fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); |
Отображение текста
Есть две основные процедуры отображения строк для добавления текста. Первый предназначен только для одной строки, где вы можете разместить ее в любом месте и с любым цветом. Можно передать необязательный параметр размера, который масштабирует шрифт по этому коэффициенту (например, size=2 будет отображать шрифт по умолчанию с размером 10×16 пикселей на символ). Это немного блочно, но наличие всего одного шрифта помогает уменьшить размер программы.
1 |
void drawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bg, uint8_t size); |
Другой тип текста работает немного по-другому. Размер текста, цвет и положение настраиваются в отдельных функциях, а затем используется функция print() — это упрощает работу и предоставляет все те же возможности форматирования строк и чисел, что и знакомая Arduino Serial.print( ) и функции println() ! Но вы ставите перед ними экранный объект вместо Serial.
1 2 3 4 5 |
void setCursor(int16_t x0, int16_t y0);<font></font> void setTextColor(uint16_t color);<font></font> void setTextColor(uint16_t color, uint16_t backgroundcolor);<font></font> void setTextSize(uint8_t size);<font></font> void setTextWrap(boolean w); |
Начните с setCursor(x, y), который разместит верхний левый угол текста там, где вам угодно. Первоначально установлено значение (0,0) (верхний левый угол экрана). Затем установите цвет текста с помощью setTextColor(color) — по умолчанию это белый цвет. Текст обычно отображается «четким» — открытые части каждого символа показывают исходное содержимое фона, но если вы хотите, чтобы текст блокировал то, что находится под ним, цвет фона можно указать в качестве необязательного второго параметра для setTextColor(). Наконец, setTextSize(size) умножит масштаб текста на заданный целочисленный коэффициент. Ниже вы можете увидеть масштабы 1 (по умолчанию), 2 и 3. Текст выглядит блочным при больших размерах, потому что мы загрузили библиотеку только с одним простым шрифтом для экономии места.
Цвет фона текста не поддерживается для пользовательских шрифтов. Для него вам нужно будет определить границы текста и явно отобразить заполненный прямоугольник перед текстом. |
После того, как вы все настроите, вы можете использовать print() или println(). Например, чтобы отобразить строку, используйте print(«Hello world») — это первая строка изображения выше. Вы также можете использовать print() для чисел и переменных — вторая строка выше — это вывод print(1234.56), а третья строка — print(0xDEADBEEF, HEX).
По умолчанию длинные строки текста настроены на автоматический «перенос» обратно в крайний левый столбец. Чтобы переопределить это поведение (чтобы текст выходил за правую часть экрана — полезно для эффектов бегущей строки), используйте setTextWrap(false). Нормальное поведение переноса восстанавливается с помощью setTextWrap(true).
CP437 и скрытая ошибка
Стандартный встроенный шрифт включает ряд символов и знаков с диакритическими знаками помимо обычных букв и цифр, которые вы использовали бы в строках print(). Доступ к ним можно получить с помощью drawChar(), передав 8-битное значение (0–255, хотя обычно выражается в шестнадцатеричном виде, 0x00–0xFF) в качестве третьего аргумента.
Встроенный шрифт основан на оригинальном наборе символов IBM PC, известном как Code Page 437 (сокращенно CP437). Многие встроенные системы до сих пор используют его, поскольку он компактен и хорошо зарекомендовал себя.
Много лет назад, когда CP437 изначально транскрибировался в библиотеку GFX, один символ был случайно пропущен. Ничего фатального, код работает нормально, но каждый последующий символ отстает на единицу по сравнению с «настоящим» набором символов CP437. К тому времени, когда это было обнаружено, было написано так много кода — проектов, опубликованных в Интернете, а также на фиксированных носителях, таких как книги и журналы, — что исправление ошибки сломало бы все существующие проекты, которые полагались на эти расширенные символы!
Таким образом, ошибка была намеренно оставлена на месте, но это создает другую проблему, если кто-то адаптирует код из другого места, который использует правильные значения символов CP437.
Компромиссным решением является функция, которая включает или отключает «настоящую» последовательность CP437. По умолчанию это отключено, используется порядок «выход за один», так что все старые проекты GFX в книгах работают без изменений. Правильный порядок можно включить с помощью:
1 |
display.cp437(true); |
Вот карта встроенного набора символов, как стандартная ошибочная версия, так и исправленная версия, используемая при вызове cp437(true). Обратите внимание, что это влияет только на последние пять рядов символов; все до символа 0xB0 не затрагивается:
Наличие символов расширенной кодовой страницы 437 гарантируется только во встроенном шрифте. Пользовательские шрифты редко включают их.
Растровые изображения
Вы можете отображать небольшие монохромные (одноцветные) растровые изображения, подходящие для спрайтов и других мини-анимаций или иконок:
1 |
void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color); |
Эта строка выводит на дисплей непрерывный блок битов, где каждый бит «1» устанавливает для соответствующего пикселя «цвет», а каждый бит «0» пропускается. x, y — верхний левый угол, в котором рисуется растровое изображение, w, h — ширина и высота в пикселях.
Данные растрового изображения должны быть расположены в памяти программы с помощью директивы PROGMEM. Это несколько продвинутая функция, и новичкам лучше изучить ее позже.
Очистка или заполнение экрана
Функция fillScreen() установит для всего экрана заданный цвет, удалив любой существующий текст:
1 |
void fillScreen(uint16_t color); |
Аппаратно-зависимые функции
Некоторые дисплеи могут иметь уникальные функции, такие как инвертирование экрана или аппаратная прокрутка. Документацию по этим функциям можно найти в соответствующем руководстве по конкретному дисплею. Поскольку эти функции не являются общими для всех дисплеев, совместимых с GFX, они здесь не описываются.
Поворот дисплея
Вы также можете поворачивать свой рисунок. Обратите внимание, что это не повернет то, что вы уже нарисовали, но изменит систему координат для любого нового рисунка. Это может быть очень удобно, если вам пришлось перевернуть плату или дисплей, чтобы его поместить в определенный корпус. В большинстве случаев это нужно сделать только один раз внутри setup().
Поворачивать можно только на 0, 90, 180 или 270 градусов — все остальное невозможно аппаратно и слишком сложно для Arduino.
1 |
void setRotation(uint8_t rotation); |
Параметр поворота может принимать значения 0, 1, 2 или 3. Для дисплеев, являющихся частью платы Arduino, значение поворота 0 устанавливает дисплей в книжный (высокий) формат вверху справа. Значение поворота 2 также является книжным форматом внизу слева. Поворот 1 — это альбомный (широкий) режим внизу справа, а поворот 3 — тоже альбомный, вверху слева.
Для других дисплеев попробуйте все 4 вращения, чтобы выяснить, как они в конечном итоге вращаются, поскольку выравнивание будет различаться в зависимости от каждого дисплея, как правило, все вращения движутся против часовой стрелки.
При вращении исходная точка (0,0) меняется — идея состоит в том, что она должна располагаться в верхнем левом углу экрана, чтобы другие графические функции имели смысл (и соответствовали всем описанным выше функциям).
Если вам нужно указать размер экрана (который будет меняться между книжным и альбомным фориатами), используйте width() и height().
1 2 |
uint16_t width(); uint16_t height(); |
Каждая строка возвращает размер (в пикселях) соответствующей оси с поправкой на текущую настройку поворота дисплея.
Использование шрифтов
Более поздние версии библиотеки Adafruit GFX предлагают возможность использовать альтернативные шрифты помимо одного встроенного стандартного шрифта фиксированного размера и интервала. Включено несколько альтернативных шрифтов, а также есть возможность добавления новых.
Включенные шрифты — это бесплатное семейство масштабируемых контурных шрифтов GNU FreeFont. Есть три шрифта: «Serif» (напоминает Times New Roman), «Sans» (напоминает Helvetica или Arial) и «Mono» (напоминает Courier). Каждый из них доступен в нескольких стилях (жирный, курсив и т. д.) и размерах. Включенные шрифты имеют растровый формат, а не масштабируемые векторы, поскольку они должны работать в рамках ограничений небольшого микроконтроллера.
Расположенные в папке «Шрифты» внутри Adafruit_GFX:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
FreeMono12pt7b.h FreeSansBoldOblique12pt7b.h FreeMono18pt7b.h FreeSansBoldOblique18pt7b.h FreeMono24pt7b.h FreeSansBoldOblique24pt7b.h FreeMono9pt7b.h FreeSansBoldOblique9pt7b.h FreeMonoBold12pt7b.h FreeSansOblique12pt7b.h FreeMonoBold18pt7b.h FreeSansOblique18pt7b.h FreeMonoBold24pt7b.h FreeSansOblique24pt7b.h FreeMonoBold9pt7b.h FreeSansOblique9pt7b.h FreeMonoBoldOblique12pt7b.h FreeSerif12pt7b.h FreeMonoBoldOblique18pt7b.h FreeSerif18pt7b.h FreeMonoBoldOblique24pt7b.h FreeSerif24pt7b.h FreeMonoBoldOblique9pt7b.h FreeSerif9pt7b.h FreeMonoOblique12pt7b.h FreeSerifBold12pt7b.h FreeMonoOblique18pt7b.h FreeSerifBold18pt7b.h FreeMonoOblique24pt7b.h FreeSerifBold24pt7b.h FreeMonoOblique9pt7b.h FreeSerifBold9pt7b.h FreeSans12pt7b.h FreeSerifBoldItalic12pt7b.h FreeSans18pt7b.h FreeSerifBoldItalic18pt7b.h FreeSans24pt7b.h FreeSerifBoldItalic24pt7b.h FreeSans9pt7b.h FreeSerifBoldItalic9pt7b.h FreeSansBold12pt7b.h FreeSerifItalic12pt7b.h FreeSansBold18pt7b.h FreeSerifItalic18pt7b.h FreeSansBold24pt7b.h FreeSerifItalic24pt7b.h FreeSansBold9pt7b.h FreeSerifItalic9pt7b.h |
Каждое имя файла начинается с имени шрифта(«FreeMono», «FreeSerif» и т. д.), за которым следует стиль («жирный», «наклонный», нет и т. д.), размер шрифта в пунктах (в настоящее время 9, 12, 18 и т. д.). предусмотрено 24 размера точек) и «7b», чтобы указать, что они содержат 7-битные символы (коды ASCII от « » до «~»); 8-битные шрифты (поддерживающие символы и/или международные символы) пока не предоставляются, но могут появиться позже.
Использование шрифтов GFX в скетчах Arduino
После подключения Adafruit_GFX и библиотек, специфичных для дисплея, включите файлы шрифтов, которые вы планируете использовать в своем скетче. Например:
1 2 3 4 |
#include <Adafruit_GFX.h> // Core graphics library #include <Adafruit_TFTLCD.h> // Hardware-specific library #include <Fonts/FreeMonoBoldOblique12pt7b.h> #include <Fonts/FreeSerif9pt7b.h> |
Один шрифт занимает не очень много места в программе; для большего числа шрифтов обычно требуется больше места. Это ограниченный ресурс (максимум около 32 КБ на Arduino Uno для данных шрифтов и всего кода вашего скетча ), поэтому выбирайте тщательно. Если будет слишком много шрифтов, то код откажется компилироваться (или, в некоторых крайних случаях, может скомпилироваться, но затем не будет загружен на плату). В этом случае используйте меньшее количество шрифтов или шрифты меньшего размера либо используйте стандартный встроенный шрифт.
Внутри этих файлов .h находится несколько структур данных, в том числе одна основная структура шрифта, которая обычно имеет то же имя, что и файл шрифта (minus the .h). Чтобы выбрать шрифт для последующих графических операций, используйте функцию setFont(), передав адрес этой структуры, например:
1 |
tft.setFont(&FreeMonoBoldOblique12pt7b); |
Последующие вызовы tft.print() теперь будут использовать этот шрифт. Большинство других атрибутов, которые раньше работали со встроенным шрифтом (цвет, размер и т. д.), здесь работают аналогично.
Чтобы вернуться к стандартному шрифту фиксированного размера, вызовите setFont(), передав либо NULL, либо без аргументов:
1 |
tft.setFont(); |
Некоторые текстовые атрибуты ведут себя немного по-другому с этими новыми шрифтами. Не желая нарушать совместимость с существующим кодом, «классический» шрифт продолжает вести себя по-прежнему.
Например, в то время как положение курсора при печати классическим шрифтом определяло верхний левый угол ячейки символа, с новыми шрифтами положение курсора указывает базовую линию — самую нижнюю строку — последующего текста. Символы могут различаться по размеру и ширине и не обязательно начинаются точно в столбце курсора (как показано ниже, этот символ начинается на один пиксель слева от курсора, но другие могут быть на нем или справа от него).
При переключении между встроенными и пользовательскими шрифтами библиотека будет автоматически сдвигать положение курсора вверх или вниз на 6 пикселей, если это необходимо, чтобы продолжать движение по той же базовой линии.
При работе с новыми шрифтами следует помнить об одном нюансе: параметр «фоновый» цвет отсутствует… вы можете установить это значение, но оно будет проигнорировано.
Функция цвета фона иногда используется с «классическим» шрифтом, чтобы перезаписать старое содержимое экрана новыми данными. Это работает только потому, что эти символы имеют одинаковый размер; это не будет работать с пропорциональными шрифтами, где неопределенное количество символов может перекрывать одну и ту же область. Функция отображения текста просто не настроена для такого рендеринга (это было бы непомерно затратно как для памяти, так и для скорости на AVR, которые до сих пор поддерживаются библиотекой).
Чтобы заменить ранее нарисованный текст при использовании пользовательского шрифта, выполните одно из следующих действий:
Используйте getTextBounds(), чтобы определить наименьший прямоугольник, охватывающий строку, сотрите область, используя fillRect(), затем нарисуйте новый текст:
1 2 3 4 |
int16_t x1, y1; uint16_t w, h; tft.getTextBounds(string, x, y, &x1, &y1, &w, &h); |
getTextBounds ожидает строку, начальную позицию курсора X&Y (текущая позиция курсора не будет изменена) и адреса двух 16-битных целых чисел со знаком и двух без знака. Эти последние четыре значения будут содержать верхний левый угол, а также ширину и высоту области, охватываемой этим текстом — их можно будет передать напрямую в качестве аргументов в fillRect().
К сожалению, это приведет к «миганию» текста при стирании и перерисовке, но это неизбежно. Старая схема отрисовки фоновых пикселей за один и тот же проход только создает новый набор проблем.
Но вы можете создать объект GFXcanvas1 (вне экранное растровое изображение) для области фиксированного размера, нарисуйте там пользовательский текст и скопируйте его на экран с помощью drawBitmap().
1 2 3 4 5 6 |
// В глобальных объявлениях: GFXcanvas1 canvas(128, 32); // экран 128x32 пикселей // Позже в коде: canvas.println("I like led"); tft.drawBitmap(x, y, canvas.getBuffer(), 128, 32, foreground, background); // Копируем на экран |
Это иллюстрация синтаксиса, а не полная программа — измените , и X и Y на желаемые координаты и значения цвета, подходящие для дисплея. Некоторые дисплеи также требуют явного вызова display() или show() для обновления содержимого экрана
Должно быть все без мерцания, но потребует больше оперативной памяти (около 512 байт для экрана 128×32 пикселей, показанного выше), поэтому это не всегда практично на платах AVR только с 2K. Arduino Mega или любая 32-битная плата должны работать нормально.
Добавление новых шрифтов
Если вы хотите создать новые размеры шрифтов, не включенных в библиотеку, или адаптировать совершенно новые шрифты, у нас есть для этого инструмент командной строки (в папке «fontconvert»). Он должен работать на многих Linux- или UNIX-подобных системах (среди прочего, Raspberry Pi, Mac OS X, возможно, Cygwin для Windows).
Для создания этого инструмента требуется компилятор gcc и библиотека FreeType. Большинство дистрибутивов Linux включает его в себя. Для других вам может потребоваться установить инструменты разработчика, а также загрузить и собрать FreeType из исходного кода. Затем отредактировать Makefile, чтобы он соответствовал вашей настройке, прежде чем вызывать «make».
fontconvert принимает по крайней мере два аргумента: имя файла шрифта (например, масштабируемый векторный шрифт TrueType) и размер в пунктах (72 пункта = 1 дюйм; код предполагает разрешение экрана, аналогичное 2,8-дюймовым TFT-дисплеям Adafruit) должен быть перенаправлен в файл .h… Вы можете называть его как хотите.
1 |
./fontconvert myfont.ttf 12 > myfont12pt7b.h |
Файлы GNU FreeFont не включены в репозиторий библиотеки, но их легко загрузить. Или вы можете конвертировать почти любой шрифт, который вам нравится.
Имя, назначенное структуре шрифта в этом файле, основано на имени входного файла и размере шрифта, а не на выходе. Вот почему мы рекомендуем использовать описательные имена файлов, включающие базовое имя шрифта, размер и «7b». Тогда имя файла .h и имя структуры шрифта могут совпадать.
Полученный файл .h можно скопировать в папку Adafruit_GFX/Fonts или импортировать файл в качестве новой вкладки в скетче Arduino с помощью команды Sketch→Add File….
В папке «Шрифты», используйте этот синтаксис #including файла:
1 |
#include <Fonts/myfont12pt7b.h> |
Во вкладке скетча, используйте этот синтаксис:
1 |
#include "myfont12pt7b.h" |
Загрузка изображений
Загрузка изображений .BMP с SD-карты (или микросхемы флэш-памяти на платах Adafruit «Express») является опцией для большинства цветных дисплеев… хотя она не встроена в Adafruit_GFX и должна устанавливаться отдельно.
С этой задачей справляется библиотека Adafruit_ImageReader. Ее можно установить через диспетчер библиотек Arduino (Скетч→Подключить библиотеку→Управлять библиотеками…). Введите «imageread» в поле поиска, и библиотеку будет легко найти:
Таким же путем найдите библиотеку Adafruit_SPIFlash и установите ее аналогичным образом.
Требуется еще одна библиотека, но ее нельзя установить через менеджер библиотек. Adafruit fork библиотеки SdFat необходимо загрузить в виде файла .ZIP, распаковать и установить на Arduino.
Использование библиотеки Adafruit_ImageReader
Синтаксис для использования этой библиотеки (и отдельной установки выше), по общему признанию, немного своеобразен … это побочный эффект того, как Arduino обрабатывает библиотеки. Мы намеренно не включили это в Adafruit_GFX, потому что любое простое упоминание о библиотеке SD-карт повлечет за собой более значительные требования к памяти этой библиотеки… даже если какой-то скетч вообще не использует SD-карту! Большинство графических проектов автономны и не ссылаются на файлы с карты… не всем нужен этот функционал.
В папке Adafruit_ImageReader/examples есть несколько примеров скетчей. Рекомендуется разобрать их на идеи, как использовать библиотеку в ваших собственных проектах.
Все они начинаются с нескольких #includes…
1 2 3 4 5 |
# include <Adafruit_GFX.h> // Основная графическая библиотека # include <Adafruit_ILI9341.h> // Библиотека для аппаратного обеспечения # include <SdFat.h> // Библиотека файловой системы SD-карты и FAT # include <Adafruit_SPIFlash.h> // SPI / Флэш-библиотека QSPI # include <Adafruit_ImageReader.h> // Функции чтения изображений |
Одна из этих строк может варьироваться от одного примера к другому, в зависимости от того, для какого оборудования дисплея она написана. Выше мы видим, что она используется с библиотекой отображения Adafruit_ILI9341, необходимой для определенных экранов, FeatherWings или коммутационных плат. Другие примеры ссылаются на Adafruit_HX8357, Adafruit_ST7735 или другие библиотеки цветных TFT или OLED-дисплеев… используйте правильный вариант для имеющегося у вас оборудования.
Большинство примеров могут работать либо с SD-карты, либо с небольшого флэш-накопителя, который есть на некоторых платах Adafruit «Express». Код для инициализации одного или другого немного отличается, и в примерах проверяется, является ли USE_SD_CARD #defined для выбора одного метода по сравнению с другим. Если вы точно знаете, что ваш собственный проект должен работать только на том или ином типе, вам действительно нужна только соответствующая инициализация.
Для использования SD-карт объявлены эти два глобальных параметра:
1 2 |
SdFat SD; // файловая система SD-карты Adafruit_ImageReader reader (SD) ; // Объект для чтения изображений, передать в SD filesys |
Для файловой системы флэш-памяти сделаны некоторые специальные объявления, которые помогают нам найти флэш-устройство на разных платах Express, а затем объявить три глобальных параметра:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Файловая система флэш-памяти SPI или QSPI (например, диск CIRCUITPY) #if defined(__SAMD51__) || defined(NRF52840_XXAA) Adafruit_FlashTransport_QSPI flashTransport(PIN_QSPI_SCK, PIN_QSPI_CS, PIN_QSPI_IO0, PIN_QSPI_IO1, PIN_QSPI_IO2, PIN_QSPI_IO3); #else #if (SPI_INTERFACES_COUNT == 1) Adafruit_FlashTransport_SPI flashTransport(SS, &SPI); #else Adafruit_FlashTransport_SPI flashTransport(SS1, &SPI1); #endif #endif Adafruit_SPIFlash flash(&flashTransport); FatFileSystem filesys; Adafruit_ImageReader reader(filesys); // Считыватель изображений, передать flash filesys |
Объект «reader» будет использоваться для доступа к функциям загрузки изображений позже.
Затем… мы объявляем объект отображения (называемый в большинстве примеров «tft») обычным способом… например, с 2,8-дюймовым сенсорным экраном TFT для Arduino это:
1 2 3 4 5 |
#define SD_CS 4 // контакт выбора SD-карты #define TFT_CS 10 // контакт выбора TFT #define TFT_DC 9 // дисплей TFT/командный контакт Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); |
Все это происходит в разделе глобальных переменных, даже до функции setup().
Теперь нам нужно немного поработать в setup(), и снова она отличается для SD-карт и файловых систем на флэш-памяти…
Для SD-карты это может выглядеть так:
1 2 3 4 |
if(!SD.begin(SD_CS, SD_SCK_MHZ(25))) { // ESP32 требует ограничения в 25 МГц Serial.println(F("SD begin() failed")); for(;;); // Фатальная ошибка, не продолжайте } |
В этом примере показана очень простая обработка ошибок… проверка состояния возврата SD.begin() и печать сообщения в Serial Monitor в случае возникновения проблемы.
Вместо этого для использования файловой системы флэш-памяти требуется два шага:
1 2 3 4 5 6 7 8 |
if(!flash.begin()) { Serial.println(F("flash begin() failed")); for(;;); } if(!filesys.begin(&flash)) { Serial.println(F("filesys begin() failed")); for(;;); } |
Весь остальной код теперь одинаков независимо от того, используется ли SD-карта или флэш-память. Эта установка «и/или» потребовала некоторых дополнительных шагов, но теперь все пойдет гладко…
После begin()вызова функций SD (или flash) и TFT можно вызвать reader.drawBMP() загрузку BMP-изображения с карты на экран:
1 2 |
ImageReturnCode stat; stat = reader.drawBMP("/purple.bmp", tft, 0, 0); |
Строка принимает четыре аргумента:
- Имя файла в формате «8.3» (вам не нужно указывать абсолютный путь (ведущий «/»), но есть некоторые проблемы с библиотекой SD на некоторых передовых платах, таких как ESP32, поэтому включите это на всякий случай).
- Экранный объект, в котором будет отображаться изображение (например, «tft»). Это странный синтаксис, упомянутый ранее… вместо tft.drawBMP(), используйте reader.drawBMP(tft).
- Координаты X и Y, где расположен верхний левый угол изображения (не обязательно должны быть в пределах экрана… библиотека будет обрезать изображение по мере его загрузки). 0, 0 отрисовывает изображение в верхнем левом углу… поэтому, если размеры изображения соответствуют размерам экрана, оно заполнит весь экран.
Эта функция возвращает значение типа ImageReturnCode, которое нужно либо игнорировать, либо использовать для предоставления некоторых диагностических функций. Возможные значения:
- IMAGE_SUCCESS — Изображение загружено успешно (или было полностью обрезано за пределы экрана, по-прежнему считается «успешным» в том смысле, что ошибки не было).
- IMAGE_ERR_FILE_NOT_FOUND — Не удалось открыть запрошенный файл (проверьте правописание, убедитесь, что файл действительно существует на карте, убедитесь, что он соответствует соглашению об именах файлов «8.3» (например, «имя файла.bmp»).
- IMAGE_ERR_FORMAT — Неподдерживаемый формат изображения. В настоящее время поддерживаются только несжатые 24-битные цветные BMP (скорее всего, со временем будет добавлено больше).
- IMAGE_ERR_MALLOC — Не удалось выделить память для операции (drawBMP() не выдаст эту ошибку, но другие функции ImageReader могут).
Вместо того, чтобы работать с этими значениями самостоятельно, вы можете дополнительно вызвать функцию для отображения основного диагностического сообщения на консоли:
1 |
reader.printStatus(stat); |
Если вам нужно узнать размер изображения BMP, не загружая его, есть функция bmpDimensions():
1 2 |
int32_t width, height; stat = reader.bmpDimensions("/parrot.bmp", &width, &height); |
Она принимает три аргумента:
- Имя файла, те же правила, что и drawBMP()функция.
- Указатели на два 32-битных целых числа. При успешном завершении их содержимое будет равно ширине и высоте изображения в пикселях. При любой ошибке эти значения следует игнорировать (они остаются неинициализированными).
- Эта функция возвращает, ImageReturnCod eкак описано drawBMP() выше для функции.
Загрузка и использование изображений из оперативной памяти
В зависимости от размера изображения и других факторов, загрузка изображения с SD-карты на экран может занять несколько секунд. Небольшие изображения… те, которые могут полностью поместиться в ОЗУ… можно загрузить один раз и использовать повторно. Это может быть удобно для часто используемых значков или спрайтов, поскольку обычно это намного проще, чем преобразование и встраивание изображения в виде массива непосредственно в код… ужасный процесс.
Это вводит еще одну функцию ImageReader плюс новый тип объекта Adafruit_Image:
1 2 |
Adafruit_Image img; stat = reader.loadBMP("/wales.bmp", img); |
loadBMP() принимает два аргумента:
- Имя файла, те же правила, что и для предыдущих функций.
- Объект Adafruit_Image. Это немного более гибкий тип, чем растровые изображения, используемые несколькими функциями рисования в библиотеке GFX.
Она возвращает, ImageReturnCode как описано ранее. Если изображение слишком велико и не помещается в доступной оперативной памяти, IMAGE_ERR_MALLOC будет возвращено значение. Для цветных изображений требуется два байта на пиксель… например, для изображения размером 100×25 пикселей потребуется 100*25*2 = 5000 байт ОЗУ.
В случае успеха img объект будет содержать изображение в оперативной памяти.
Функция loadBMP() полезна только на микроконтроллерах со значительным объемом оперативной памяти, таких как платы Adafruit «M0» и «M4» или ESP32. Маленькие устройства, такие как Arduino Uno, просто не могут его сократить. Это может быть возможно на Arduino Mega с очень маленькими изображениями.
После загрузки используйте img.draw() функцию для вывода изображения на экран:
1 |
img.draw(tft, x, y); |
Функция принимает три аргумента:
- Экранный объект (например, «tft» в большинстве примеров), похожий на то, как drawBMP() работает.
- Координаты X и Y для верхнего левого угла изображения на экране, опять же похожие на drawBMP().
Мы используем img.draw(tft,…) вместо tft.drawRGBBitmap(…) (или другие функции рисования растровых изображений в библиотеке Adafruit_GFX), потому что в будущем мы планируем добавить больше гибкости в отношении форматов и типов файлов изображений. Объект Adafruit_Image «понимает» немного о загруженном изображении и автоматически вызывает соответствующую функцию рендеринга растрового изображения, и вам не придется обрабатывать каждый отдельный случай самостоятельно.
Если изображение не удалось загрузить по какой-либо причине, функцию img.draw() все равно можно вызвать, оно просто ничего не сделает. Но по крайней мере скетч не рухнет.
С Уважением, МониторБанк