Skip to content

Самая мощная библиотека для работы с дисплеями на 7-сегментных индикаторах

License

Notifications You must be signed in to change notification settings

GyverLibs/GyverSegment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

latest PIO Foo Foo Foo

Foo

GyverSegment

Самая мощная библиотека для работы с дисплеями на 7-сегментных индикаторах

  • Поддержка всех популярных китайских модулей (74HC595 4/8 цифр, TM1637 4/6 цифр + двоеточие, MAX7219 каскад любой длины)
  • Символьный процессор и встроенный шрифт: удобный вывод на дисплей любых данных
  • Единый API для всех дисплеев
  • Бегущая строка, можно выводить несколько на одном дисплее
  • 7 анимированных эффектов переключения символов
  • Удобный API для поддержки всех возможностей на любом другом дисплее
  • Настройка яркости, в том числе для динамических дисплеев
  • Быстрый bitbang на базе GyverIO - отправка данных на дисплей в 10 раз быстрее, чем в других библиотеках

Совместимость

Совместима со всеми Arduino платформами (используются Arduino-функции)

Зависимости

Содержание

Документация

Поддерживаемые дисплеи

Если вы нашли хороший модуль дисплея, который не поддерживается библиотекой - пишите. Закажу, протестирую, добавлю.

Фото Контроллер Размер AliExpress Класс
Disp1637Colon-0.36 TM1637 0.36" ссылка, ссылка, ссылка Disp1637Colon
Disp1637Colon-0.56 TM1637 0.56" ссылка, ссылка Disp1637Colon
Disp1637_4-0.36 TM1637 0.36" ссылка, ссылка, ссылка Disp1637_4
Disp1637_4-0.56 TM1637 0.56" ссылка, ссылка Disp1637_4
Disp1637_6-0.36 TM1637 0.36" ссылка, ссылка, ссылка Disp1637_6
Disp1637_6-0.56 TM1637 0.56" ссылка Disp1637_6
Disp595_4 74HC595 0.36" ссылка, ссылка, ссылка Disp595_4*
Disp595_8 74HC595 0.36" ссылка, ссылка Disp595_8*
Disp595_8v2 74HC595 0.36"
0.56"
ссылка Disp595_8v2*
Disp595_8s 74HC595 0.56" ссылка, ссылка, ссылка Disp595Static
Disp7219 MAX7219 0.36" ссылка, ссылка Disp7219
DispBare - - DispBare*

* - дисплеи с динамической индикацией

Как устроена библиотека

  • Все дисплеи работают в режиме программного буфера - после внесения изменений в буфер нужно вызвать update(). Бегущая строка и эффекты переключения сами вызывают update(), когда им это нужно
  • Все дисплеи наследуют класс SegBuffer, который отвечает за вывод данных, поэтому вывод одинаковый для всех дисплеев
  • Инструменты SegRunner (бегущая строка) и SegAnimation (эффекты переключения) также работают одинаково для всех дисплеев
  • У некоторых дисплеев есть дополнительные методы

Общее

void power(bool state);     // управление питанием (true вкл, false выкл)
void update();              // обновить дисплей
uint8_t* buffer;            // доступ к буферу

TM1637

Disp1637Colon(uint8_t DIO, uint8_t CLK, bool dots);  // Модуль 4 цифры + двоеточие
Disp1637_4(uint8_t DIO, uint8_t CLK);     // Модуль 4 цифры + точки
Disp1637_6(uint8_t DIO, uint8_t CLK);     // Модуль 6 цифр + точки

// у всех
void brightness(uint8_t bright);          // яркость, 0.. 7

// у Disp1637Colon
void colon(bool show);                    // вкл-выкл двоеточие

// дефайны настроек (объявлять перед подключением библиотеки)
#define DISP1637_CLK_DELAY 100   // задержка интерфейса в мкс

74HC595

Disp595_4(uint8_t DIO, uint8_t SCLK, uint8_t RCLK);     // Модуль 4 цифры
Disp595_8(uint8_t DIO, uint8_t SCLK, uint8_t RCLK);     // Модуль 8 цифр
Disp595_8v2(uint8_t DIO, uint8_t SCLK, uint8_t RCLK);   // Модуль 8 цифр, другая разводка
Disp595Static<int amount> (uint8_t SDI, uint8_t SCLK, uint8_t LOAD); // любое кол-во цифр. Статическая индикация!

// у всех
uint8_t tick();       // тикер динамической индикации, вызывать в loop
uint8_t tickManual(); // тикер динамической индикации, вызывать по своему таймеру
void brightness(uint8_t bright);    // установить яркость (0.. 15)

// дефайны настроек (объявлять перед подключением библиотеки)
#define DISP595_CLK_DELAY 0   // задержка интерфейса в мкс
#define GS_EXP_TIME 100       // время экспонирования знакоместа

Disp595Static работает без вызова tick. Но если нужно управление яркостью - нужно вызывать tick!

MAX7219

// Модуль 8 цифр
Disp7219<int amount>(uint8_t DIN, uint8_t CLK, uint8_t CS, bool reverse = true);

void begin();                       // инициализировать (нужно после сброса питания модуля)
void brightness(uint8_t value);     // установить яркость (0.. 15)

uint8_t getAmount();                // получить количество чипов
void brightness(uint8_t* value);    // установить яркость (0.. 15) для нескольких чипов
void power(uint8_t* state);         // управление питанием (true вкл, false выкл) для нескольких чипов

// дефайны настроек (объявлять перед подключением библиотеки)
#define DISP7219_CLK_DELAY 0   // задержка интерфейса в мкс
  • amount - количество микросхем MAX7219, каждая микросхема поддерживает 8 индикаторов, микросхемы можно соединять каскадом для увеличения длины бегущей строки
  • reverse разворачивает выводимые данные справа налево для китайских модулей (умолч. вкл). Если собирать свой дисплей по даташиту - при объявлении нужно поставить false

Голый дисплей

DispBare<uint8_t digits, bool decimal = 1, bool anode = 0>(uint8_t* dig, uint8_t* seg);

uint8_t tick();       // тикер динамической индикации, вызывать в loop
uint8_t tickManual(); // тикер динамической индикации, вызывать по своему таймеру
void brightness(uint8_t bright);    // установить яркость (0.. 15)

// дефайны настроек (объявлять перед подключением библиотеки)
#define GS_EXP_TIME 100       // время экспонирования знакоместа
  • digits - количество индикаторов
  • decimal - индикатор с десятичными точками DP, которые подключены к пину
  • anode - true - общий анод, false - общий катод
  • dig - массив пинов индикаторов (совпадает по количеству с digits)
  • seg - массив пинов сегментов (7 пинов без точки decimal, 8 с точкой)

Схема и онлайн симуляция здесь

Скорость интерфейса

Почти у всех поддерживаемых дисплеев можно настроить скорость интерфейса через дефайн DISPxxx_CLK_DELAY - это задержка в микросекундах при передаче данных, стандартные значения указаны выше. Если дисплей не работает - попробуйте увеличить задержку на 50-100 мкс! Например некоторые китайские модули TM1637 стабильно работают при нулевой задержке, а некоторым нужно 100 мкс (стоит по умолчанию). Также при подключении дисплея длинными проводами и/или при наличии источников ЭМ помех придётся увеличить задержку, чтобы передача данных была более надёжной.

#define DISP7219_CLK_DELAY 70
#include <GyverSegment.h>

Динамические дисплеи

tick()

У динамических дисплеев (74HC595, DispBare) обязательно должен вызываться тикер tick(), т.к. он выводит поочерёдно все цифры по своему таймеру. Если в программе есть задержки, которые мешают вызывать тикер - на дисплее могут появиться артефакты (неравномерная яркость цифр, исчезание цифр и другие "глюки"). Если в программе есть глухие циклы - тикер нужно вызывать также внутри них:

void loop() {
  disp.tick();

  while (1) {
    disp.tick();
    //.... break;
  }
}

В простом коде "на дилеях" можно использовать не встроенный delay(), а задержку дисплея. Библиотека будет вызывать тикер внутри него:

disp.print("hello");
disp.update();
disp.delay(1000); // тикер сам вызывается здесь
disp.print("1234");
disp.update();

Как часто библиотека обновляет дисплей:

  • При максимальной яркости - смена цифры каждую 1 миллисекунду
  • При другой яркости - смена цифр каждые GS_EXP_TIME (умолч. 100) микросекунд + время ожидания, соответствующее яркости

tickManual()

Отрисовку дисплея можно делать самостоятельно по прерыванию таймера или другими способами, для этого есть тикер tickManual() - без встроенного таймера. Как часто его нужно вызывать:

  • При установленной максимальной яркости (15) достаточно вызывать тикер каждые 2 миллисекунды. Этого достаточно для того, чтобы мерцания не было видно (проверено на 8-разрядном дисплее, на 4-разрядном можно увеличить время).
  • Если нужно управление яркостью - тикер придётся вызывать чаще - каждые 100 микросекунд. На более длинных периодах появится мерцание на минимальной яркости.

SegBuffer - вывод на дисплей

Вывод на дисплей осуществляется так же, как у 99% других дисплеев и библиотек: нужно установить курсор в setCursor(x) и вывести нужные данные в print(данные):

  • Установить курсор можно в том числе "за дисплей" для создания своих анимаций и других сценариев
  • print() - стандартный интерфейс библиотеки Arduino, выводит любые данные (целые числа, с плавающей точкой, строки любого типа)
  • Курсор автоматически смещается на длину текста
  • Для обновления дисплея вызвать update()
  • В библиотеку встроен довольно читаемый шрифт со всеми латинскими буквами и символами (32.. 126 ASCII). Строчные буквы равносильны большим
  • Точки в тексте обрабатываются отдельно и выводятся в десятичный разделитель дисплея, если он есть
  • Есть режим printRight(true), в котором данные будут печататься справа налево от текущей позиции курсора. Курсор в этом случае не смещается во время печати, вместо этого весь дисплей смещается влево

font

  DispXXX disp;   // любой дисплей из библиотеки
  // ...
  disp.setCursor(0);
  disp.print("hello");
  disp.update();

  // выравнивание по правому краю
  // числа неизвестной длины
  int val = 123;
  disp.setCursorEnd(sseg::intLen(val) - 1);
  disp.print(val);

  // или так
  disp.printRight(true);  // печатать справа
  disp.setCursorEnd();    // курсор в конец
  // disp.fillChar('0');  // с заполнением нулями
  disp.print(val);
  disp.printRight(false); // отключить печать справа

  disp.writeByte(0b11001100); // выводить свои данные на сегменты
Описание класса
// конструктор. decimal - поддерживает ли дисплей десятичные точки
SegBuffer(uint8_t* buffer, uint8_t size, bool decimal);

// установить курсор для печати, 0 (начало) слева
void setCursor(int16_t pos);

// установить курсор в начало
void home();

// установить курсор от конца дисплея (справа налево)
void setCursorEnd(int16_t pos = 0);

// получить позицию курсора
int16_t getCursor();

// проверка уместится ли int число при текущем курсоре
bool checkInt(int32_t val);

// проверка уместится ли float число при текущем курсоре
bool checkFloat(double val, uint8_t dec = 2);

// установить символ в позицию
void setChar(uint8_t pos, char symb);

// установить код в позицию
void set(uint8_t pos, uint8_t data);

// заполнить символом
void fillChar(char symb);

// заполнить кодом
void fill(uint8_t data);

// очистить
void clear();

// направление печати: false - печать слева, true - задвигание справа
void printRight(bool right);

// с указанием длины смещения буфера
void printRight(bool right, uint8_t shiftSize);

// установить или выключить десятичную точку на позиции
void point(uint8_t pos, bool state = 1);

// отобразить часы (час без нуля, минута с нулём) начиная с указанной позиции курсора
void showClock(uint8_t hour, uint8_t minute, uint8_t from = 0);

// вывести символ в текущую позицию курсора
size_t write(uint8_t data);

// вывести байт в текущую позицию курсора
void writeByte(uint8_t data);
void writeByte(uint8_t* data, uint8_t len);

// получить размер дисплея
uint8_t getSize();

// аналог delay, но внутри него вызывается тикер динамической индикации
void delay(uint32_t prd);

// очистить, напечатать слева, обновить
void clearPrint(T v);
void clearPrint(T v, uint8_t d);

// очистить, напечатать справа, обновить
void clearPrintR(T v);
void clearPrintR(T v, uint8_t d);

SegRunner - бегущая строка

Асинхронный вывод на дисплей бегущей строки в установленных границах курсора

  • Так как вывод асинхронный, строка должна быть либо объявлена глобально, либо в области определения тикера на время работы бегущей строки.
  • Во время движения строку можно изменять, это будет работать корректно. Но если у строки меняется длина - нужно вызвать setText() для её пересчёта. Движение не начнётся заново!
  • После выхода за левый край дисплея строка движение автоматически запускается сначала. Отследить это можно по результату tick() (см. ниже)
DispXXX disp;           // любой дисплей из библиотеки
SegRunner run(&disp);   // передать адрес дисплея

const char cstr_p[] PROGMEM = "progmem cstring";
const char* cstr[] = "cstring pointer";
char cstr_a[] = "global char array";
String str = "global String";

void setup() {
  // так можно
  run.setText_P(cstr_p);
  run.setText(cstr);
  run.setText(cstr_a);
  run.setText(str);
  // string literal изначально являются глобальными
  run.setText("hello");

  // так нельзя
  String lstr = "local String";
  run.setText(lstr);
  char lcstr_a[] = "local char array";
  run.setText(lcstr_a);

  run.start();  // запустить
  // run.setWindow(1, 6);  // границы, в которых движется текст
}

void loop() {
  // движение происходит здесь по таймеру
  run.tick();
}

Работать локально можно так:

void foo() {
  SegRunner run(&disp);
  String str = "local string";
  run.setText(s);
  run.start();
  run.waitEnd();
  // это работает в том числе для динамических дисплеев
}
Описание класса
// конструктор
SegRunner(SegBuffer* buf);

// установить текст const char*
void setText(const char* str);

// установить текст String
void setText(String& str);

// установить текст из PROGMEM (глобальный)
void setText_P(PGM_P str);

// установить окно вывода (x0, x1)
void setWindow(uint8_t x0, uint8_t x1);

// установить скорость (символов в секунду)
void setSpeed(uint16_t symPerSec);

// установить скорость (период в мс)
void setPeriod(uint16_t ms);

// запустить бегущую строку с начала
void start();

// остановить бегущую строку
void stop();

// продолжить движение с момента остановки
void resume();

// true - строка движется
bool running();

// ждать окончания движения строки
void waitEnd();

// тикер. Вернёт 0 в холостом, 1 при новом шаге, 2 при завершении движения
// Можно передать false чтобы дисплей не обновлялся сам
uint8_t tick(bool update = true);

// сдвинуть строку на 1 символ. Можно передать false чтобы дисплей не обновлялся сам
uint8_t tickManual(bool update = true);

// статусы возврата tick:
GS_RUNNER_IDLE
GS_RUNNER_STEP
GS_RUNNER_END

SegAnimation - эффекты переключения

Асинхронные анимации смены данных на дисплее

  • Анимация создаёт свой буфер типа SegBuffer, в который нужно выводить новые данные вместо вывода на дисплей
  • Система сама увидит изменение данных и воспроизведёт установленный эффект для смены изображения на актуальное
  • Анимация воспроизводится только в заданных границах дисплея (начало, длина), остальной дисплей не затрагивает

Эффекты:

SegEffect::Blink
SegEffect::RollUp
SegEffect::RollDown
SegEffect::TwistFill
SegEffect::TwistClear
SegEffect::SlideUp
SegEffect::SlideDown
DispXXX disp;           // любой дисплей из библиотеки
// указать кол-во символов, дисплей и позицию курсора дисплея
SegAnimation<3> anim(&disp, 0);

void setup() {
  anim.setEffect(SegEffect::TwistClear);
  anim.start();

  // вывести новые данные и ждать окончания эффекта
  // ожидание корректно работает в т.ч. для динамических дисплеев
  anim.setCursor(0);
  anim.print(random(1000));
  anim.waitEnd();
  disp.delay(1000);

  anim.setCursor(0);
  anim.print(random(1000));
  anim.waitEnd();
  disp.delay(1000);
}

void loop() {
  anim.tick();
  disp.tick();

  static uint32_t tmr;
  if (millis() - tmr >= 1000) {
    // если где то изменить данные аниматора - 
    // он сам обновит дисплей
    anim.setCursor(0);
    anim.print(random(1000));
  }
}
Описание класса
// конструктор
SegAnimation(SegBuffer* disp, uint8_t from, bool dec = 1);

// установить эффект и время его выполнения в мс
void setEffect(SegEffect eff, uint16_t duration = 300);

// запустить
void start();

// остановить анимацию
void stop();

// true - эффект воспроизводится
bool running();

// принудительно обновить дисплей из буфера эффекта
void refresh();

// воспроизводить эффект на всех цифрах (умолч. откл)
void forceAll(bool force);

// ждать окончания воспроизведения эффекта
void waitEnd();

// тикер. Вернёт 0 в холостом, 1 при новом шаге, 2 при завершении анимации
uint8_t tick();

// ручной тикер. Вернёт 0 в холостом, 1 при новом шаге, 2 при завершении анимации
uint8_t tickManual();

// статусы возврата tick:
GS_ANIMATION_IDLE
GS_ANIMATION_STEP
GS_ANIMATION_END

Утилиты

// получить код для символа
uint8_t sseg::getCharCode(char symb);

// получить длину int числа
uint8_t sseg::intLen(int32_t val);
uint8_t sseg::intLen(uint32_t val);

// получить длину float числа при указанной точности
uint8_t sseg::floatLen(double val, uint8_t dec = 2);

Как добавить дисплей

Библиотека позволяет очень легко добавлять свои возможности (вывод, бегущая строка, эффекты) к любым другим 7-сегментным дисплеям, даже самодельным. Для этого достаточно создать класс от SegBuffer и реализовать в нём:

  • Буфер на нужное количество знакомест
  • Метод update(), который будет выводить этот буфер на дисплей
  • Метод tick() (для динамических дисплеев)
  • Буфер имеет следующий порядок битов: 0bPGFEDCBA

7seg

// количество знакомест и наличие точек
// имена констант даны для примера
#define MY_DISP_SIZE 4
#define MY_DISP_DECIMALS true

class MyDisp : public SegBuffer {
   public:
    MyDisp() : SegBuffer(buffer, MY_DISP_SIZE, MY_DISP_DECIMALS) {}

    // обновить дисплей
    void update() {
        // ваш код
        return 0;
    }

    // тикер динамической индикации, вызывать в loop постоянно или по таймеру
    uint8_t tick() {
        // ваш код
        return 0;
    }

    uint8_t buffer[MY_DISP_SIZE] = {0};

   private:
};

Теперь ваш дисплей поддерживает все возможности библиотеки!

Примеры

Демка

#include <Arduino.h>
#include <GyverSegment.h>

#define DIO_PIN 2
#define CLK_PIN 3
#define LAT_PIN 4

// объявление дисплеев. Выбери любой
Disp595_4 disp(DIO_PIN, CLK_PIN, LAT_PIN);
// Disp595_8 disp(DIO_PIN, CLK_PIN, LAT_PIN);
// Disp595_8v2 disp(DIO_PIN, CLK_PIN, LAT_PIN);
// Disp595Static<4> disp(DIO_PIN, CLK_PIN, LAT_PIN);
// Disp1637_4 disp(DIO_PIN, CLK_PIN);
// Disp1637_6 disp(DIO_PIN, CLK_PIN);
// Disp1637Colon disp(DIO_PIN, CLK_PIN);
// Disp7219<1> disp(DIO_PIN, CLK_PIN, LAT_PIN);  // 1 чип - 8 цифр

uint8_t digs[] = {2, 3, 4, 5, A2, A3, A4, A5};  // пины цифр
uint8_t segs[] = {6, 7, 8, 9, 10, 11, 12, 13};  // пины сегментов
// 8 цифр, дсятичные точки есть, общий катод
// DispBare<8, true, false> disp(digs, segs);

void setup() {
  // используется disp.delay() для дисплеев с дин. индикацией

  // текст
  disp.setCursor(0);
  disp.print("hello");
  disp.update();
  disp.delay(1000);

  // целое число
  disp.setCursor(0);
  disp.print(1234);
  disp.update();
  disp.delay(1000);

  // float
  disp.setCursor(0);
  disp.print(3.14, 3);  // точность 3 знака
  disp.update();
  disp.delay(1000);

  // вывод числа справа с заполнением нулями
  disp.setCursorEnd();
  disp.printRight(true);
  disp.fillChar('0');
  disp.print(3.14, 3);
  disp.update();
  disp.delay(1000);

  disp.printRight(false);
}
void loop() {
  // нужен для дисплеев с динамической индикацией, но 
  // для совместимости есть у всех дисплеев, даже если ничего не делает
  disp.tick();
}

Счётчик с выравниванием справа

#include <GyverSegment.h>

Disp1637_4 disp(2, 3);

void setup() {
  disp.printRight(true);  // печатать справа
  disp.setCursorEnd();    // курсор в конец
}

int n = 0;

void loop() {
  disp.clear();
  disp.print(n);
  disp.update();
  n++;
  disp.delay(100);  // для дин. дисплеев
}

Версии

  • v1.0
  • v1.1 - добавлены forceAll() и длина для printRight()
  • v1.1.1 - исправлен баг
  • v1.1.2 - исправлен баг c printRight
  • v1.2
    • Переписан драйвер яркости для динамических дисплеев. Снижена нагрузка на процессор, повышена стабильность
    • Добавлен ручной тикер для динамических дисплеев
  • v1.3 - добавлена поддержка дисплея Disp595_8v2
  • v1.4 - добавлена поддержка дисплея Disp595Static
  • v1.4.2 - добавлено пакетное управление яркостью 7219 и режим вывода сырых данных writeByte

Установка

  • Библиотеку можно найти по названию GyverSegment и установить через менеджер библиотек в:
    • Arduino IDE
    • Arduino IDE v2
    • PlatformIO
  • Скачать библиотеку .zip архивом для ручной установки:
    • Распаковать и положить в C:\Program Files (x86)\Arduino\libraries (Windows x64)
    • Распаковать и положить в C:\Program Files\Arduino\libraries (Windows x32)
    • Распаковать и положить в Документы/Arduino/libraries/
    • (Arduino IDE) автоматическая установка из .zip: Скетч/Подключить библиотеку/Добавить .ZIP библиотеку… и указать скачанный архив
  • Читай более подробную инструкцию по установке библиотек здесь

Обновление

  • Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи
  • Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить"
  • Вручную: удалить папку со старой версией, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам!

Баги и обратная связь

При нахождении багов создавайте Issue, а лучше сразу пишите на почту alex@alexgyver.ru
Библиотека открыта для доработки и ваших Pull Request'ов!

При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать:

  • Версия библиотеки
  • Какой используется МК
  • Версия SDK (для ESP)
  • Версия Arduino IDE
  • Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
  • Какой код загружался, какая работа от него ожидалась и как он работает в реальности
  • В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код

About

Самая мощная библиотека для работы с дисплеями на 7-сегментных индикаторах

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •