Контроллер ворот на LCD грузил конфиг с SD «точками»: точка, точка, точка — и замолчал на полминуты. По этим точкам невозможно понять ни сколько прошло, ни сколько осталось: прогресс без цифры — это не индикация, а заглушка. Переделал на честный бар: полоса из блоков на второй строке и проценты сверху. Люди перестали выключать прибор «на середине» — когда видишь 78%, рука ждёт ещё немного.
Простая полоса загрузки из своих блоков уже была: базовый вариант с колонкой — в уроке про полосу загрузки; против мерцания и за точечную перерисовку — урок про diff-перерисовку. Здесь угол — арифметика: как уложить 100% в 16 колонок, не теряя плавности, и зачем хвосту пять плотностей вместо одного целого блока.
Арифметика: 16 ячеек на сто процентов
Одна ячейка из шестнадцати несёт 6,25% — грубовато: бар шёл бы рывками по шесть процентов. Спасает хвост: последний недозаполненный пиксель рисуется глифом промежуточной плотности. Беру пять глифов — по 1/5, 2/5, 3/5, 4/5 и целую ячейку — и шаг бара превращается из 6,25% в 1,25%: восемьдесят состояний вместо шестнадцати, глаз видит плавный рост. Пустой глиф не нужен — это обычный пробел.
Счёт веду в пятых долях ячейки: units = progress * 80 / 100 (16 ячеек по 5 долей), полные ячейки — units / 5, остаток units % 5 выбирает глиф хвоста. Проценты живут в верхней строке с фиксированной шириной — печать « 7%» вместо «7%» не оставляет хвостов от «100%», когда число скакнуло. Перерисовывать весь бар каждый кадр можно, но заметнее трогать только хвост — об этом заметка ниже.
#include <Wire.h>#include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27, 16, 2); byte b20[8] = {0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000};byte b40[8] = {0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000};byte b60[8] = {0b11100, 0b11100, 0b11100, 0b11100, 0b11100, 0b11100, 0b11100, 0b11100};byte b80[8] = {0b11110, 0b11110, 0b11110, 0b11110, 0b11110, 0b11110, 0b11110, 0b11110};byte b100[8] = {0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111}; // индексы слотов по остатку 0..5: пробел, 1/5, 2/5, 3/5, 4/5, целаяconst byte BARS[6] = {' ', 0, 1, 2, 3, 4}; void drawBar(int progress) { // progress 0..100 lcd.setCursor(0, 0); lcd.print(" "); lcd.print(progress); lcd.print("% "); // фикс-ширина, без хвостов int units = progress * 80 / 100; // 16 ячеек по 5 долей int full = units / 5; // полных ячеек int rest = units % 5; // плотность хвоста 0..5 for (int i = 0; i < 16; i++) { lcd.setCursor(i, 1); if (i < full) lcd.write(byte(4)); // целая ячейка else if (i == full && rest > 0) lcd.write(byte(BARS[rest])); else lcd.print(' '); }} int progress = 0; void loop() { progress += 1; // здесь грузится ваш конфиг if (progress > 100) progress = 0; drawBar(progress); delay(300);}Разбор. units = progress * 80 / 100 — это переворот обычной дроби: вместо «сколько процентов в ячейке» считаем «сколько пятых долей в прогрессе», и остаток сам становится номером глифа. Проверьте крайние точки: progress = 1 даёт units = 0, full = 0, rest = 0? Нет — 1*80/100 = 0, rest = 0, бар пуст; прогресс 2 даст units = 1 — первая пятая блока. На 100% units = 80, full = 16, rest = 0 — все ячейки целые, хвоста нет, ровно то, что ожидает глаз на финише.
delay(300) здесь стоит только для демо; в приборе темп задаёт ваша загрузка: каждая операция (строка с SD, шаг калибровки) дёргает progress и drawBar. Пять слотов из восьми — цена вопроса: на градус и стрелку остаётся три, и этого хватает. Если глифов стало впритык, 1/5 и 2/5 можно слить в один блок на треть — шаг станет 2,5%, всё ещё плавнее рывков по 6,25.
| Остаток rest | Глиф (слот) | Заполнение ячейки |
|---|---|---|
| 0 | пробел | пустая |
| 1 | 0b10000 (0) | 1/5 |
| 2 | 0b11000 (1) | 2/5 |
| 3 | 0b11100 (2) | 3/5 |
| 4 | 0b11110 (3) | 4/5 |
| 5 | 0b11111 (4) | целая |
- 1. Сколько процентов несёт одна ячейка бара на 16 колонок?
- 2. Зачем хвосту бара пять плотностей вместо одного целого блока?
- 16 колонок на 100% — ячейка 6,25%; пять блоков плотности дают шаг 1,25%
- Счёт в пятых долях: units = progress * 80 / 100, full = units / 5, rest = units % 5
- Пять слотов: 0b10000, 0b11000, 0b11100, 0b11110, 0b11111; пустая ячейка — пробел
- Проценты печатаются фикс-шириной (« 7%»), иначе остаются хвосты от «100%»
- Перерисовывайте зону хвоста, а не весь бар — против подёргивания
Частые вопросы по теме урока
- Сколько процентов несёт одна ячейка бара на 16 колонок?
- 100 / 16 = 6,25%. Отсюда и шаг в пятых долях: одна пятая ячейки даёт шаг 1,25%, а пять глифов плотности — восемьдесят состояний бара. 6,25%
- Зачем хвосту бара пять плотностей вместо одного целого блока?
- Целый блок даёт 16 состояний, пять плотностей — 80. На короткой загрузке разница между «стоит» и «ползёт» видна именно на хвосте. Шаг бара становится 1,25% вместо 6,25% — рост виден плавно