В этой статье описываются простые часы на базе процессора ATtiny85, в которых для отображения времени в 12-ти часовом формате используютя два светодиодных матричных дисплея с интерфейсом I2C.
Каждую минуту цифры случайным образом "растворяются", и дисплей становится пустым, а затем таким же образом появляются цифры обновленных часов и минут, создавая впечатление, что время распределяется между двумя экранами.
Вступление
На двух светодиодных дисплеях с матрицей 8x8 можно разместить четыре цифры для отображения времени, используя символы размером 3x8, и даже останется место для двоеточия между часами и минутами.
В данном устройстве используются два матричных дисплея Keyestudio на основе драйвера HT16K33, который управляет матрицами методом мультиплексирования и имеет интерфейс I2C, что позволяет общаться с ним при помощи всего двух линий ввода/вывода.
Три перемычки на плате позволяют выбрать один из восьми адресов I2C для каждого дисплея, что позволяет одновременно управлять восемью дисплеями. Можно выбрать адрес от 0x70 (112) до 0x77 (119), а адрес по умолчанию - 0x70 или 112.
Схема
Кроме микроконтроллера в схеме используется кварцевый резонатор частотой 8 МГц. Кнопка, подключенная к выводу PB1, позволяет установить время. На платах индикаторов уже имеются подтягивающие резисторы на SCL и SDA, поэтому устанавливать их в цепь не нужно.
Сборка
Схема собрана на макетной плате, индикаторы установлены вертикально в передней части платы. Вот вид макета:
Часы питаются от перезаряжаемой батареи Li-Po 3,7 В.
Программа
Сначала определяем наборы символов для цифр размером 3x8, от 0 до 9:
char CharMap[10][3] = { { 0x7F, 0x41, 0x7F }, // 0 { 0x00, 0x20, 0x7F }, // 1 { 0x4F, 0x49, 0x79 }, // 2 { 0x49, 0x49, 0x7F }, // 3 { 0x78, 0x08, 0x7F }, // 4 { 0x79, 0x49, 0x4F }, // 5 { 0x7F, 0x49, 0x4F }, // 6 { 0x40, 0x40, 0x7F }, // 7 { 0x7F, 0x49, 0x7F }, // 8 { 0x79, 0x49, 0x7F }, // 9 };
Переменные adr1 и adr2 используются для указания адресов двух дисплеев:
const int addr1 = 117; const int addr2 = 116;
Подпрограммы setupDisplay() и setBrightness() настраивают драйвер дисплея HT16K33 и устанавливают яркость от 0 до 15:
void setupDisplay (int addr) { Wire.beginTransmission(addr); Wire.write(0x21); Wire.endTransmission(); Wire.beginTransmission(addr); Wire.write(0x81); Wire.endTransmission(); } void setBrightness (int addr, int bri) { Wire.beginTransmission(addr); Wire.write(0xe0 + bri); Wire.endTransmission(); }
Подпрограмма putColumn() записывает битовый шаблон для одного столбца (от 0 до 15) для соответствующего дисплея:
void putColumn (int col, int bits) { int addr; if (col < 8) addr = addr1; else addr=addr2; Wire.beginTransmission(addr); Wire.write((col & 0x07)<<1); Wire.write(bits>>1 | bits<<7); Wire.endTransmission(); }
Выражение:
(bits >> 1)|(bits << 7)
исправляет момент, что по какой-то причине строки этих дисплеев нумеруются в последовательности 7, 0, 1, 2, 3, 4, 5, 6 снизу вверх.
Подпрограмма copyDigit() копирует три столбца для определения символа указанной цифры в три последовательных элемента в массиве columns[], начиная со столбца col:
void copyDigit (int digit, int col, int columns[]) { for (int i=0; i < 3; i++) columns[col+i] = CharMap[digit][i]; }
Наконец, showTime() отображает время в часах и минутах:
void showTime (int hours, int mins, int halfseconds) { int cols[16]; // Массив столбцов матрицы // Десятки часов if (hours/10 != 0) { cols[0] = CharMap[1][1]; cols[1] = CharMap[1][2]; } else { cols[0] = 0; cols[1] = 0; } // Единицы часов cols[2] = 0; copyDigit(hours % 10, 3, cols); // Двоеточие cols[6] = 0; if (halfseconds & 1) cols[7] = 0x02; else cols[7] = 0x10; // Десятки минут cols[8] = 0; copyDigit(mins / 10, 9, cols); // Единицы минут cols[12] = 0; copyDigit(mins % 10, 13, cols); if (halfseconds == 119 && !ButtonDown()) { FadeOut(cols); } else if (halfseconds == 0 && !ButtonDown()) { FadeIn(cols); } else { // Обычный режим for (int col=0; col<16; col++) { putColumn(col, cols[col]); } } }
Эффект растворения
Эффект растворения включается при каждом изменении минут:
Это реализуется с помощью следующих двух процедур:
Подпрограмма FadeOut() гасит пиксели на предыдущем экране:
void FadeOut(int cols[]) { for (int i=0; i<127; i++) { int r = pseudoRandom(); int col = r & 0xF; int bit = r>>4; cols[col] = cols[col] & ~(1<<bit); putColumn(col, cols[col]); delay(3); // Max. 3 } }
а затем процедура FadeIn() зажигает пиксели на новом экране:
void FadeIn(int cols[]) { int newcols[16]; for (int i=0; i<16; i++) newcols[i] = 0; for (int i=0; i<127; i++) { int r = pseudoRandom(); int col = r & 0xF; int bit = r>>4; newcols[col] = newcols[col] | (cols[col] & 1<<bit); putColumn(col, newcols[col]); delay(3); // Max. 3 } }
Значение бита вычисляется с помощью генератора псевдослучайных чисел, который генерирует числа от 1 до 127 в псевдослучайной последовательности. Каждое число используется для выбора одной из точек, чтобы создать видимость случайной последовательности изменений:
int pseudoRandom() { static int r = 1; int l = r & 1; r = r>>1; if (l == 1) r = r ^ 0x69; return r; }
Кнопка установки времени
Кнопка "Установить время" позволяет установить правильное время. При удержании кнопки происходит увеличение значения часов с интервалом два раза в секунду. Отпустив и нажав кнопку еще раз, происходит увеличение минут также с интервалом два раза в секунду.
Кнопка подключается к выводу PB1, у которого настроена подтяжка к плюсу питания. Состояние кнопки обрабатывается функцией ButtonDown(), которая возвращает true, если кнопка нажата:
boolean ButtonDown() { return (digitalRead(1) == 0); // Если кнопка нажата, то истина }
Процедура updateTime() вызывается дважды в секунду и обновляет текущее время, также в зависимости от состояния кнопки "Установить время" обновляются часы или минуты:
void updateTime() { int minutes, hours, halfseconds; halfseconds = Time % 120; minutes = (Time / 120) % 60; hours = (Time / 7200) % 12; if (ButtonDown()) { if (ButtonState == 1 || ButtonState == 3) { ButtonState = (ButtonState + 1) % 4; } else if (ButtonState == 2) { // Подготовка часов hours = (hours + 1) % 12; } else { // Подготовка минут minutes = (minutes + 1) % 60; } Time = (unsigned long)hours * 7200 + minutes * 120 + 1; } else { // Кнопка не нажата if (ButtonState == 0 || ButtonState == 2) { ButtonState = (ButtonState + 1) % 4; } Time = (Time + 1) % 172800; // Повтор через 24 часа } showTime(hours+1, minutes, halfseconds); }
Чтобы эффект "растворения" не раздрожал во время устанавки времени, он отключается, если кнопка нажата.
Основной цикл просто вызывает функцию updateTime() каждые 500 мс:
void loop() { while (millis() - Start < 500); Start = Start + 500; updateTime(); }
Компилирование программы
Программа скомпилирована с помощью Spence Konde's ATTiny Core, для этого выберите вариант ATtiny25/45/85 под заголовком ATTinyCore в меню плат. Затем убедитесь, что следующие параметры установлены следующим образом (игнорируйте любые другие параметры):
Chip: "ATtiny85"
Clock: "8 MHz (external)"
B.O.D Level: "B.O.D. Disabled"
Выберите записать загрузчик чтобы установить биты конфигурации для использования с внешним кристаллом. Затем загрузите программу с помощью ISP программатора (внутрисистемное программирование).
Дальнейшие предложения
Данные дисплеи очень яркие, и вы можете затемнить их после наступления темноты для экономии энергии. Для этого достаточно добавить функцию, которая регулирует яркость, вызывая setBrightness() в зависимости от времени суток, используя глобальную переменную Time.
Дисплеи поддерживают восемь разных адресов I2C, поэтому к данной схеме можно полключить до восьми дисплеев. Например, вы можете расширить этот проект, чтобы отображать время в четырех разных точках мира.
Наконец, преимущество в использовании точечно-матричных дисплеев заключается в том, что вы можете настроить отображаемые цифры в соответствии со своими предпочтениями. Например, вот несколько альтернативных определений символов для округленных цифр:
char CharMap[10][3] = { { 0x3E, 0x41, 0x3E }, // 0 { 0x00, 0x20, 0x7F }, // 1 { 0x27, 0x49, 0x31 }, // 2 { 0x2A, 0x49, 0x36 }, // 3 { 0x18, 0x28, 0x7F }, // 4 { 0x7A, 0x49, 0x4E }, // 5 { 0x3E, 0x49, 0x26 }, // 6 { 0x47, 0x48, 0x70 }, // 7 { 0x36, 0x49, 0x36 }, // 8 { 0x32, 0x49, 0x3E }, // 9 };
Файлы к статье "Часы на ATtiny85 и светодиодной матрице с драйвером HT16K33" | |
Описание:
Исходный код(СИ) |
|
Размер файла: 1.67 KB Количество загрузок: 170 | Скачать |