Вывод информации на дисплей практически не отличается от вывода в консоль.
Функции hide(), show() и setBrightness(x) управляют состоянием дисплея.
ngTM1637.h
#pragma once
// ================================
// ---
#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
#if defined(__AVR_ARCH__)
#include <avr/pgmspace.h>
#else
#include <pgmspace.h>
#endif
#include <stdint.h>
#include <stdio.h>
template <const uint8_t Digits>
class TDriverTM1637 : public Print
{
private:
static constexpr uint8_t cmd_address_autoincrement = 0x40;
static constexpr uint8_t cmd_address_not_increment = 0x44;
static constexpr uint8_t cmd_set_display_mode_off = 0x80;
static constexpr uint8_t cmd_set_address(const uint8_t address) { return 0xC0u | (7 & address); }
static constexpr uint8_t cmd_brightness(const uint8_t level) { return 0x88u | (7 & level); }
const uint8_t _DIO;
const uint8_t _CLK;
protected:
static constexpr uint8_t width = Digits;
uint8_t brightness;
size_t byteWrite(const uint8_t value) const;
size_t rawWrite(const uint8_t addr, const uint8_t * const raw, const uint8_t count = Digits) const;
size_t rawWrite(const uint8_t * const raw, const uint8_t count = Digits) const;
inline void stop() const {
digitalWrite(_CLK, HIGH);
digitalWrite(_DIO, HIGH);
}
inline void restart() const {
//stop();
digitalWrite(_CLK, HIGH);
digitalWrite(_DIO, HIGH);
// Start
digitalWrite(_DIO, LOW);
digitalWrite(_CLK, LOW);
}
public:
TDriverTM1637() = delete;
//TDriverTM1637(TDriverTM1637&) = delete;
//TDriverTM1637(TDriverTM1637&&) = delete;
TDriverTM1637(const uint8_t clk_pin, const uint8_t dio_pin)
: Print(), _CLK(clk_pin), _DIO(dio_pin)
{};
/* начальная инициализация драйвера TM1637
* порты DIO и CLK будут переключены в режим выхода
* и включены все сегменты на минимальной яркости
*/
void init();
/* включить индикацию с указанием уровня яркости
* от 0 - минимальный до 7 - максимальный уровень
*/
inline void setBrightness(const uint8_t lvl = 0) { brightness = 7 & lvl; show(); }
/* вернуть установленный уровень яркости
*/
inline uint8_t getBrightness() const { return brightness; }
/* включить индикацию
* с ранее установленным уровнем яркости
*/
void show() const;
/* выключить/погасить/ индикацию
* информация в буфере TM1637 сохраняется
*/
void hide() const;
};
// -------------
// | \ a / | a = 0x01
// | \ ----- / | b = 0x02
// | f | | b | c = 0x04
// | /-----\ | d = 0x08
// |--< g >--| e = 0x10
// | \-----/ | f = 0x20
// | e | | c | g = 0x40
// | / ----- \ | h = 0x80
// | / d \ | ---
// ------------- | h |
// ---
#define _a_ 0x01
#define _b_ 0x02
#define _c_ 0x04
#define _d_ 0x08
#define _e_ 0x10
#define _f_ 0x20
#define _g_ 0x40
#define _h_ 0x80
template <class Inherited>
class T7SegmentDisplay : public Inherited
{
private:
uint8_t frame_buff[2 * Inherited::width];
uint8_t position;
public:
T7SegmentDisplay() = delete;
//T7SegmentDisplay(T7SegmentDisplay&) = delete;
//T7SegmentDisplay(T7SegmentDisplay&&) = delete;
T7SegmentDisplay(const uint8_t clk_pin, const uint8_t dio_pin);
protected:
uint8_t getCharCode(const char x) const;
virtual size_t write(uint8_t);
using Print::write; // pull in write(str) and write(buf, size) from Print
private:
struct T7SegmentCode {
const char code;
const uint8_t glif;
};
/* массив начертаний символов
* должен быть отсортирован по возрастанию кода символа
* для корректной работы алгоритма поиска
* методом половинного деления
*/
static constexpr T7SegmentCode segment_code[] PROGMEM = {
{'0', (_a_|_b_|_c_|_d_|_e_|_f_) },
{'1', (_b_|_c_) },
{'2', (_a_|_b_|_d_|_e_|_g_) },
{'3', (_a_|_b_|_c_|_d_|_g_) },
{'4', (_b_|_c_|_f_|_g_) },
{'5', (_a_|_c_|_d_|_f_|_g_) },
{'6', (_a_|_c_|_d_|_e_|_f_|_g_) },
{'7', (_a_|_b_|_c_) },
{'8', (_a_|_b_|_c_|_d_|_e_|_f_|_g_) },
{'9', (_a_|_b_|_c_|_d_|_f_|_g_) },
{'A', (_a_|_b_|_c_|_e_|_f_|_g_) },
{'B', (_c_|_d_|_e_|_f_|_g_) },
{'C', (_a_|_d_|_e_|_f_) },
{'D', (_b_|_c_|_d_|_e_|_g_) },
{'E', (_a_|_d_|_e_|_f_|_g_) },
{'F', (_a_|_e_|_f_|_g_) },
{'G', (_a_|_c_|_d_|_e_|_f_) },
{'H', (_b_|_c_|_e_|_f_|_g_) },
{'I', (_e_|_f_) },
{'J', (_b_|_c_|_d_|_e_) },
{'L', (_d_|_e_|_f_) },
{'N', (_c_|_e_|_g_) },
{'O', (_a_|_b_|_c_|_d_|_e_|_f_) },
{'P', (_a_|_b_|_e_|_f_|_g_) },
{'Q', (_a_|_b_|_c_|_f_|_g_) },
{'R', (_e_|_g_) },
{'S', (_a_|_c_|_d_|_f_|_g_) },
{'T', (_d_|_e_|_f_|_g_) },
{'U', (_b_|_c_|_d_|_e_|_f_) },
{'Y', (_b_|_c_|_d_|_f_|_g_) },
{'[', (_a_|_d_|_e_|_f_) },
{']', (_a_|_b_|_c_|_d_) },
};
static constexpr uint8_t segment_code_count = sizeof(segment_code) / sizeof(T7SegmentCode);
};
/* -------------------------------------------------------------
* объявление типа для четырехзначного дисплея
*/
using TM1637 = T7SegmentDisplay<TDriverTM1637<4>>;
/* -------------------------------------------------------------
* объявление массива перекодирования символов
*/
template <class Inherited>
const struct T7SegmentDisplay<Inherited>::T7SegmentCode T7SegmentDisplay<Inherited>::segment_code[];
/* -------------------------------------------------------------
* реализация шаблона класса TDriverTM1637
*/
template <const uint8_t Digits>
void TDriverTM1637<Digits>::init()
{
stop();
pinMode(_CLK, OUTPUT);
pinMode(_DIO, OUTPUT);
brightness = 0;
restart();
if (byteWrite(cmd_brightness(brightness)))
{
restart();
byteWrite(cmd_set_address(0));
restart();
byteWrite(cmd_address_autoincrement);
for (int ii = Digits; ii; --ii)
byteWrite(0xFF);
}
stop();
}
template <const uint8_t Digits>
size_t TDriverTM1637<Digits>::byteWrite(const uint8_t value) const
{
int8_t x = static_cast<int8_t>(value);
// передать 8 бит младшим битом вперед
for (uint8_t ii = 8; ii; --ii, x >>= 1) {
digitalWrite(_CLK, LOW);
digitalWrite(_DIO, 1 & x);
digitalWrite(_CLK, HIGH);
}
// Для подтверждения приема TM1637 формирует ACK
// (низкий уровень на линии данных) с минимальной
// (100..150 наносекунд) задержкой относительно
// заднего фронта 8го строба и чтобы избежать
// сквозного тока по линии данных
// нужно проверять ее состояние
// если 1 - сразу перевести ее в режим входа
// а затем опусть строб
// если 0 - сначала опусть строб и только после
// перевести линию данных в режим входа
if (x) pinMode(_DIO, INPUT);
digitalWrite(_CLK, LOW);
pinMode(_DIO, INPUT_PULLUP);
// INPUT_PULLUP здесь нужен, чтобы обнаружить
// отсутсвие ACK когда дисплей не подключен
// 9й строб - обработка ACK
// TM1637 будет держать ACK до получения заднего фронта
// поднимаем строб, это даст небольшую задержку перед
// проверкой наличия ACK
// задержка нужна потому что, когда дисплей не подключен
// фронт на линии данных может быть завален
digitalWrite(_CLK, HIGH);
// проверить наличие ACK
x = digitalRead(_DIO);
digitalWrite(_DIO, x);
pinMode(_DIO, OUTPUT);
// завершаем передачу байта
digitalWrite(_CLK, LOW);
digitalWrite(_DIO, LOW);
// 1 - if ACK;
// 0 - not ACK;
return !(x);
}
template <const uint8_t Digits>
void TDriverTM1637<Digits>::hide() const
{
restart();
byteWrite(cmd_set_display_mode_off);
stop();
}
template <const uint8_t Digits>
void TDriverTM1637<Digits>::show() const
{
restart();
byteWrite(cmd_brightness(brightness));
stop();
}
template <const uint8_t Digits>
size_t TDriverTM1637<Digits>::rawWrite(const uint8_t addr, const uint8_t * const raw, const uint8_t length) const
{
int8_t count = 0;
restart();
if (byteWrite(cmd_set_address(addr)))
{
restart();
byteWrite(cmd_address_autoincrement);
count = (Digits < length) ? Digits : length;
for (uint8_t ii = 0; count > ii; ++ii)
byteWrite(raw[ii]);
}
stop();
return count;
}
template <const uint8_t Digits>
size_t TDriverTM1637<Digits>::rawWrite(const uint8_t * const raw, const uint8_t count) const
{
return rawWrite(0, raw, count);
}
/* -------------------------------------------------------------
* реализация шаблона класса T7SegmentDisplay
*/
template <class Inherited>
T7SegmentDisplay<Inherited>::T7SegmentDisplay(const uint8_t clk_pin, const uint8_t dio_pin)
: Inherited(clk_pin, dio_pin), position(Inherited::width - 1)
{
memset(frame_buff, 0, Inherited::width);
}
template <class Inherited>
size_t T7SegmentDisplay<Inherited>::write(uint8_t x)
{
switch (x) {
case '\n': // FL - новая строка
position = Inherited::width - 1;
break;
case '\r': // CR - возврат каретки
Inherited::rawWrite(&frame_buff[position - (Inherited::width - 1)], Inherited::width);
break;
case '.': // включить точку в текущей позиции
case ',':
if (Inherited::width < position)
frame_buff[position] |= _h_;
break;
default: // заполнение буфера
if (((2 * Inherited::width) - 1) > position)
frame_buff[++position] = getCharCode(x);
break;
}
return 1;
}
template <class Inherited>
uint8_t T7SegmentDisplay<Inherited>::getCharCode(const char x) const
{
uint8_t glif = 0;
switch (x) {
case ' ': glif = 0; break;
case '*': glif = (_a_|_b_|_f_|_g_); break;
case '-': glif = (_g_); break;
case '_': glif = (_d_); break;
case '~': glif = (_a_); break;
default:
const char upcase = x - ((('a' <= x) && (x <= 'z')) ? 0x20 : 0);
uint8_t imax = segment_code_count - 1;
uint8_t imin = 0;
for (uint8_t step = segment_code_count; step; step /= 2) {
const uint8_t index = imin + (imax - imin) / 2;
const char key = pgm_read_byte(&(segment_code[index].code));
if (key == upcase) {
glif = pgm_read_byte(&(segment_code[index].glif));
break;
} else {
if (key > upcase) imax = index - 1;
else imin = index + 1;
}
};
break;
}
return glif;
}
и пример использования
ngTM1637.ino
#include "ngTM1637.h"
#define DISPLAY_DIO 10
#define DISPLAY_CLK 11
TM1637 display(DISPLAY_CLK, DISPLAY_DIO);
uint16_t count;
void setup()
{
display.init();
count = 0;
}
void loop()
{
int ii;
delay(1000);
// вывод строки
char timedot[8];
char timestr[8];
// время с мигающей точкой/двоеточием посередине
sprintf_P(timedot, PSTR("%02i.%02i"), count / 60, count % 60);
sprintf_P(timestr, PSTR("%02i%02i"), count / 60, count % 60);
for (ii = 7; ii; --ii) {
display.println(timestr);
delay(600);
display.println(timedot);
delay(400);
}
// погасить и снова включить экран
// информация на экране сохраняется
display.hide();
delay(600);
display.show();
// меняем яркость экрана
// библиотека обрежет устанавливаеммый
// уровень до допустимого 0..7 по маске
// поэтому немного схалтурим...
for (ii = 0; 5 * 8 + 1 > ii; ++ii) {
display.setBrightness(ii);
delay(96);
}
// очистка экрана
display.println();
delay(600);
// вывод чисел
// положительная температура
auto celsius = F("*C");
display.print(10);
display.println(celsius);
delay(1000);
// отрицательная температура
display.print(-2);
display.println(celsius);
delay(1000);
// очистка экрана
display.println();
++count;
}