Использование библиотеки U8glib с OLED-экраном и SSD1306

В этой статье речь пойдет об использовании библиотеки U8glib. Это универсальная библиотека под Arduino для работы с различными типами экранов.

Использование библиотеки

Я рассмотрю ее использование с OLED экраном, управляемым контроллером SSD1306. Чаще всего встречаются экраны с разрешением 128х64 пикселей:

И 128х32 пикселей:

Подключаться они могут как по I2C, так и по SPI (особых различий в использовании не будет).

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

Новая версия U8g2 доступна github.com автора.

Модифицированная U8glib

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

Также можно скачать исходники шрифтов в архиве с небольшой инструкцией по использованию:

Скачать модифицированную библиотеку U8glib можно ниже:

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

Стандартная библиотека U8glib устанавливается следующим образом.

1. Откройте Arduino IDE

2. Перейдите в: Скетч –> Подключить библиотеку –> менеджер библиотек

3. Введите в строке поиска u8glib и выберете "U8glib by oliver":

Проверка работоспособности экрана

При первом подключении экрана к Arduino стоит убедиться в том, что он корректно работает:

  • Правильно подключены пины управления
  • Сам экран и его драйвер не повреждены
  • Объект экрана правильно инициализирован в программе

Как это сделать?

1. В Arduino IDE открыть: Файл -> Примеры

2. Пролистать вниз до U8glib и выбрать пункт GraphicsTest:

3. Посмотреть тип своего дисплея.

Раскомментировать инициализацию, с названием, похожим на наименование подключаемого дисплея. Если не сработало – попробовать другую похожую инициализацию.

Если закончились все похожие инициализации – проверить правильность подключения дисплея и повторить предпредыдущий пункт.

Если и тут не сработало - перепроверить тип и наименование дисплея, повторить предпредпредыдущий пункт. Если даже так не сработает – убедиться, что плата Arduino жива и звать шамана.

4. Загрузить код в Arduino и смотреть на демонстрацию возможностей библиотеки.

Работаем с библиотекой

Библиотека U8glib позволяет в различных комбинациях:

  1. Рисовать на экране планиметрические объекты.
  2. Выводить заготовленную картинку.
  3. Писать различными шрифтами (выбор очень велик)

Теперь рассмотрим по порядку функционал.

Инициализация

Для начала необходимо подключить саму библиотеку

#include "U8glib.h"

Потом нужно инициализировать дисплей, который будет использоваться.

Для этого посмотрите модель и разрешение своего дисплея и раскомментируйте строку, подходящую по названию

В моем случае это:

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0); // I2C / TWI

Дисплей с разрешением 128 на 64 пикселя, подключается по интерфейсу I2C:

VCC <–> 5V

GND <-> GND

SCL <–> A5

SDA <–> A4

Данная библиотека в отличии от других не требует объявлений в: void setup(void).

Служебные функции могут быть вызваны в любом месте программы, а вот функции отрисовки должны всегда вызываться в цикле отрисовки:

u8g.firstPage();  
  do {
    // функции отрисовки
  } while( u8g.nextPage() );

Иногда бывает удобнее занести их в какую-то одну функцию и вызывать потом только ее:

void draw(void) { 
  u8g.setFont(u8g_font_unifont); 
  u8g.setPrintPos(0, 20); 
  u8g.print("Hello World!");
}

void setup(void) {
}

void loop(void) {
  u8g.firstPage();  
  do {
    draw();
    // функции отрисовки
  } while( u8g.nextPage() );
  delay(500);
}

Система координат

Дисплей работает в декартовой системе координат. Начало координат (0,0) находится в левом верхнем углу дисплея. Ось Х параллельна более длинной грани дисплея и идет от левого края к правому, ось У параллельна более короткой грани дисплея и идет от верхней грани к нижней.

Основные аспекты рассмотрены, теперь можно переходить к самим функциям отрисовки.

Служебные функции

Поворот экрана

Может пригодиться, если в проекте экран перевернут или хочется дисплей, вытянутый вверх, а не вбок.

Синтаксис:

  • setRot90() – поворот на 90 градусов
  • setRot180() – поворот на 180 градусов
  • setRot270() – поворот на 270 градусов

Пример:

u8g.setFont(u8g_font_unifont);  
u8g.setPrintPos(0, 20); 
u8g.print("Hello World!");

u8g.setRot90();
u8g.setFont(u8g_font_unifont);  
u8g.setPrintPos(0, 20); 
u8g.print("Hello World!");

u8g.setRot180();
u8g.setFont(u8g_font_unifont); 
u8g.setPrintPos(0, 20);
u8g.print("Hello World!");

u8g.setRot270();
u8g.setFont(u8g_font_unifont); 
u8g.setPrintPos(0, 20);
u8g.print("Hello World!");

Функция undoRotation отменяет поворот экрана, возвращая его в исходное положение.

Синтаксис: undoRotation().

Пример:

u8g. undoRotation();

Изменение цвета

Функция setColorIndex задает цвет пикселя.

Синтаксис: setColorIndex(uint8_t color_index).

Где:

  • color_index – цвет пикселя,
  • 0 – пиксель не горит,
  • 1 – пиксель горит.

Пример:

u8g.setColorIndex(1);
u8g.drawBox(10, 10, 60, 20);
u8g.setColorIndex(0);
u8g.setFont(u8g_font_unifont);
u8g.setPrintPos(10, 30);
u8g.print("Hello World!");

Масштабирование

Функция setScale2x2 уменьшает разрешение дисплея в 2 раза. Если раньше 1 точка занимала один пиксель, то теперь она занимает 4.

Синтаксис: setScale2x2().

Пример:

u8g.setFont(u8g_font_unifont);
u8g.setPrintPos(0, 20);
u8g.print("Hello World!");
u8g.drawPixel(30, 30);

u8g.setFont(u8g_font_unifont);
u8g.setScale2x2();
u8g.setPrintPos(0, 20);
u8g.print("Hello World!");
u8g.drawPixel(30, 30);

Функция undoScale отменяет действие функции setScale2x2.

Синтаксис: undoScale().

Пример:

u8g.undoScale();

Основные функции будут описаны ниже. Полный список можно посмотреть на GitHub автора библиотеки.

Планиметрические фигуры

Отрисовка прямоугольников

Функция drawBox рисует заполненные прямоугольник.

Синтаксис: drawBox(u8g_uint_t x, u8g_uint_t y, u8g_uint_t w, u8g_uint_t h).

  • x и y – координаты левого верхнего угла
  • w – ширина (по х) прямоугольника
  • h – высота (по у) прямоугольника

Пример:

u8g.drawBox(10,12,20,30);

Функция drawRBox рисует заполненные прямоугольник со скругленными краями.

Синтаксис: drawRBox(u8g_uint_t x, u8g_uint_t y, u8g_uint_t w, u8g_uint_t h, u8g_uint_t r).

  • x и y – координаты левого верхнего угла
  • w – ширина (по х) прямоугольника
  • h – высота (по у) прямоугольника
  • r – радиус скругления

Есть необходимое требование - ширина и высота должны быть больше, чем 2*(r+1).

Пример:

u8g.drawRBox(10, 12, 20, 30, 5);

Функция drawFrame рисует полый прямоугольник (рамку).

Синтаксис: drawFrame(u8g_uint_t x, u8g_uint_t y, u8g_uint_t w, u8g_uint_t h).

  • x и y – координаты левого верхнего угла
  • w – ширина (по х) прямоугольника
  • h – высота (по у) прямоугольника

Пример:

u8g.drawFrame(10,12,30,20);

Функция drawRFrame рисует полый прямоугольник (рамку) со скругленными краями.

Синтаксис: drawRFrame(u8g_uint_t x, u8g_uint_t y, u8g_uint_t w, u8g_uint_t h, u8g_uint_t r).

  • x и y – координаты левого верхнего угла
  • w – ширина (по х) прямоугольника
  • h – высота (по у) прямоугольника
  • r – радиус скругления

Пример:

u8g.drawRFrame (10,12,30,20);

Отрисовка треугольников

Функция drawTriangle рисует треугольник.

Синтаксис: drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2).

  • x0, y0, x1, y1, x2, y2 – координаты вершин треугольника

Пример:

u8g.drawTriangle(14,9, 45,32, 9,42);

Отрисовка линий

Функция drawLine рисует линию.

Синтаксис: drawLine(u8g_uint_t x1, u8g_uint_t y1, u8g_uint_t x2, u8g_uint_t y2).

  • x1, y1, x2, y2 – координаты вершин линии

Пример:

u8g.drawLine(7, 10, 40, 55);

Функция drawHLine рисует горизонтальную линию.

Синтаксис: drawHLine(u8g_uint_t x, u8g_uint_t y, u8g_uint_t w).

  • x и y – координаты верхней точки линии
  • w – длина линии

Пример:

u8g.drawHLine(7, 10, 20);

Функция drawVLine рисует вертикальную линию.

Синтаксис: drawVLine(u8g_uint_t x, u8g_uint_t y, u8g_uint_t h).

  • x и y – координаты левой точки линии
  • w – длина линии

Пример:

u8g.drawVLine (7, 10, 20);

Отрисовка эллипсов

Функция drawEllipse рисует полый эллипс.

Синтаксис: drawEllipse(u8g_uint_t x0, u8g_uint_t y0, u8g_uint_t rx, u8g_uint_t ry, uint8_t opt).

  • x0 и y0 – координаты центра эллипса
  • rx – горизонтальный радиус
  • ry – вертикальный радиус
  • opt – область, которая будет отрисована. Можно сразу выбрать несколько областей, разделив их знаком “|”
  • U8G_DRAW_UPPER_RIGHT – верхняя правая часть
  • U8G_DRAW_UPPER_LEFT – верхняя левая часть
  • U8G_DRAW_LOWER_LEFT – нижняя левая часть
  • U8G_DRAW_LOWER_RIGHT – нижняя правая часть
  • U8G_DRAW_ALL – весь эллипс

Пример:

u8g.drawEllipse(50, 30, 10, 20, U8G_DRAW_ALL);

u8g.drawEllipse(50, 30, 10, 20, U8G_DRAW_LOWER_LEFT | U8G_DRAW_UPPER_RIGHT);

Функция drawFilledEllipse рисует заполненный эллипс.

Синтаксис: drawFilledEllipse(u8g_uint_t x0, u8g_uint_t y0, u8g_uint_t rx, u8g_uint_t ry, uint8_t opt).

  • x0 и y0 – координаты центра эллипса
  • rx – горизонтальный радиус
  • ry – вертикальный радиус
  • opt – область, которая будет отрисована. Можно сразу выбрать несколько областей, разделив их знаком “|”
  • U8G_DRAW_UPPER_RIGHT – верхняя правая часть
  • U8G_DRAW_UPPER_LEFT – верхняя левая часть
  • U8G_DRAW_LOWER_LEFT – нижняя левая часть
  • U8G_DRAW_LOWER_RIGHT – нижняя правая часть
  • U8G_DRAW_ALL – весь эллипс

Пример:

u8g. drawFilledEllipse (50, 30, 10, 20, U8G_DRAW_ALL);

u8g. drawFilledEllipse (50, 30, 10, 20, U8G_DRAW_LOWER_LEFT | U8G_DRAW_UPPER_RIGHT);

Окружность

Функция drawCircle рисует окружность.

Синтаксис: drawCircle(u8g_uint_t x0, u8g_uint_t y0, u8g_uint_t rad, uint8_t opt).

  • x0 и y0 – координаты центра окружности
  • rad – радиус окружности (не включая центр окружности круга => диаметр равен 2*rad+1)
  • opt – область, которая будет отрисована. Можно сразу выбрать несколько областей, разделив их знаком “|”. Если не прописать параметр opt, будет нарисована вся окружность.
  • U8G_DRAW_UPPER_RIGHT – верхняя правая часть
  • U8G_DRAW_UPPER_LEFT – верхняя левая часть
  • U8G_DRAW_LOWER_LEFT – нижняя левая часть
  • U8G_DRAW_LOWER_RIGHT – нижняя правая часть
  • U8G_DRAW_ALL – вся окружность

Пример:

u8g.drawCircle(20, 20, 14);

u8g.drawCircle(20, 20, 14, U8G_DRAW_UPPER_RIGHT | U8G_DRAW_LOWER_LEFT);

Функция drawDisc рисует круг.

Синтаксис: drawDisc(u8g_uint_t x0, u8g_uint_t y0, u8g_uint_t rad, uint8_t opt).

  • x0 и y0 – координаты центра окружности
  • rad – радиус круга (не включая центр круга => диаметр равен 2*rad+1)
  • opt – область, которая будет отрисована. Можно сразу выбрать несколько областей, разделив их знаком “|”. Если не прописать параметр opt, будет нарисован весть круг.
  • U8G_DRAW_UPPER_RIGHT – верхняя правая часть
  • U8G_DRAW_UPPER_LEFT – верхняя левая часть
  • U8G_DRAW_LOWER_LEFT – нижняя левая часть
  • U8G_DRAW_LOWER_RIGHT – нижняя правая часть
  • U8G_DRAW_ALL – весь круг

Пример:

u8g.drawDisc(20, 20, 14);

u8g.drawDisc(20, 20, 14, U8G_DRAW_UPPER_RIGHT | U8G_DRAW_LOWER_LEFT);

Пиксель

Функция drawPixel рисует один пиксель.

Синтаксис: drawPixel(uint8_t x, uint8_t y).

  • x и y – координаты пикселя

Пример:

u8g.drawPixel(14,23);

Отрисовка изображений

Функци drawBitmap выводит растровое изображение.

Синтаксис:

  • drawBitmap(u8g_uint_t x, u8g_uint_t y, u8g_uint_t cnt, u8g_uint_t h, const uint8_t *bitmap)
  • drawBitmapP(u8g_uint_t x, u8g_uint_t y, u8g_uint_t cnt, u8g_uint_t h, const u8g_pgm_uint8_t *bitmap)
  • x и y – координаты левого верхнего угла изображения
  • cnt – количество байтов в горизонтали (1 байт кодирует 8 пикселей). Ширина в пикселях равна cnt*8.
  • h – высота изображеня.
  • *bitmap – название массива, кодирующего изображение
  • P в названии означает, что массив, кодирующий изображение храниться в PROGMEM.

Пример:

const uint8_t rook_bitmap[] U8G_PROGMEM = { // массив, хранящий изображение. 0 – пиксель не горит, 1 – пиксель горит
  0x00,         // 00000000 – 2-ичная кодировка 16-ричного числа
  0x55,         // 01010101
  0x7f,          // 01111111
  0x3e,         // 00111110
  0x3e,         // 00111110
  0x3e,         // 00111110
  0x3e,         // 00111110
  0x7f           // 01111111
};
void setup(void) {
}
void loop(void) {
  u8g.firstPage(); 
  do {
    u8g.drawBitmapP( 0, 0, 1, 8, rook_bitmap);
  } while( u8g.nextPage() );
  delay(1000);
}

Функция drawXBM рисует монохромное растровое изображение.

Синтаксис:

  • drawXBM(u8g_uint_t x, u8g_uint_t y, u8g_uint_t w, u8g_uint_t h, const uint8_t *bitmap)
  • drawXBMP(u8g_uint_t x, u8g_uint_t y, u8g_uint_t w, u8g_uint_t h, const u8g_pgm_uint8_t *bitmap)
  • x и y – координаты левого верхнего угла изображения
  • w – ширина изображения
  • h – высота изображения
  • *bitmap – название массива, кодирующего изображение
  • P в названии означает, что массив, кодирующий изображение храниться в PROGMEM.

Пример:

#define u8g_logo_width 38 // ширина картинки
#define u8g_logo_height 24 // высота картинки
//static unsigned char u8g_logo_bits[] = { // используется drawXBM
static unsigned char u8g_logo_bits[] U8G_PROGMEM = { // используется drawXBMP
   0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xe0, 0xe0,
   0xff, 0xff, 0x3f, 0xe3, 0xe1, 0xff, 0xff, 0x3f, 0xf3, 0xf1, 0xff, 0xff,
   0x3f, 0xf3, 0xf1, 0xfe, 0xbf, 0x37, 0xf3, 0x11, 0x1c, 0x1f, 0x30, 0xf3,
   0x01, 0x08, 0x8c, 0x20, 0xf3, 0x01, 0x00, 0xc0, 0x39, 0xf3, 0x81, 0xc7,
   0xc1, 0x39, 0xf3, 0xc1, 0xc7, 0xc9, 0x38, 0xf3, 0xc1, 0xc3, 0x19, 0x3c,
   0xe3, 0x89, 0x01, 0x98, 0x3f, 0xc7, 0x18, 0x00, 0x08, 0x3e, 0x0f, 0x3c,
   0x70, 0x1c, 0x30, 0x3f, 0xff, 0xfc, 0x87, 0x31, 0xff, 0xff, 0xbf, 0xc7,
   0x23, 0x01, 0x00, 0x00, 0xc6, 0x23, 0x03, 0x00, 0x00, 0x0e, 0x30, 0xff,
   0xff, 0x3f, 0x1f, 0x3c, 0xff, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0xff, 0x3f,
   0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x3f
};
void setup(void) {
}
void loop(void) {
  u8g.firstPage(); 
  do {
    u8g.drawXBMP( 0, 0, u8g_logo_width, u8g_logo_height, u8g_logo_bits);
  } while( u8g.nextPage() );
  delay(500);
}

Отрисовка строк

Функция setFont выбирает шрифт для последующий функций отрисовки строк.

Синтаксис: setFont(const u8g_fntpgm_uint8_t *font).

  • *font – указатель на устанавливаемый шрифт.

Пример:

u8g.setFont(u8g_font_unifont);

Шрифты в данной библиотеке хранятся как массивы в файле u8g_font_data.c.

Посмотреть шрифты, доступные в стандартной библиотеке можно на github. Шрифты, добавленные в модифицированную библиотеку:

  • my5x7rus
  •  cyr_6x10
  • my10x14rus
  • my6x13rus
  • cyrilic6x10
  • cyrilic6x9
  • rusMax20
  • ruscu12
  • rus10x20
  • rus9x18B
  • rus9x18
  • rus9x15B
  • rus9x15
  • rus8x13O
  • rus8x13B
  • rus8x13
  • rus7x14B
  • rus7x14
  • rus7x13O
  • rus7x13B
  • rus7x13
  • rus6x13O
  • rus6x13B
  • rus6x13
  • rus6x12
  • rus6x10
  • rus5x8
  • rus5x7
  • rus4x6

Как добавить свои шрифты я напишу в одной из следующих статей.

Функция drawStr рисует строку.

Синтаксис:

  • u8g_uint_t U8GLIB::drawStr(u8g_uint_t x, u8g_uint_t y, const char *s)
  • u8g_uint_t U8GLIB::drawStr90(u8g_uint_t x, u8g_uint_t y, const char *s)
  • u8g_uint_t U8GLIB::drawStr180(u8g_uint_t x, u8g_uint_t y, const char *s)
  • u8g_uint_t U8GLIB::drawStr270(u8g_uint_t x, u8g_uint_t y, const char *s)
  • u8g_uint_t U8GLIB::drawStrP(u8g_uint_t x, u8g_uint_t y, const u8g_pgm_uint8_t *s)
  • u8g_uint_t U8GLIB::drawStr90P(u8g_uint_t x, u8g_uint_t y, const u8g_pgm_uint8_t *s)
  • u8g_uint_t U8GLIB::drawStr180P(u8g_uint_t x, u8g_uint_t y, const u8g_pgm_uint8_t *s)
  • u8g_uint_t U8GLIB::drawStr270P(u8g_uint_t x, u8g_uint_t y, const u8g_pgm_uint8_t *s)
  • x и y – координаты левого нижнего угла первого символа строки
  • s – строка С-типа (заканчивается на “\0”)
  • P – строка храниться в PROGMEM
  • 90, 180, 270 – угол поворота строки

Перед использованием drawStr необходимо выбрать шрифт функцией setFont.

Пример:

u8g.setFont(u8g_font_unifont);
u8g.drawStr(10, 15, "hello world!");
u8g.setFont(u8g_font_unifont);
u8g.drawStr90(10, 15, "hello world!");

Функция setPrintPos устанавливает позицию для функции Print.

Синтаксис: setPrintPos(u8g_uint_t x, u8g_uint_t y).

  • x и y – координаты левого нижнего угола символа, который будет напечатан при вызове функции Print.

Пример:

U8g.setPrintPos(0, 20);

Функция print.

Синтаксис: print(...).

Подобно Serial.print() печатает цифровые значения, строки и отдельные символы. Требует вызова функции setPrintPos для установки позиции печати.

Пример:

u8g.setFont(u8g_font_unifont); 
u8g.setPrintPos(0, 20);
u8g.print("Hello World!");
int a = 100;
u8g.setPrintPos(0, 40);
u8g.print(a);
u8g.setPrintPos(20, 60);
u8g.print('@');

18 февраля 2020 в 20:19 | Обновлено 7 ноября 2020 в 01:19 (редакция)
Опубликовано:
Статьи,

6 комментариев

  1. Марат
    27 сентября 2020 в 18:22

    Приветствую. Как можно изменить пины подключения дисплея для Нано? Не на стандартные для I2C (А4, А5), а на другие из ряда D2-D13

    Ответить
    1. Vladilion90
      19 июля 2023 в 20:33

      Увы, их по-моему нельзя изменить, они находятся именно на этих пинах по устройству самой микросхемы, посмотрите распиновку, это точно так же как и ШИМ пины только определенные, на других будут проблемы и ошибки..

      Ответить
  2. Андрей
    25 октября 2020 в 04:38

    Хотел бы выразить огромную благодарность автору статьи за проделанную работу. Все объяснено крайне четко и понятно. Отдельное спасибо за добавление русских шрифтов в библиотеку, это действительно круто!

    Ответить
  3. Евгений
    2 марта 2021 в 09:38

    Превосходный материал. Давно не читал статей такого качества. Большое Вам спасибо, olikraus!

    Ответить
  4. Алексей
    15 января 2022 в 13:51

    Спасибо огромное за библиотеку и, что не менее важно, за подробное описание!

    Ответить
  5. Степан
    16 июля 2023 в 03:00

    Отличная статья для новичка!
    Спасибо!

    Ответить

Добавить комментарий

Ваш E-mail не будет никому виден. Обязательные поля отмечены *