Внимание! Сайт находится на стадии обновления, очень многое может временно не работать. Но пользоваться можно =)

Кто сказал, что символьные дисплеи для символов? Какие-то скучные чуваки. Если бы это было так, эти дисплеи не были бы настолько любимы и популярны в Arduino среде (купить можно тут https://alexgyver.ru/arduino_shop/). Подробного гайда лично от меня пока что нет, но суть такая: в этих дисплеях есть 8 ячеек для хранения “кастомных” символов, то есть символов, которые можно нарисовать самому, например при помощи различных онлайн сервисов типа такого http://maxpromer.github.io/LCD-Character-Creator/. Полученный массив байтов вставляется в скетч, передаётся в дисплей, и затем можно им пользоваться при помощи команды write(). Простой пример есть на офф сайте Ардуино https://www.arduino.cc/en/Reference/LiquidCrystalCreateChar. К слову эти ячейки в памяти занимают места с 0 по 7

Был у меня оч крутой проект, PCdisplay https://alexgyver.ru/pcdisplay/, в котором на дисплее 2004 отображалась в реальном времени информация о железе ПК: температуры и проценты загрузки, для красоты я выводил показатели не только числами, но и графически, в виде “полос загрузки” и графиков.

Проект получился очень классный, но вот я подумал, а почему бы не вынести построение графиков и полос загрузки удобными отдельными функциями? Ведь по-любому пригодятся кому-нибудь из вас, а достать код из PCdisplay новичку не под силу (к тому же он там чутка кривоват…). Так что представляю вашему вниманию GyverLCDbars – набор удобных инструментов для украшения ваших проектов графическими элементами: полосками загрузки и графиками. Актуальная версия всегда лежит у меня на GitHub https://github.com/AlexGyver/GyverLCDbars. ТЫКНИ для прямой загрузки архива.
В библиотеке вас ждёт 6 примеров: 4 типа полос загрузки и два примера с графиком.

Как этим пользоваться? Да очень просто. Одна функция для инициализации “кастомных” символов, вторая – для вывода нужного элемента с настройками его размера и позиции на дисплее!

Полоса загрузки: fillBar(столбец, строка, ширина, значение)

  • Столбец: отвечает за положение левой точки полосы, нумерация идёт слева направо с нуля
  • Строка: отвечает за положение левой точки полосы, нумерация идёт сверху вниз с нуля
  • Ширина: полная ширина полосы по горизонтали. Очевидно, что ширина + стартовая позиция по горизонтали (столбец) не должны превышать ширину дисплея в символах по горизонтали
  • Значение: число от 0 до 100 – процент заполнения полосы. Любая ваша величина приводится к диапазону 0-100 при помощи ардуиновской функции map
  • Особенность: если вы используете свои кастомные символы, то перед выводом полосок нужно обязательно вызвать initBar() для загрузки в память дисплея символов полосы! Полоски занимают разное количество мест в зависимости от типа, подробнее смотрите в самих примерах

График из массива: drawPlotArray(столбец, строка, ширина, высота, мин. значение, макс. значение, массив) – смотри пример!

График в реальном времени: drawPlot(столбец, строка, ширина, высота, мин. значение, макс. значение, величина)

  • Столбец: нумерация идёт слева направо с нуля. Начало координат графика – нижняя левая точка!
  • Строка: нумерация идёт сверху вниз с нуля. Начало координат графика – нижняя левая точка!
  • Ширина: ширина графика по горизонтали. Очевидно, что ширина + стартовая позиция по горизонтали (столбец) не должны превышать ширину дисплея в символах по горизонтали
  • Высота: высота графика по вертикали. Очевидно, что высота+ стартовая позиция по вертикали (строка) не должны превышать высоту дисплея в символах по вертикали. То  есть для 2004 максимум высота 4, для 1602 максимум 2.
  • Мин. значение: минимальное значение для графика, ниже него строиться не будет (тип данных int -32,768 to 32,767)
  • Макс. значение: максимальное значение для графика, выше него строиться не будет (тип данных int -32,768 to 32,767)
  • Величина: значение, которое будет построено на графике с краю, предыдущие столбики автоматически сдвинутся в сторону при вызове функции drawPlot. Тип данных int -32,768 to 32,767
  • Также в примере есть готовый кусок кода для расчёта и вывода максимального и минимального значения на текущем графике!
  • Особенность: если вы используете свои кастомные символы, то перед выводом графика нужно обязательно вызвать initPlot() для загрузки в память дисплея символов графика! Они занимают все места, с 0 по 7!

/*
  Скетч примера полосы загрузки на символьном LCD дисплее 1602/2004 итд.
*/

// библиотеки дисплея
#include 
#include 

// создаём дисплей
// дисплей 1602. Если не работает, используйте другой адрес
LiquidCrystal_I2C lcd(0x3f, 16, 2);
//LiquidCrystal_I2C lcd(0x27, 16, 2);

// дисплей 2004. Если не работает, используйте другой адрес!
//LiquidCrystal_I2C lcd(0x3f, 20, 4);
//LiquidCrystal_I2C lcd(0x27, 20, 4);

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.clear();
}

void loop() {
  // для примера потенциометр подключен к A0
  int perc = map(analogRead(0), 0, 1023, 0, 100);

  //fillBar0 принимает аргументы (столбец, строка, длина полосы, значение в % (0 - 100) )
  fillBar0(0, 0, 10, perc);
  fillBar0(0, 1, 16, perc);
  delay(50);
}

void fillBar0(byte start_pos, byte row, byte bar_length, byte fill_percent) {
  byte infill = round((float)bar_length * fill_percent / 100);
  lcd.setCursor(start_pos, row);
  if (infill == 0) lcd.write(16);
  else lcd.write(255);
  for (int n = 1; n < bar_length - 1; n++) {
    if (n < infill) lcd.write(255); if (n >= infill) lcd.write(16);
  }
  if (infill == bar_length) lcd.write(255);
  else lcd.write(16);
}
/*
  Скетч примера полосы загрузки на символьном LCD дисплее 1602/2004 итд.
*/

// библиотеки дисплея
#include 
#include 

// создаём дисплей
// дисплей 1602. Если не работает, используйте другой адрес
LiquidCrystal_I2C lcd(0x3f, 16, 2);
//LiquidCrystal_I2C lcd(0x27, 16, 2);

// дисплей 2004. Если не работает, используйте другой адрес!
//LiquidCrystal_I2C lcd(0x3f, 20, 4);
//LiquidCrystal_I2C lcd(0x27, 20, 4);

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.clear();
  
  // инициализация символов для отрисовки. При использовании каких то своих символов,
  // нужно вызывать эту функцию перед отрисовкой графика/полосы!
  initBar1();
}

void loop() {
  // для примера потенциометр подключен к A0
  int perc = map(analogRead(0), 0, 1023, 0, 100);

  //fillBar1 принимает аргументы (столбец, строка, длина полосы, значение в % (0 - 100) )
  fillBar1(0, 0, 10, perc);
  fillBar1(0, 1, 16, perc);
  delay(50);
}

void initBar1() {
  // необходимые символы для работы
  // создано в http://maxpromer.github.io/LCD-Character-Creator/
  byte right_empty[8] = {0b11111,  0b00001,  0b00001,  0b00001,  0b00001,  0b00001,  0b00001,  0b11111};
  byte left_empty[8] = {0b11111,  0b10000,  0b10000,  0b10000,  0b10000,  0b10000,  0b10000,  0b11111};
  byte center_empty[8] = {0b11111, 0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b11111};
  lcd.createChar(0, left_empty);
  lcd.createChar(1, center_empty);
  lcd.createChar(2, right_empty);
}

void fillBar1(byte start_pos, byte row, byte bar_length, byte fill_percent) {
  byte infill = round((float)bar_length * fill_percent / 100);
  lcd.setCursor(start_pos, row);
  if (infill == 0) lcd.write(0);
  else lcd.write(255);
  for (int n = 1; n < bar_length - 1; n++) {
    if (n < infill) lcd.write(255); if (n >= infill) lcd.write(1);
  }
  if (infill == bar_length) lcd.write(255);
  else lcd.write(2);
}
/*
  Скетч примера полосы загрузки на символьном LCD дисплее 1602/2004 итд.
*/

// библиотеки дисплея
#include 
#include 

// создаём дисплей
// дисплей 1602. Если не работает, используйте другой адрес
LiquidCrystal_I2C lcd(0x3f, 16, 2);
//LiquidCrystal_I2C lcd(0x27, 16, 2);

// дисплей 2004. Если не работает, используйте другой адрес!
//LiquidCrystal_I2C lcd(0x3f, 20, 4);
//LiquidCrystal_I2C lcd(0x27, 20, 4);

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.clear();
  
  // инициализация символов для отрисовки. При использовании каких то своих символов,
  // нужно вызывать эту функцию перед отрисовкой графика/полосы!
  initBar2();
}

void loop() {
  // для примера потенциометр подключен к A0
  int perc = map(analogRead(0), 0, 1023, 0, 100);

  //fillBar2 принимает аргументы (столбец, строка, длина полосы, значение в % (0 - 100) )
  fillBar2(0, 0, 10, perc);
  fillBar2(0, 1, 16, perc);
  delay(50);
}

void initBar2() {
  // необходимые символы для работы
  // создано в http://maxpromer.github.io/LCD-Character-Creator/
  byte right_empty[8] = {0b11111,  0b00001,  0b00001,  0b00001,  0b00001,  0b00001,  0b00001,  0b11111};
  byte left_empty[8] = {0b11111,  0b10000,  0b10000,  0b10000,  0b10000,  0b10000,  0b10000,  0b11111};
  byte center_empty[8] = {0b11111, 0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b11111};
  byte left_full[8] = {0b11111, 0b10000, 0b10111, 0b10111, 0b10111, 0b10111, 0b10000, 0b11111};
  byte right_full[8] = {0b11111, 0b00001, 0b11101, 0b11101, 0b11101, 0b11101, 0b00001, 0b11111};
  byte center_full[8] = {0b11111, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b00000, 0b11111};
  lcd.createChar(0, left_empty);
  lcd.createChar(1, center_empty);
  lcd.createChar(2, right_empty);
  lcd.createChar(3, left_full);
  lcd.createChar(4, center_full);
  lcd.createChar(5, right_full);
}

void fillBar2(byte start_pos, byte row, byte bar_length, byte fill_percent) {
  byte infill = round((float)bar_length * fill_percent / 100);
  lcd.setCursor(start_pos, row);
  if (infill == 0) lcd.write(0);
  else lcd.write(3);
  for (int n = 1; n < bar_length - 1; n++) {
    if (n < infill) lcd.write(4); if (n >= infill) lcd.write(1);
  }
  if (infill == bar_length) lcd.write(5);
  else lcd.write(2);
}
/* Скетч примера полосы загрузки на символьном LCD дисплее 1602/2004 итд. */ // библиотеки дисплея #include #include // создаём дисплей // дисплей 1602. Если не работает, используйте другой адрес LiquidCrystal_I2C lcd(0x3f, 16, 2); //LiquidCrystal_I2C lcd(0x27, 16, 2); // дисплей 2004. Если не работает, используйте другой адрес! //LiquidCrystal_I2C lcd(0x3f, 20, 4); //LiquidCrystal_I2C lcd(0x27, 20, 4); boolean change_flag = true; // флаг для 3 типа полосы void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); // инициализация символов для отрисовки. При использовании каких то своих символов, // нужно вызывать эту функцию перед отрисовкой графика/полосы! initBar3(); } void loop() { // для примера потенциометр подключен к A0 int perc = map(analogRead(0), 0, 1023, 0, 100); //fillBar3 принимает аргументы (столбец, строка, длина полосы, значение в % (0 - 100) ) fillBar3(0, 0, 10, perc); fillBar3(0, 1, 16, perc); delay(50); } void initBar3() { // необходимые символы для работы // создано в http://maxpromer.github.io/LCD-Character-Creator/ byte right_empty[8] = {0b11111, 0b00001, 0b00001, 0b00001, 0b00001, 0b00001, 0b00001, 0b11111}; byte left_empty[8] = {0b11111, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}; byte center_empty[8] = {0b11111, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}; byte bar2[] = {0b11111, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11111}; byte bar3[] = { B11111, B11100, B11100, B11100, B11100, B11100, B11100, B11111}; byte bar4[] = { B11111, B11110, B11110, B11110, B11110, B11110, B11110, B11111}; lcd.createChar(0, left_empty); lcd.createChar(1, center_empty); lcd.createChar(2, right_empty); lcd.createChar(3, bar2); lcd.createChar(4, bar3); lcd.createChar(5, bar4); } void fillBar3(byte start_pos, byte row, byte bar_length, byte fill_percent) { byte infill = bar_length * fill_percent / 10; byte fract = infill % 10; infill = infill / 10; // change_flag - true слева, false справа if (infill < bar_length - 1) { if (!change_flag) { change_flag = true; byte bar2[] = {0b11111, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11111}; byte bar3[] = { B11111, B11100, B11100, B11100, B11100, B11100, B11100, B11111}; byte bar4[] = { B11111, B11110, B11110, B11110, B11110, B11110, B11110, B11111}; lcd.createChar(3, bar2); lcd.createChar(4, bar3); lcd.createChar(5, bar4); } } else { if (change_flag) { change_flag = false; byte leftbar1[] = { B11111, B10001, B10001, B10001, B10001, B10001, B10001, B11111}; byte leftbar2[] = { B11111, B11001, B11001, B11001, B11001, B11001, B11001, B11111}; byte leftbar3[] = { B11111, B11101, B11101, B11101, B11101, B11101, B11101, B11111}; lcd.createChar(3, leftbar1); lcd.createChar(4, leftbar2); lcd.createChar(5, leftbar3); } } lcd.setCursor(start_pos, row); if (infill == 0) { if (fract >= 0 && fract < 2) lcd.write(0); else if (fract >= 2 && fract < 4) lcd.write(0); else if (fract >= 4 && fract < 6) lcd.write(3); else if (fract >= 6 && fract < 8) lcd.write(4); else if (fract >= 8) lcd.write(5); } else lcd.write(255); for (int n = 1; n < bar_length - 1; n++) { if (n < infill) lcd.write(255); if (n == infill) { if (fract >= 0 && fract < 2) lcd.write(1); else if (fract >= 2 && fract < 4) lcd.write(0); else if (fract >= 4 && fract < 6) lcd.write(3); else if (fract >= 6 && fract < 8) lcd.write(4); else if (fract >= 8) lcd.write(5); } if (n > infill) lcd.write(1); } if (infill == bar_length - 1) { if (fract >= 0 && fract < 2) lcd.write(2); else if (fract >= 2 && fract < 4) lcd.write(3); else if (fract >= 4 && fract < 6) lcd.write(4); else if (fract >= 6 && fract < 8) lcd.write(5); else if (fract >= 8) lcd.write(255); } else if (infill == bar_length) lcd.write(255); else lcd.write(2); } 
/* Скетч примера графика на символьном LCD дисплее 1602/2004 итд. */ // библиотеки дисплея #include #include // создаём дисплей // дисплей 1602. Если не работает, используйте другой адрес //LiquidCrystal_I2C lcd(0x3f, 16, 2); //LiquidCrystal_I2C lcd(0x27, 16, 2); // дисплей 2004. Если не работает, используйте другой адрес! //LiquidCrystal_I2C lcd(0x3f, 20, 4); LiquidCrystal_I2C lcd(0x27, 20, 4); int plot_array[20]; // массив данных для графика void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); // инициализация символов для отрисовки. При использовании каких то своих символов, // нужно вызывать эту функцию перед отрисовкой графика/полосы! initPlot(); } void loop() { // для примера потенциометр подключен к A0 int value = map(analogRead(0), 0, 1023, 10, 80); // drawPlot принимает аргументы (столбец, строка, ширина, высота, мин. значение, макс. значение, величина) // строка, столбец: начало графика - нижняя левая точка! // значения целочисленные (int) и могут быть отрицательными! // для примера отрисовка графика 12х4 занимает 140 миллисекунд // график сдвигается на 1 шаг АВТОМАТИЧЕСКИ при вызове функции! drawPlot(0, 3, 12, 4, 10, 80, value); // также можно вывести минимум и максимум по отображаемому участку графика /* int max_value = -32000; int min_value = 32000; for (byte i = 0; i < 12; i++) { if (plot_array[i] > max_value) max_value = plot_array[i]; if (plot_array[i] < min_value) min_value = plot_array[i]; } lcd.setCursor(17, 1); lcd.print(value); lcd.print(" "); lcd.setCursor(13, 0); lcd.print("max "); lcd.print(max_value); lcd.print(" "); lcd.setCursor(13, 3); lcd.print("min "); lcd.print(min_value); lcd.print(" "); */ delay(300); } void initPlot() { // необходимые символы для работы // создано в http://maxpromer.github.io/LCD-Character-Creator/ byte row8[8] = {0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row7[8] = {0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row6[8] = {0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row5[8] = {0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; byte row4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111}; byte row3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111}; byte row2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111}; byte row1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}; lcd.createChar(0, row8); lcd.createChar(1, row1); lcd.createChar(2, row2); lcd.createChar(3, row3); lcd.createChar(4, row4); lcd.createChar(5, row5); lcd.createChar(6, row6); lcd.createChar(7, row7); } void drawPlot(byte pos, byte row, byte width, byte height, int min_val, int max_val, int fill_val) { for (byte i = 0; i < width; i++) { plot_array[i] = plot_array[i + 1]; } fill_val = constrain(fill_val, min_val, max_val); plot_array[width] = fill_val; for (byte i = 0; i < width; i++) { // каждый столбец параметров byte infill, fract; // найти количество целых блоков с учётом минимума и максимума для отображения на графике infill = floor((float)(plot_array[i] - min_val) / (max_val - min_val) * height * 10); fract = (infill % 10) * 8 / 10; // найти количество оставшихся полосок infill = infill / 10; for (byte n = 0; n < height; n++) { // для всех строк графика if (n < infill && infill > 0) { // пока мы ниже уровня lcd.setCursor(i, (row - n)); // заполняем полными ячейками lcd.write(0); } if (n >= infill) { // если достигли уровня lcd.setCursor(i, (row - n)); if (fract > 0) lcd.write(fract); // заполняем дробные ячейки else lcd.write(16); // если дробные == 0, заливаем пустой for (byte k = n + 1; k < height; k++) { // всё что сверху заливаем пустыми lcd.setCursor(i, (row - k)); lcd.write(16); } break; } } } }

На этом сегодня всё, надеюсь вам пригодятся эти наработки! Как говорится, лайк-подписка, до новых встреч =)


2018-09-05T16:06:55+00:00
WordPress: 18.91MB | MySQL:207 | 0,516sec