Давно было намерение сделать универсальный таймер, который можно было бы использовать для разных процессов, где требуется выдержка определённого времени и, который бы мог включать/отключать, по истечении времени, различную нагрузку, вплоть до устройств на ~220 В. Время пришло. Схема и программа разработаны. Основой этого таймера послужили проекты Таймер для катушки Мишина и МТР-1.2 (термометр-регулятор).
Как часто бывает, начальный проект всегда имеет какие-то недостатки и более оптимальное решение приходит уже после того, как углубляешься в тему. Так и в этом случае с таймером. После проверки первой версии, пришёл к написанию второй, которую считаю гораздо лучше, оптимальнее и имеющую большие возможности.
Начиная с версии v2.2 дополнительно применена условная компиляция, что позволило легко выбирать вариант логики управления кнопками: HIGH или LOW.
Начиная с версии v2.4 появилась возможность выбирать количество разрядов в индикаторе (3 или 4).
Кнопкой [Часы] устанавливаются часы до 99 часов (чуть больше 4-х суток), в первых двух разрядах таймера.
Кнопкой [Минуты] устанавливаются минуты до 59, в третьем и четвёртом разрядах. Часы и минуты разделены точкой второго разряда.
Кнопкой [Старт/Стоп] производится пуск и остановка таймера. Если таймер запущен, то разделительная точка второго разряда мигает. Если таймер остановлен, то точка второго разряда светится постоянно. Точка четвёртого разряда указывает на подключение модуля реле. Если модуль реле отключен, то точка четвёртого разряда не светится.
При одновременно нажатии на кнопки [Часы] и [Минуты] производится сброс установленного времени и на индикаторе выводится [0.00], первый незначащий 0 гасится.
При одновременно нажатии на кнопки [Минуты] и [Старт/Стоп] производится подключение [0.00.] или отключение [0.00] модуля реле, так как бывает, что не всегда необходимо управлять какой-то нагрузкой, поэтому сделал так, чтобы модуль реле можно было бы отключить программно, сигнал же об окончании работы таймера осуществляется звуком через буззер.
Таймер v2
Управляется таймер так же тремя кнопками:
MODE – Выбор параметра настройки
MINUS – Убавить [–]
PLUS – Прибавить [+]
С помощью этих кнопок теперь возможно устанавливать время в двух режимах:
hh.mm(.) – часы.минуты(.)
mm.ss(.) – минуты.секунды(.)
Возможно через меню настроек:
Включение/Отключение звука
Включение/Отключение активности модуля реле
Установка яркости индикатора [0...7]
По нажатию на кнопку MODE происходит переход в настройки. Каждое нажатие – переход к следующему параметру. В крайнем левом разряде выводится номер пункта меню настройки. Так на индикаторе будет выводится следующая информация:
Индикация текущего времени: hh.mm(.) | mm.ss(.) (зависит от настройки в mode=1)
Выбран режим hh.mm (часы.минуты) - на индикаторе: 10.00(.)[1.00(.)]
Выбран режим mm.ss (минуты.секунды) - на индикаторе: 10.01(.)[1.01(.)]
Установка часов|минут – на индикаторе: 20.хх [2.xx(.)], где хх = 0...99
Установка минут|секунд – на индикаторе: 30.хх [3.xx(.)], где хх = 0...59
Активность реле включена – на индикаторе: 40.01. [4.00.] - свечение точки DIG4
Активность реле отключена – на индикаторе: 40.00 [4.00] - точка DIG4 не светится
Активность зуммера включена – на индикаторе: 50.01(.)[5.01(.)]
Активность зуммера отключена – на индикаторе: 50.00(.)[5.00(.)]
Яркость индикатора – на индикаторе: 60.0х[6.0x(.)], где х = 0...7
Запуск таймера производится кнопкой PLUS.
Остановка запущенного таймера на паузу производится кнопками PLUS, MINUS. При работе таймера кнопка MODE блокируется. Настройка таймера возможна только при остановленном таймере.
В момент сигнала об окончании работы таймера, прервать сигнал можно нажатием на любую кнопку.
Управление достаточно интуитивное и позволяет легко настраивать и пользоваться таймером.
Схема
Основой универсального таймера является плата Ардуино (Arduino Uno, Arduino Nano, Arduino Pro Mini и подобные). Управление осуществляется сенсорными кнопками TTP223, конечно можно вместо них использовать и обычные кнопки.
Индикация таймера осуществляется на четырёх разрядном семисегментном светодиодном индикаторе. В первом варианте схемы (только для версии v1) используется индикатор с общим катодом, который управляется сдвиговым регистром SN74HC595N. В настоящее время это вполне рабочий бюджетный вариант.
Исполнительным устройство является модуль реле K1, с управляющим сигналом высокого уровня (только для версии v1). У меня самодельный модуль, но возможно использование любого модуля и покупного в том числе.
По окончании работы таймера выдаётся звуковой сигнал через буззер активного типа BA1. Выключатель SA2 позволяет отключать звук окончания работы таймера, к примеру, когда окончание работы таймера приходится на ночное время.
Питание таймера осуществляется от внешнего источника питания +5В.
Pic 1. Схема электрическая принципиальная. Вариант 1
Второй вариант схемы в качестве индикатора использует модуль на TM1637. Для контроллера TM1637 используются светодиодные семисегментные индикаторы с общим анодом. В данном случае модуль самодельный, если использовать покупной модуль, то в нём должна быть возможность управления точками разрядов.
Pic 2. Схема электрическая принципиальная. Вариант 2
В версии v2 программы таймера используется логика управления низким уровнем выходными устройствами. Кнопки имеют несколько другие функции, поэтому схема изменилась. Попутно отпала необходимость в выключателе звука SA2. Для совместимости вариантов, управляющие пины остались прежними. Исходя из логики управления низким уровнем выходными устройствами, изготовил модуль реле, который управляется (включается) подачей на вход сигнала низкого уровня - LOW.
Pic 3. Схема электрическая принципиальная. Версия v2
С версии v2.2 программы таймера сделана возможность выбирать логику управления кнопками. Возможно использовать сенсорные кнопки типа TTP223 (с логикой HIGH), или обычные кнопки (с логикой LOW). Так же для совместимости вариантов, управляющие пины остались прежними. Реализация совместимости программная.
Pic 4. Схема электрическая принципиальная. Версия v2.2
Макетирование схемы
Вначале программа отлаживалась на макете. Ниже фото макета схемы 1. На фото модуль с кнопками снят. Программа проверялась на Arduino Pro Mini.Pic 5. Универсальный таймер Макет схемы 1
Далее отлаживалась программа для схемы 2. Модуль индикатора собран на четырёх разрядном индикаторе LN-5644-11Q(общий анод). Микросхема TM1637 припаяна к плате стенда проверки индикаторов. В момент запуска таймера включается модуль реле. На фото видно свечение зелёного светодиода на модуле реле (управление высоким уровнем). Подключение модуля реле указывается свечением точки четвёртого разряда (DIG4).
Индикатор LN-5644-11Q был использован для создания модуля индикации на TM1637, который применил в макете универсального таймера v2. Модуль реле изготовлен с управляющим LOWIN. Светодиод его включения красный.
Pic 8. Универсальный таймер Макет схемы 2
Ниже на фото модули реле своего изготовления с управляющими HIGHIN и LOWIN. Светодиод включения у HIGHIN зелёного цвета, а у LOWIN – красного цвета.
Pic 9. Модули реле
Пробовал использовать индикатор 51936N с высотой символов 9,2 мм (0.36'). Он установлен в панельку стенда проверки индикаторов.
Pic 10. Универсальный таймер Макет схемы 2
Собрал вариант на покупном модуле индикатора с TM1637 и обычных кнопках. На стенде с кнопками используются три левых кнопки. Общий у них GND. Логика управления кнопками переключена в скетче на LOW.
В качестве МК использован китайский вариант LGT8F328, который установлен на плату подобную Arduino Nano. Попутно стоит заметить, что эта плата очень капризная и всё время виснет, особенно в режиме отладки, когда необходимо контролировать процессы через монитор последовательного порта. Для устранения этого недостатка были дополнительно припаяны два SMD конденсатора ёмкостью по 100nF между выводами платы IN(Внешнее питание) и GND и между +5V и GND. После такой небольшой доработки плата с LGT8F328 работает без сбоев.
Вместо реле в этом макете используется красный светодиод.
Этот вариант практически любой может реализовать очень легко, так как все части схемы доступны.
Pic 11. Универсальный таймер Макет схемы 2.2
Скетч
Ниже приводится скетч версии v1 второго варианта схемы с индикатором на TM1637. Скетч версии v1 первого варианта схемы на индикаторе с управлением через сдвиговый регистр SN74HC595N можно скачать в Приложении.
/*************************************************************
2022-03-11 Mr.ALB Универсальный таймер на TM1637
Индикатор 7-сегментный 4-х разрядный
LN5644 - общий анод! Можно любой другой,
но с общим анодом!
Кнопки TP223 - сенсорные.
Кнопки:
1. HOUR – Установка часов
2. MIN – Установка минут
3. START – Старт/Стоп
С помощью кнопки HOUR устанавливаются часы до 99 (чуть больше 4-х суток).
С помощью кнопки MIN устанавливаются минуты до 59.
Кнопкой START осуществляется пуск и остановка таймера.
Кнопки MIN и START нажать одновременно для
включения/выключения активности реле.
Индикация активности реле – светящаяся точка 4-го разряда.
Кнопки MIN и HOUR нажать одновременно для обнуления
установленного времени.
Работа таймера – мигание точки 2-го разряда.
Таймер в режиме паузы или ожидания пуска –
постоянно светящаяся точка 2-го разряда.
Ранее установленное время после окончания работы
таймера сохраняется и новый пуск таймера
осуществляется только нажатием кнопки START.
****************************************************************/
#include <TimerOne.h> // Подключение библиотеки Таймер1
#include "TM1637_mralb2.h" // Библиотека для TM1637
#define BT_HOUR 10
#define BT_MIN 11
#define BT_START 12
#define BUZZER 9
#define OUT 8
#define CLK 3
#define DIO 2
TM1637 disp(CLK, DIO); // Создание объекта индикатора
// Массив символов
static int8_t simb[] =
{
0x3f, 0x06, 0x5b, 0x4f, // 0, 1, 2, 3
0x66, 0x6d, 0x7d, 0x07, // 4, 5, 6, 7
0x7f, 0x6f, 0x00, 0x80 // 8, 9, 10 - пусто, 11 - точка
};
#define BUZZER 9 // Зуммер
#define MAXALARM 9 // Сколько раз бикнет буззер
/* Глобальные переменные для времени */
uint32_t t1, dt;
byte hour_time = 0, hour_set;
int min_time = 0, min_set;
bool flag_run = false; // Флаг работы таймера
bool flag_rele = true; // Флаг подключения реле
// Переменная для прерывания, мигание точек
volatile boolean flag_point;
/***********************
* Функция настройки
************************/
void setup()
{
//Serial.begin(9600);
pinMode(BT_HOUR, INPUT); // Установка часов
pinMode(BT_MIN, INPUT); // Установка минут
pinMode(BT_START, INPUT); // Старт/стоп таймера
pinMode(OUT, OUTPUT); // Управляющий выход
digitalWrite(OUT, LOW); // Отключим выход
pinMode(BUZZER, OUTPUT); // Зуммер
digitalWrite(BUZZER, HIGH); // Отключим выход
disp.clear();
// Яркость минимум=0, стандарт=2, максимум=7)
disp.brightness(5);
// Для Ардуино Уно, загасить светодиод на пин13
// В других Ардуино не нужно
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
pinMode(BUZZER, OUTPUT); // Зуммер
digitalWrite(BUZZER, LOW); // Отключим выход
// Инициализация прерывания на 500 мс
// Для мигания точек
Timer1.initialize(500000);
flag_run = false;
// Проверка сегментов и зумера
fnTestSeg();
} // End setup
/***************************
* Рабочая программа
**************************/
void loop()
{
t1 = millis(); // Текущее время работы программы в мс
// Если нажать две кнопки, то обнуляем значение времени
while (digitalRead(BT_HOUR) && digitalRead(BT_MIN))
{
if (millis() - t1 >= 2000)
{ // Удерживать более 2 секунд
hour_time = 0;
min_time = 0;
fnStopTimer(); // Стоп таймер
fnPrintTime(); // Вывод текущего времени
fnMyDelay(500); // Задержка
break;
}
fnPrintTime(); // Вывод текущего времени
fnMyDelay(100); // Задержка
}
// Если нажать кнопки BT_MIN и BT_START,
// то Влючить/Выключить активность Реле
bool statBtStart, statBtMin;
statBtStart = debounce(false, BT_START);
statBtMin = debounce(false, BT_MIN);
while (!flag_run && statBtStart && statBtMin)
{
if (millis() - t1 >= 2000)
{ // Удерживать более 2 секунд
flag_rele = !flag_rele;
flag_point = true;
fnPrintTime(); // Вывод текущего времени
break;
}
statBtStart = debounce(false, BT_START);
statBtMin = debounce(false, BT_MIN);
fnPrintTime(); // Вывод текущего времени
}
// Было нажатие кнопки СТАРТ
if
(
debounce(false, BT_START) &&
!digitalRead(BT_HOUR) &&
!digitalRead(BT_MIN)
)
{
delay(500);
if (flag_run == false && (hour_time != 0 || min_time != 0))
{
// Запуск таймера
flag_run = true;
// Подключение прерывания
Timer1.attachInterrupt(blink);
// Включим выход
if (flag_rele)digitalWrite(OUT, HIGH);
hour_set = hour_time;
min_set = min_time;
dt = t1;
}
else
{
// Остановим таймер на ПАУЗУ
fnStopTimer();
flag_point = true;
}
}
// Установка часов
if
(
flag_run == false &&
digitalRead(BT_HOUR) == HIGH &&
!digitalRead(BT_START)
)
{
hour_time++;
if (hour_time > 99)hour_time = 0;
fnMyDelay(300);
}
// Установка минут
if
(
flag_run == false &&
digitalRead(BT_MIN) == HIGH &&
!digitalRead(BT_START)
)
{
min_time++;
if (min_time > 59)min_time = 0;
fnMyDelay(300);
}
fnPrintTime(); // Вывод текущего времени
// Проверка пройденной минуты
if (flag_run == true && ((t1 - dt) >= 60000))
{
min_time--;
if (min_time < 0 && hour_time != 0)
{
min_time = 59;
hour_time--;
}
// Завершение времени таймера
if (min_time == 0 && hour_time == 0)
{
fnStopTimer(); // Стоп таймер
fnAlarmTimer(); // Сигнал окончания таймера
// Восстановим начальные установки
// для нового запуска
hour_time = hour_set;
min_time = min_set;
flag_point = true;
fnPrintTime(); // Вывод текущего времени
delay(1000);
}
dt = t1;
}
} // End loop
/**********************************
ПОДПРОГРАММЫ
**********************************/
/****************************************
* Функция: Проверка сегментов и буззера
***************************************/
void fnTestSeg()
{
flag_point = true;
hour_time = 88;
min_time = 88;
fnPrintTime();
delay(2000);
digitalWrite(BUZZER, HIGH);
delay(100);
digitalWrite(BUZZER, LOW);
//disp.clear();
hour_time = 0;
min_time = 0;
} // End fnTestSeg
/******************************
Функция: Задержка в мс
*****************************/
void fnMyDelay(uint32_t mydelay)
{
dt = millis();
while ((millis() - dt) < mydelay)
{
if (digitalRead(BT_START)) break;
}
} // End fnMyDelay
/*************************
Функция мигания точек
************************/
void blink()
{
flag_point = !flag_point;
} // End blink
/******************************
Функция: Стоп таймер
*****************************/
void fnStopTimer()
{
flag_run = false;
if (flag_rele)
digitalWrite(OUT, LOW); // Отключим выход
Timer1.detachInterrupt(); // Остановка прерывания
} // End fnStopTimer
/***************************
Функция: сигнал таймера
***************************/
void fnAlarmTimer()
{
// Включаем сигнал
// Сигнал пульсирующий
// .5 секунды звук
// .5 секунды пауза
long t;
for (byte j = 0; j < MAXALARM; j++)
{
t = millis();
while (millis() - t < 500)
{
// Звук .5 секунды
digitalWrite(BUZZER, HIGH);
//PORTB |= (1 << PB5); // Включаем
fnPrintTime(); // Вывод текущего времени
// Если нажата кнопка START
// Выход из цикла while()
if (digitalRead(BT_START)) break;
}
t = millis();
while (millis() - t < 500)
{
// Пауза .5 секунды
digitalWrite(BUZZER, LOW);
//PORTB &= ~(1 << PB5); // Выключаем
fnPrintTime(); // Вывод текущего времени
// Если нажата кнопка START
// Выход из цикла while()
if (digitalRead(BT_START)) break;
}
// Если нажата кнопка START
// Выход из цикла for()
if (digitalRead(BT_START)) break;
fnPrintTime(); // Вывод текущего времени
}
} // End fnAlarmTimer
/****************************************
Вывод времени в формате: mm.ss
***************************************/
void fnPrintTime()
{
byte dig1 = hour_time / 10,
dig2 = hour_time % 10,
dig3 = min_time / 10,
dig4 = min_time % 10;
byte digit2, digit4;
// Вывод точки 2-го разряда
if (flag_point)
digit2 = simb[dig2] | _dp;
else digit2 = simb[dig2];
// Вывод точки 4-го разряда
if (flag_rele)
digit4 = simb[dig4] | _dp;
else digit4 = simb[dig4];
if (dig1 == 0) dig1 = 10; // Гашение незначащего нуля
// Вывод на индикатор
disp.displayByte(simb[dig1], digit2, simb[dig3], digit4);
} // End fnPrintTime
/****************************************
Функция для подавления дребезга
*****************************************/
boolean debounce(boolean last, byte buttonPin)
{
// Читаем состояние кнопки
boolean current = digitalRead(buttonPin);
if (last != current)
{
fnMyDelay(5);
current = digitalRead(buttonPin);
}
return current;
} // End debounce
После тестирования первой версии скетча, пришёл к мнению, что требуется значительная переделка управления, чтобы с помощью всего трёх кнопок создать полное управление всеми функциями универсального таймера. Была создана версия v2.
В итоге получилось создать меню настроек в котором 7 пунктов:
0 - Индикация текущего времени: hh.mm(.) | mm.ss(.) (зависит от режима 1)
1 - Режим hh.mm (часы.минуты) - 0 | режим mm.ss (минуты.секунды) - 1
На данный момент самая последняя версия v2.4. Её можно посмотреть ниже по нажатию на соответствующую кнопку (скрыл, чтобы не загромождать страницу), применена условная компиляция, чтобы легко можно было перейти на логику управления кнопками либо LOW, либо HIGH. Дополнительно, используя условную компиляцию, удалось быстро перенастраивать скетч на использование индикатора, имеющего всего три разряда. Модуль индикатора смотрите по ссылке: Модуль на TM1637, модификация платы v4.
/*************************************************************
2022-03-11 Mr.ALB Универсальный таймер на TM1637
Индикатор 7-сегментный 4-х разрядный (можно 3-х)
LN5644 - общий анод! Можно любой другой,
но с общим анодом!
Кнопки TP223 - сенсорные (можно обычные).
https://mralb.ru/sections/programming/arduino_24.php#content_center
В версии v1 Кнопки:
1. HOUR – Установка часов
2. MIN – Установка минут
3. START – Старт/Стоп
С помощью кнопки HOUR устанавливаются часы до 99
(чуть больше 4-х суток).
С помощью кнопки MIN устанавливаются минуты до 59.
Кнопкой START осуществляется пуск и остановка таймера.
Кнопки MIN и START нажать одновременно
для включения/выключения активности реле.
Индикация активности реле – светящаяся точка 4-го разряда.
Кнопки MIN и HOUR нажать одновременно
для обнуления установленного времени.
Работа таймера – мигание точки 2-го разряда.
Таймер в режиме паузы или ожидания пуска – постоянно светящаяся
точка 2-го разряда.
Ранее установленное время после окончания работы таймера сохраняется
и новый пуск таймера осуществляется только нажатием кнопки START.
2022-04-14 Mr.ALB Переделка на полное управление тремя кнопками
2022-04-15 Mr.ALB написана v2.0
- Имеется меню режимов настройки.
- Возможно устанавливать время в двух режимах:
1. hh.mm(.)
2. mm.ss(.)
- Вкл./Откл. звука
- Вкл./Откл. реле
- установка яркости индикатора 0...7
2022-04-16 Mr.ALB v2.1 оптимизация кода
2022-04-17 Mr.ALB v2.2 адаптация под LGT8F + создание
адаптивной компиляции под разную логику кнопок.
Теперь можно использовать кнопки с логикой
управления высоким уровнем HIGH или кнопки с
логикой управления низким уровнем LOW
2022-04-20 Mr.ALB v2.3 предварительный сигнал при остатке
времени 5 мин
2022-04-28 Mr.ALB v2.4 логика управления кнопками TTP223 - LOW
+ отладка + оптимизация + выбор индикатора
на 3 или 4 разряда
Кнопки TTP223 (логика HIGH) или обычные (логика LOW):
1. MODE – Выбор режима
2. PLUS – Прибавить +
3. MINUS – Убавить -
* Примечание: Если у TTP223 замкнута перемычка А, то
* выбрать логику управления #define LOGICA 0
Соединения:
DIO - пин 2
CLK - пин 3
Buzzer - пин 9
MODE - пин 10
MINUS - пин 11
PLUS - пин 12
Реле - пин А0
2022-04-15 Скетч использует 4012 байт
2022-04-16 Скетч использует 3854 байт
2022-04-17 Скетч использует 5596 байт - режим отладки
2022-04-18 Скетч использует 4246 байт - режим откладки отключен
2022-04-19 Скетч использует 4080 байт ARDUINO_UNO
2022-04-20 Скетч использует 4110 байт ARDUINO_UNO
2022-04-28 Скетч использует 4072 байт Индикатор 4 разряда
2022-04-28 Скетч использует 4078 байт Индикатор 3 разряда
Архив с проектом: https://disk.yandex.ru/d/B4miN-and3bNtQ
****************************************************************/
#include <TimerOne.h> // Подключение библиотеки Таймер1
#include "TM1637_mralb2.h" // Библиотека для TM1637
#define OUT A0
#define BUZZER 9
#define BT_MODE 10
#define BT_MINUS 11
#define BT_PLUS 12
#define MAXBEEP 16 // Сколько раз бикнет буззер
#define DEF_WAIT_BUTTON 26 // Подтверждение кнопки
#define DELAY_CLICK 21 // Время клика
#define DELAY_BT 350 // Пауза после нажатия кнопки
// Включение режима отладки
// Если нет надобности, то строку ниже закоментировать
//#define DEBUG
// Определение типа используемой платы
#define ARDUINO_UNO
//#define LGT8F
// Установите логику управления кнопками в вашей схеме
// Логика управления 1 - HIGH | 0 - LOW
#define LOGICA 1
// Определение символов
#define DP 10
#define PUSTO 11
#define CLK 3
#define DIO 2
#define MAXDIGIT 4 // Сколько всего разрядов в индикаторе 3|4
TM1637 disp(CLK, DIO); // Создание объекта индикатора
; // Объявление и инициализация
// Массив символов
static int8_t symb[] =
{
0x3f, 0x06, 0x5b, // 0, 1, 2
0x4f, 0x66, 0x6d, // 3, 4, 5,
0x7d, 0x07, 0x7f, // 6, 7, 8,
0x6f, 0x80, 0x00, // 9, 10 - точка, 11 - пусто,
};
// Структура разрядов дисплея
struct Disp
{
byte high; // Разряды индикатора DID1, DIG2
byte low; // Разряды индикатора DIG3, DIG4
byte bright = 3; // Яркость 0...7
};
// Создание переменной типа Disp
Disp sdisp;
/* Глобальные переменные для времени */
uint32_t t1, dt;
// Структура переменной времени
struct Tim
{
byte hh;
byte hh_set;
byte mm;
byte mm_set;
byte ss;
byte ss_set;
};
// Создание переменной типа Tim
Tim stime;
// Структура переменной флагов
struct Flag
{
bool runTimer = false; // Флаг работы таймера
bool rele = true; // Флаг подключения реле
bool buzz = true; // Флаг активности звука
bool modeTime2 = false; // Флаг mm.ss(.)
};
// Создание переменной типа Flag
Flag sflag;
// Переменная для прерывания, мигание точек
volatile boolean flag_point = true;
byte mode = 0; // Пункт меню 0 - вывод времени
// Объявление функции сигнала Buzzer-a
// Окончание времени таймера
void fnAlarmTimer
(
byte max_beep = MAXBEEP,
unsigned int delay_beep = 500
);
// Объявление функции сканирования кнопки TTP223
boolean fnScanButton
(
byte buttonPin,
byte buttonWait = DEF_WAIT_BUTTON
);
// Объявление функции пиканья кнопки
void fnClickButton
(
unsigned int delay_ms = DELAY_CLICK
);
/***********************
Функция настройки
************************/
void setup()
{
#ifdef DEBUG
Serial.begin(115200);
#endif
#if (LOGICA == 1)
// Если логика управления кнопками 1 - HIGH
pinMode(BT_MODE, INPUT); // Выбор режима
pinMode(BT_PLUS, INPUT); // Прибавить +
pinMode(BT_MINUS, INPUT); // Убавить -
#else
// Если логика управления кнопками 0 - LOW
pinMode(BT_MODE, INPUT_PULLUP); // Выбор режима
pinMode(BT_PLUS, INPUT_PULLUP); // Прибавить +
pinMode(BT_MINUS, INPUT_PULLUP); // Убавить -
#endif
// Логика управления реле 0 - LOW
pinMode(OUT, OUTPUT); // Управляющий выход
digitalWrite(OUT, HIGH); // Отключим выход
// Логика управления звуком 0 - LOW
pinMode(BUZZER, OUTPUT); // Зуммер
digitalWrite(BUZZER, HIGH); // Отключим выход
disp.brightness(sdisp.bright);
// Для Ардуино Уно, загасить светодиод на пин13
// В других Ардуино не нужно
#ifdef ARDUINO_UNO
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
#endif
// Инициализация прерывания на 500 мс
// Для мигания точек
Timer1.initialize(500000);
// Проверка символов индикатора
// и работы звука буззера
fnDisplayTest();
//flag_point = true;
// Вывод текущего времени
fnPrintDisp(stime.hh, stime.mm);
} // End setup
/***************************
Рабочая программа
**************************/
// Статусы кнопок
bool st_bt_minus,
st_bt_plus,
st_bt_mode;
void loop()
{
// Проверка состояния кнопки BT_MODE
/*******************************************************
Всего 7 пунктов меню:
0 - Индикация текущего времени: hh.mm(.)|mm.ss(.)
1 - Режим hh.mm - 0 | режим mm.ss - 1
2 - Установка часов|минут 0...99
3 - Установка минут|секунд 0...59
4 - Активность реле 0|1 00.00. - точка DIG4
5 - Активность зуммера 0|1
6 - Яркость индикатора 0...7
При запущенном таймере меню заблокировано
*******************************************************/
// Условная компиляция. Выбор логики управления
#if (LOGICA == 1)
if (digitalRead(BT_MODE))st_bt_mode = HIGH;
#else
if (!digitalRead(BT_MODE))st_bt_mode = HIGH;
#endif
if (st_bt_mode && !sflag.runTimer)
{
st_bt_mode = LOW;
fnMenuMode(); // Меню выбора режима
}
// Текущее время работы программы в мс
t1 = millis();
if (dt > t1) dt = t1; // Защита от переполнения t1
// Автоматической возвращение из меню параметров
// к индикации времени через 10 секунд бездействия
if ((t1 - dt > 10000) && mode != 0) mode = 0;
// Вывод текущего значения на дисплей
// Выбор установленных значений на индикатор
switch (mode)
{
case 1:
// Альтернативное время
sdisp.low = sflag.modeTime2;
break;
case 2:
// Значение в разрядах DIG1, DIG2
if (sflag.modeTime2)
sdisp.low = stime.mm;
else
sdisp.low = stime.hh;
break;
case 3:
// Значение в разрядах DIG3, DIG4
if (sflag.modeTime2)
sdisp.low = stime.ss;
else
sdisp.low = stime.mm;
break;
case 4:
// Активность реле
sdisp.low = sflag.rele;
break;
case 5:
// Активность буззера
sdisp.low = sflag.buzz;
break;
case 6:
// Уровень яркости 0...7
sdisp.low = sdisp.bright;
}
fnPrintTime(mode); // Выводим текущее время
/******************** setting *********************/
// Настройка альтернативного времени mm.ss(.)
if (mode == 1)
{
sflag.modeTime2 = fnSetParamMode(sflag.modeTime2, false, true);
sdisp.low = sflag.modeTime2;
#if(MAXDIGIT==3)
if (sflag.modeTime2 && stime.mm_set > 9)stime.mm_set = 9;
#endif
sdisp.high = mode;
}
// Установка часов - hours|minits
if (mode == 2)
{
if (sflag.modeTime2)
{
#if(MAXDIGIT==4)
stime.mm_set = fnSetParamMode(stime.mm_set, 0, 99);
#elif(MAXDIGIT==3)
stime.mm_set = fnSetParamMode(stime.mm_set, 0, 9);
#endif
}
else
{
#if(MAXDIGIT==4)
stime.hh_set = fnSetParamMode(stime.hh_set, 0, 99);
#elif(MAXDIGIT==3)
stime.hh_set = fnSetParamMode(stime.hh_set, 0, 9);
#endif
}
// Установка минут - minits|seconds
if (mode == 3)
{
if (sflag.modeTime2)
stime.ss_set = fnSetParamMode(stime.ss_set, 0, 59);
else
stime.mm_set = fnSetParamMode(stime.mm_set, 0, 59);
// Установим текущее время, если таймер не запущен
if (!sflag.runTimer)
{
if (sflag.modeTime2)
{
stime.ss = stime.ss_set;
sdisp.low = stime.ss_set;
}
else
{
stime.mm = stime.mm_set;
sdisp.low = stime.mm_set;
}
}
sdisp.high = mode;
}
// Настройка активности реле - rele
if (mode == 4)
{
sflag.rele = fnSetParamMode(sflag.rele, false, true);
// Выключим выход
if (!sflag.rele)digitalWrite(OUT, HIGH);
sdisp.low = sflag.rele;
sdisp.high = mode;
}
// Настройка активности звука - buzz/click/alarm
if (mode == 5)
{
sflag.buzz = fnSetParamMode(sflag.buzz, false, true);
sdisp.low = sflag.buzz;
sdisp.high = mode;
}
// Настройка яркости индикатора - brightness
if (mode == 6)
{
sdisp.bright = fnSetParamMode(sdisp.bright, 0, 7);
disp.brightness(sdisp.bright);
sdisp.low = sdisp.bright;
sdisp.high = mode;
}
/****************** End setting ******************/
/****************** START TIMER ******************/
st_bt_plus = LOW;
st_bt_minus = LOW;
// Условная компиляция. Выбор логики управления
#if (LOGICA == 1)
if (digitalRead(BT_PLUS))st_bt_plus = HIGH;
#else
if (!digitalRead(BT_PLUS))st_bt_plus = HIGH;
#endif
// Если таймер ещё не запущен
if (!sflag.runTimer && st_bt_plus)
{ // Было нажатие кнопки BT_PLUS - старт таймера
if
(
mode == 0 &&
(
(
(stime.hh_set != 0 || stime.mm_set != 0) &&
!sflag.modeTime2
) ||
(
(stime.mm_set != 0 || stime.ss_set != 0) &&
sflag.modeTime2
)
)
)
{
delay(500);
// Запуск таймера
sflag.runTimer = true;
fnClickButton();
// Подключение прерывания
Timer1.attachInterrupt(blink_point);
// Включим выход
if (sflag.rele)digitalWrite(OUT, LOW);
stime.mm_set = stime.mm;
stime.hh_set = stime.hh;
if (sflag.modeTime2)
stime.ss_set = stime.ss;
dt = t1;
}
}
else
{
#if (LOGICA == 1)
if (digitalRead(BT_PLUS))st_bt_plus = HIGH;
if (digitalRead(BT_MINUS))st_bt_minus = HIGH;
#else
if (!digitalRead(BT_PLUS))st_bt_plus = HIGH;
if (!digitalRead(BT_MINUS))st_bt_minus = HIGH;
#endif
if
( // Если таймер запущен, то
// кнопками BT_MINUS, BT_PLUS
// остановить на паузу
sflag.runTimer == true &&
(st_bt_plus || st_bt_minus)
)
{ // Остановим таймер на ПАУЗУ
st_bt_plus = LOW;
fnStopTimer();
// Установим новые значения
stime.mm_set = stime.mm;
stime.hh_set = stime.hh;
if (sflag.modeTime2)
stime.ss_set = stime.ss;
flag_point = true;
}
}
/**************** END START TIMER ****************/
/**************** CONTROL TIMING ****************/
// Проверка пройденной минуты
if
(
(sflag.runTimer == true &&
((t1 - dt) >= 60000) &&
!sflag.modeTime2) ||
(sflag.runTimer == true &&
((t1 - dt) >= 1000) &&
sflag.modeTime2)
)
{
if (sflag.modeTime2)
{
#ifdef DEBUG
Serial.print(stime.mm);
Serial.print(":");
if (stime.ss < 10)Serial.print("0");
Serial.println(stime.ss);
#endif
if (stime.ss != 0) stime.ss--;
else
{
if (stime.mm != 0)
{
stime.ss = 59;
stime.mm--;
}
}
}
else
{
#ifdef DEBUG
Serial.print(stime.hh);
Serial.print(":");
if (stime.mm < 10)Serial.print("0");
Serial.println(stime.mm);
#endif
if (stime.mm != 0) stime.mm--;
else
{
if (stime.hh != 0)
{
stime.mm = 59;
stime.hh--;
}
}
}
// beep-beep если осталось 5 мин
if
(
(stime.hh == 0 && stime.mm == 5 && !sflag.modeTime2) ||
(stime.mm == 5 && stime.ss == 0 && sflag.modeTime2)
)
{
fnClickButton();
delay(300);
fnClickButton();
}
if
(
(stime.hh == 0 && stime.mm == 0 && !sflag.modeTime2) ||
(stime.mm == 0 && stime.ss == 0 && sflag.modeTime2)
)
{ // Завершение времени таймера
fnStopTimer(); // Стоп таймер
fnAlarmTimer(); // Сигнал окончания таймера
// Восстановим начальные установки
// для нового запуска
stime.mm = stime.mm_set;
stime.hh = stime.hh_set;
if (sflag.modeTime2)
stime.ss = stime.ss_set;
flag_point = true;
}
dt = t1; // Установка отсчёта новой минуты
}
/************* END CONTROL TIMING ***************/
// Выводим текущее время
fnPrintTime(mode);
} // End loop
/******************************************
*********** ПОДПРОГРАММЫ **************
******************************************/
/*************************
Функция мигания точек
************************/
void blink_point()
{
flag_point = !flag_point;
}
/******************************
Функция: Стоп таймер
*****************************/
void fnStopTimer()
{
sflag.runTimer = false;
if (sflag.buzz)fnClickButton();
if (sflag.rele)
digitalWrite(OUT, HIGH); // Отключим выход
Timer1.detachInterrupt(); // Остановка прерывания
delay(300);
}
/***********************************************
Функция: сигнал таймера
Включаем сигнал, если активен звук
Сигнал пульсирующий:
.5 секунды звук
.5 секунды пауза
2022-04-16 Mr.ALB v2
2022-04-20 Mr.ALB v2.2 добавлены аргументы:
max_beep - сколько раз бикнет,
delay_beep - время звука
***********************************************/
void fnAlarmTimer
(
byte max_beep = MAXBEEP,
unsigned int delay_beep = 500
)
{
long t;
// Если активен звук
if (sflag.buzz)
{
fnPrintDisp(0, 0);
for (byte j = 0; j < max_beep; j++)
{
t = millis();
// Вывод текущего времени
//fnPrintDisp(stime.hh, stime.mm);
while (millis() - t < delay_beep)
{
// Звук .5 секунды
digitalWrite(BUZZER, LOW);
// Если нажата любая кнопка
// Выход из цикла while()
#if (LOGICA == 1)
if (digitalRead(BT_MINUS))st_bt_minus = HIGH;
if (digitalRead(BT_PLUS))st_bt_plus = HIGH;
if (digitalRead(BT_MODE))st_bt_mode = HIGH;
#else
if (!digitalRead(BT_MINUS))st_bt_minus = HIGH;
if (!digitalRead(BT_PLUS))st_bt_plus = HIGH;
if (!digitalRead(BT_MODE))st_bt_mode = HIGH;
#endif
if (st_bt_minus || st_bt_plus || st_bt_mode)
{
st_bt_minus = LOW;
st_bt_plus = LOW;
st_bt_mode = LOW;
break;
}
}
t = millis();
while (millis() - t < delay_beep)
{
// Пауза .5 секунды
digitalWrite(BUZZER, HIGH);
// Если нажата любая кнопка
// Выход из цикла while()
#if (LOGICA == 1)
if (digitalRead(BT_MINUS))st_bt_minus = HIGH;
if (digitalRead(BT_PLUS))st_bt_plus = HIGH;
if (digitalRead(BT_MODE))st_bt_mode = HIGH;
#else
if (!digitalRead(BT_MINUS))st_bt_minus = HIGH;
if (!digitalRead(BT_PLUS))st_bt_plus = HIGH;
if (!digitalRead(BT_MODE))st_bt_mode = HIGH;
#endif
if (st_bt_minus || st_bt_plus || st_bt_mode)
{
st_bt_minus = LOW;
st_bt_plus = LOW;
st_bt_mode = LOW;
break;
}
}
// Если нажата любая кнопка
// Выход из цикла for()
#if (LOGICA == 1)
if (digitalRead(BT_MINUS))st_bt_minus = HIGH;
if (digitalRead(BT_PLUS))st_bt_plus = HIGH;
if (digitalRead(BT_MODE))st_bt_mode = HIGH;
#else
if (!digitalRead(BT_MINUS))st_bt_minus = HIGH;
if (!digitalRead(BT_PLUS))st_bt_plus = HIGH;
if (!digitalRead(BT_MODE))st_bt_mode = HIGH;
#endif
if (st_bt_minus || st_bt_plus || st_bt_mode)
{
st_bt_minus = LOW;
st_bt_plus = LOW;
st_bt_mode = LOW;
break;
}
}
fnClickButton();
delay(DELAY_BT);
}
} // End fnAlarmTimer
/********************************************
Вывод на индикатор в формате: 00.00(.)
2022-04-16 Mr.ALB v1.2
dig_high - число в разрядах DIG1, DIG2
dig_low - число в разрядах DIG3, DIG4
******************************************/
void fnPrintDisp(byte dig_high, byte dig_low)
{
byte dig1 = dig_high / 10,
dig2 = dig_high % 10,
dig3 = dig_low / 10,
dig4 = dig_low % 10;
byte digit2, digit4;
// Вывод точки 2-го разряда
if (flag_point)
digit2 = symb[dig2] | symb[DP];
else digit2 = symb[dig2];
// Вывод точки 4-го разряда
if (sflag.rele)
digit4 = symb[dig4] | symb[DP];
else digit4 = symb[dig4];
// Вывод на индикатор
#if (MAXDIGIT==4) // Если в индикаторе 4-е разряда
if (dig1 == 0) dig1 = PUSTO; // Гашение незначащего нуля
disp.displayByte(symb[dig1], digit2, symb[dig3], digit4);
#elif (MAXDIGIT == 3) // Если в индикаторе 3-и разряда
disp.displayByte(digit2, symb[dig3], digit4, symb[PUSTO]);
#endif
} // End fnPrintDisp
/*************************************************
Функция проверки всех сегментов
тест сегментов, поджигаем все на 2 сек
2022-01-02 Константин К.
2022-04-11 Mr.ALB переделка на вывод символов
************************************************/
void fnDisplayTest()
{
// Проверка символов индикатора
for (byte i = 0; i < sizeof(symb); i++)
{
disp.displayByte(symb[i], symb[i], symb[i], symb[i]);
delay(DELAY_BT);
}
fnClickButton(30);
}
/**************************************
Функция звука нажатия кнопки
2022-04-12 Mr.ALB
2022-04-20 Mr.ALB v2.3
delay_ms - продолжительность звука
**************************************/
void fnClickButton(unsigned int delay_ms = DELAY_CLICK)
{
if (sflag.buzz)
{
digitalWrite(BUZZER, LOW); // Звук
delay(DELAY_CLICK); // Задержка
digitalWrite(BUZZER, HIGH); // Откл. звук
}
}
/***************************************************
Функция сканирования состояния кнопки
Кнопка TTP223 (сенсорная)
Активное состояние HIGH, но
если на TTP223 замкнута перемычка А,
то активное состояние LOW
2022-04-14 Mr.ALB
but_pin - номер пина кнопки
but_wait - ожидание устойчивого состояния
***************************************************/
boolean fnScanButton(byte buttonPin, byte buttonWait = DEF_WAIT_BUTTON)
{
boolean current;
byte count = 0;
for (byte i = 0; i < buttonWait; i++)
{
// Опрос состояния кнопки
#if (LOGICA == 1)
current = digitalRead(buttonPin);
#else
current = !digitalRead(buttonPin);
#endif
if (current == LOW && count != 0) count--;
if (current == HIGH) count++;
delay(2);
}
// Возвращаем истину, если подтверждений > 0
return ((count > 0) ? true : false);
}
/**************************************************
Функция: установка параметра в mode
2022-04-16 Mr.ALB v1
*************************************************/
byte fnSetParamMode
(
byte mode_param, // Устанавливаемый параметр
byte mode_param_min, // Минимальное значение
byte mode_param_max // Максимальное значение
)
{
if (fnScanButton(BT_PLUS))
{ // Нажата кнопка "Плюс"
if (mode_param != mode_param_max) mode_param++;
fnClickButton(21);
}
if (fnScanButton(BT_MINUS))
{ // Нажата кнопка "Минус"
if (mode_param != mode_param_min) mode_param--;
fnClickButton(21);
}
delay(21);
return mode_param;
}
/*********************************************
Функция вывода времени
********************************************/
void fnPrintTime(byte mode)
{
if (mode == 0)
{ // Выводим текущее время
if (sflag.modeTime2)
fnPrintDisp(stime.mm, stime.ss);
else
fnPrintDisp(stime.hh, stime.mm);
}
else
// Иначе выводим значение параметра
fnPrintDisp(sdisp.high, sdisp.low);
}
/******************************************
Функция меню выбора режима
*****************************************/
void fnMenuMode()
{
delay(DELAY_BT);
// Было нажатие кнопку "mode"
// Переходим на следующий пункт
mode++;
fnClickButton();
if (mode > 6) mode = 0;
sdisp.high = mode;
// Установка автоматического выхода
// в режим индикации времени
if (mode != 0) dt = t1;
#ifdef DEBUG // Отладка
Serial.print("mode= ");
Serial.println(mode);
#endif
}
На данный момент универсальный таймер не имеет законченной конструкции, когда сделаю, то дополню на страницу фото конструкции. Пока идёт проверка и оптимизация как программы, так и будующей конструкции.