В этой статье описываются простые часы на базе процессора 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 Количество загрузок: 213 | Скачать |





