Первый вечер с библиотекой TM1637Display у меня ушёл на три промаха подряд, и все три — из одной серии: я сыпал в функции числа, а библиотека молча их резала или ставила не туда. Хотел показать счётчик в шестнадцатеричном виде, скормил showNumberHex число 4095 — на экране горело честное FF, старшие байты ушли мимо окна. Потом выводил температуру в правых трёх разрядах через showNumberDec(t, false, 3, 1) и полчаса ждал её слева, пока не понял, что позиция отсчитывается от другого края. Экран был исправен все три раза, датчик тоже — я просто не читал сигнатуры методов, а они у этой библиотеки короче документации, но с подвохом.
В этом уроке пройдём по библиотеке как по инструментальной панели: установка и конструктор с пинами CLK и DIO, восемь уровней яркости, четыре метода вывода с их аргументами и свои знаки через сырые байты сегментов. Отдельно разберу ловушку, на которой спотыкается почти каждый: двоеточие, зажжённое через showNumberDecEx с 0x40, гаснет от первого же обычного showNumberDec. База — что за чип, почему двух проводов хватает и как модуль выглядит в схеме — в уроке про сам индикатор TM1637, распиновка с фотографиями шёлка — на лендинге про подключение TM1637; здесь — работа из скетча.
Установка и конструктор: две строки до первой цифры
Ставится библиотека из менеджера библиотек Arduino IDE по двум словам tm1637 в поиске; автор Avishay Orpaz, и из одноимённых сборок она самая живая. Если поиск ничего не отдаёт или вы вообще первый раз открываете менеджер — я разбирал установку библиотек по шагам в уроке про библиотеки в IDE. Дальше вся церемония: #include <TM1637Display.h> в шапке и строка TM1637Display disp(4, 5); рядом с ним — в скобках пины CLK и DIO, строго в этом порядке. Я сажаю дисплей на D4 и D5, потому что D2 и D3 у меня всегда под кнопки; брать можно любые цифровые пины, ограничений у чипа нет. Перепутанные местами CLK и DIO не сжигают ничего — экран показывает мусор или молчит, лечится перестановкой аргументов.
В setup — disp.setBrightness(5). Аргумент — целое от 0 до 7: внутри чипа восемь ступеней скважности, и почему значение 255 не делает дисплей ярче, я уже разбирал в вводном уроке. Здесь про практику: 2-3 — для прибора, который стоит на столе и горит сутками, 5 — универсальный уровень по умолчанию, 7 — только под прямым солнцем из окна, иначе слепит и сушит глаза вечером. Тёмную комнату я решаю не уменьшением яркости, а полным гашением вторым аргументом — disp.setBrightness(5, false) тушит дисплей, но чип помнит кадр, и включение обратно возвращает те же цифры без перерисовки.
| Метод | Аргументы | Когда беру |
|---|---|---|
| showNumberDec(num, leading, length, pos) | число int, ведущие нули, длина окна, позиция слева | счётчики, температура, любые десятичные цифры |
| showNumberDecEx(num, dots, leading, length, pos) | то же плюс байт точек: 0x40 — двоеточие | часы и таймеры, где двоеточие должно гореть |
| showNumberHex(num, leading, length, pos) | только байт 0-255, не int | адреса устройств и дампы регистров при отладке |
| setSegments(массив, length, pos) | сырые байты сегментов a-g | минус, градус, прочерки, любые свои знаки |
| setBrightness(y, on) | яркость 0-7 и включённость | подстройка под место установки и ночное гашение |
showNumberDec: четыре аргумента вместо одного
Сигнатура showNumberDec(num, leading_zero, length, pos) выглядит громоздко, но аргументов по делу два с половиной. num — само число, обычный int: 42 ляжет как 42, -7 как -7 со знаком; про то, что происходит с числом больше 9999, я писал в вводном уроке — счётчики с большим потолком режьте сами. leading_zero — ведущие нули: false прижимает число вправо и гасит пустые разряды, true дорисовывает нули слева — 0042 вместо 42. Для часов true обязательно: без него 12:05 показывается одинокой пятёркой у правого края, и глаз читает это как пять.
length и pos режут окно: length — сколько разрядов занимает число, pos — с какого разряда рисовать, и счёт идёт слева: pos 0 — крайний левый разряд, pos 3 — крайний правый. Отсюда мой вечерний промах: я мысленно считал разряды справа и передавал pos 3, ожидая цифру слева. Пара length и pos нужна, когда число занимает не весь экран: температура в правых трёх разрядах при знаке в левом, счёт партий при служебном разряде. Важная мелочь: разряды вне окна библиотека не трогает — там остаётся всё, что вы рисовали раньше; если старые сегменты мешают, гасите их сами через setSegments с нулевым байтом.
showNumberHex устроен хитрее, чем кажется: число в нём — uint8_t, байт, а не int. Потолок — 255, то есть 0xFF: передадите 4095 — на экране загорится FF, старшие байты уйдут без предупреждений, и никто в компиляторе не пикнет. Для чего HEX вообще живёт: адреса устройств на шине при отладке, дампы регистров, сверка с листингами даташита — все случаи, когда вы сами читаете экран, а не прибор для людей. В пользовательских приборах я его не использую вовсе: человек, которому нужен 0x1F на счётчике оборотов, уже открыл монитор порта. Байтовый потолок держите в голове каждый раз, когда соблазняетесь показать большое число в hex: сначала уместите его в байт руками и решите, куда деть старшую часть.
Отдельной функции показать одну цифру в заданном разряде в библиотеке нет — её роль делят length и pos у чисел и setSegments у сырых байтов. Это осознание сэкономило мне час поиска: я листал шапку библиотеки в поисках showDigitAt, которого не существует, пока не понял, что showNumberDec(3, false, 1, 0) и есть та самая одна цифра в левом разряде.
// Демо-тур по методам TM1637Display: кнопка D2 листает режимы#include <TM1637Display.h> TM1637Display disp(4, 5); // CLK на D4, DIO на D5 const int btnPin = 2;int mode = 0;int prevBtn = HIGH; void setup() { pinMode(btnPin, INPUT_PULLUP); disp.setBrightness(5); Serial.begin(9600); Serial.println("Knopka D2: sleduyushchij rezhim");} void loop() { int btn = digitalRead(btnPin); if (btn == LOW && prevBtn == HIGH) { // срабатывание по фронту mode = (mode + 1) % 4; Serial.print("rezhim="); Serial.println(mode); delay(40); // антидребезг на нажатии } prevBtn = btn; long s = millis() / 1000; // счётчик секунд для демо if (mode == 0) { disp.showNumberDec(s); // прижато вправо, пустые гаснут } else if (mode == 1) { disp.showNumberDec(s, true); // ведущие нули: 0042 } else if (mode == 2) { disp.showNumberDec(s, false, 3, 1); // число в правых трёх разрядах byte znak[1] = { 0x40 }; disp.setSegments(znak, 1, 0); // минус в крайнем левом } else { byte tire[4] = { 0x40, 0x40, 0x40, 0x40 }; disp.setSegments(tire, 4, 0); // прочерк на весь экран } delay(50);}Свои знаки: байты сегментов и комбинирование кадра
setSegments принимает массив байтов, где каждый бит — сегмент a-g: 0x3F рисует ноль, 0x06 единицу, 0x40 — минус в пустом разряде, 0x63 — знак градуса, 0x39 — букву C. Аргументы length и pos работают как у чисел: setSegments(gradus, 1, 0) пишет один байт в крайний левый разряд. Комбинация двух вызовов — главный приём термометров и любых приборов с единицей измерения: showNumberDec(t, false, 3, 1) рисует число в правых трёх разрядах, setSegments — градус слева, и экран читается как 23 без двусмысленностей. Каждый бит в байте — отдельный сегмент, и если захочется собрать свой знак, придётся вспомнить, как складывать и снимать биты: я разбирал это на масках шины I2C в уроке про битовые операции.
Практическое правило, которое я вывел после пары экранов-каши: сырые байты — только для знаков, числа — всегда через showNumberDec. Когда весь кадр собирается из setSegments, скетч обрастает магическими константами 0x5B и 0x6D, и правка через месяц превращается в расшифровку. Исключение одно — прочерк на весь экран: массив из четырёх 0x40, он и читается мгновенно, и рисуется одной строкой.
Симулятор повторяет скетч-шпаргалку: кнопка листает четыре кадра, и разница между режимами видна мгновенно — прижатое вправо число против ведущих нулей, число в трёх разрядах с минусом слева против прочерка на весь экран. Честная оговорка про разницу движков: в симуляторе showNumberHex(0xABCD) нарисует «ABCD» — число режется до четырёх последних hex-разрядов, а на живой плате метод принимает только байт: передадите 4095, увидите FF, как разобрано выше. Обе ветки теперь проверяются в браузере — меняйте аргумент и следите, что осталось на экране. Покликайте кнопку быстро: антидребезг в 40 мс держит фронт, и режим не перескакивает через один. Кнопка здесь опрашивается каждый проход loop() — почему это принципиально для всех приборов с экранами, стало темой урока про дребезг кнопки, а у нас — заготовка следующего урока про часы.
- TM1637Display disp(CLK, DIO) и setBrightness(0-7) — весь запуск; уровней восемь, ярче седьмого физически нет
- showNumberDec(num, leading, length, pos): позиция отсчитывается слева, разряды вне окна не трогаются — хвосты гасите сами
- showNumberHex принимает только байт: потолок 0xFF, старшие байты уходят молча — HEX для отладки, не для пользователей
- Двоеточие — бит 0x40 в байте разряда: любой showNumberDec его гасит, вписывайте 0x40 в каждый кадр
- Свои знаки — setSegments с байтами: 0x40 минус, 0x63 градус; числа — всегда через showNumberDec, а не байтами
- 1. Что покажет disp.showNumberHex(4095)?
- 2. Куда встанет цифра при вызове showNumberDec(3, false, 1, 0)?
- 3. Часы рисуют время через showNumberDecEx(x, 0x40, true), а температуру — через showNumberDec(t). Что станет с двоеточием?
- 4. Каким байтом setSegments рисуют знак градуса?
- 5. Разряды, которые не вошли в окно length и pos, — что с ними?
Частые вопросы по теме урока
- Что покажет disp.showNumberHex(4095)?
- Число в showNumberHex — uint8_t: всё, что выше 255, обрезается до байта, и компилятор молчит. Большие числа уместите в байт сами. FF — аргумент байт, старшие байты ушли без предупреждения
- Куда встанет цифра при вызове showNumberDec(3, false, 1, 0)?
- length — сколько разрядов, pos — с какого рисовать, и счёт слева: pos 0 — крайний левый разряд, pos 3 — крайний правый. в крайний левый — счёт позиций идёт слева
- Часы рисуют время через showNumberDecEx(x, 0x40, true), а температуру — через showNumberDec(t). Что станет с двоеточием?
- Бит двоеточия лежит в байте разряда, и showNumberDec перезаписывает кадр целиком. 0x40 нужно вписывать в каждый кадр, где двоеточие горит. погаснет после первого вывода температуры
- Каким байтом setSegments рисуют знак градуса?
- 0x63 — градус, 0x3F — ноль, 0x40 — минус, 0x06 — единица. Таблица байтов лежит в шапке библиотеки. 0x63
- Разряды, которые не вошли в окно length и pos, — что с ними?
- Библиотека трогает только разряды внутри окна. Хвосты от старых кадров гасятся отдельно — setSegments с нулевым байтом. остаются как были — там висит предыдущий кадр