Загрузка данных
#include <Arduino.h>
// Контакты 74HC595
const byte DATA_PIN = 8;
const byte CLOCK_PIN = 7;
const byte LATCH_PIN = 4;
// Кнопки
const byte BTN_INC = A1;
const byte BTN_MODE = A2;
const byte BTN_START = A3;
// Активный зуммер
const byte BUZZER_PIN = 3;
const byte SEG_CA[10] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90
};
const byte DIGIT_SEL[4] = {0x01, 0x02, 0x04, 0x08};
// Состояния таймера
enum State { SETTING, RUNNING, PAUSED, FINISHED };
State currentState = SETTING;
struct Button {
bool last = HIGH;
bool state = HIGH;
unsigned long t = 0;
};
Button b1, b2, b3;
uint8_t mins = 0;
uint8_t secs = 0;
long remainTotal = 0;
unsigned long lastTick = 0;
unsigned long lastBlink = 0;
bool blinkState = true;
const unsigned long DEBOUNCE = 50;
// --- ФУНКЦИИ ---
void setBuzzer(bool on) {
digitalWrite(BUZZER_PIN, on ? HIGH : LOW);
}
bool isPressed(Button &b, byte pin) {
bool r = digitalRead(pin);
if (r != b.last) { b.t = millis(); b.last = r; }
if (millis() - b.t > DEBOUNCE) {
if (r != b.state) {
b.state = r;
if (r == LOW) return true;
}
}
return false;
}
void sendDisplay(byte segments, byte digit) {
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, segments);
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, digit);
digitalWrite(LATCH_PIN, HIGH);
}
void render(uint8_t m, uint8_t s, bool blinkActive = false, bool isMin = true) {
byte digits[4] = { (byte)(m / 10), (byte)(m % 10), (byte)(s / 10), (byte)(s % 10) };
for (int i = 0; i < 4; i++) {
// Логика мигания при настройке
if (blinkActive && !blinkState) {
if (isMin && i < 2) { sendDisplay(0xFF, DIGIT_SEL[i]); delayMicroseconds(500); continue; }
if (!isMin && i >= 2) { sendDisplay(0xFF, DIGIT_SEL[i]); delayMicroseconds(500); continue; }
}
byte seg = SEG_CA[digits[i]];
// Точка посередине (на 2-м разряде) для разделения ММ:СС
if (i == 1) seg &= 0x7F;
sendDisplay(seg, DIGIT_SEL[i]);
delayMicroseconds(800);
sendDisplay(0xFF, 0); // Очистка для предотвращения засветки
}
}
// --- ОСНОВНОЙ ЦИКЛ ---
void setup() {
pinMode(DATA_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(LATCH_PIN, OUTPUT);
pinMode(BTN_INC, INPUT_PULLUP);
pinMode(BTN_MODE, INPUT_PULLUP);
pinMode(BTN_START, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
setBuzzer(false); // Гарантируем тишину при старте
}
bool editMinutes = true;
void loop() {
// Обновление таймера мигания
if (millis() - lastBlink > 400) {
lastBlink = millis();
blinkState = !blinkState;
}
// 1. ОБРАБОТКА КНОПОК
if (isPressed(b2, BTN_MODE)) {
if (currentState == SETTING) editMinutes = !editMinutes;
}
if (isPressed(b1, BTN_INC)) {
if (currentState == SETTING || currentState == PAUSED) {
if (editMinutes) mins = (mins + 1) % 100;
else secs = (secs + 5) % 60; // Прибавляем по 5 сек для удобства
setBuzzer(true); delay(20); setBuzzer(false); // Короткий клик
}
}
if (isPressed(b3, BTN_START)) {
if (currentState == SETTING || currentState == PAUSED) {
remainTotal = (long)mins * 60 + secs;
if (remainTotal > 0) currentState = RUNNING;
}
else if (currentState == RUNNING) {
currentState = PAUSED;
}
else if (currentState == FINISHED) {
setBuzzer(false);
currentState = SETTING;
}
}
// 2. ЛОГИКА РАБОТЫ
switch (currentState) {
case SETTING:
case PAUSED:
render(mins, secs, true, editMinutes);
break;
case RUNNING:
if (millis() - lastTick >= 1000) {
lastTick = millis();
if (remainTotal > 0) {
remainTotal--;
// Писк каждую секунду последние 3 секунды
if (remainTotal < 3) { setBuzzer(true); delay(50); setBuzzer(false); }
} else {
currentState = FINISHED;
}
}
render(remainTotal / 60, remainTotal % 60);
break;
case FINISHED:
// Финальный сигнал: мигаем 00:00 и пищим
setBuzzer(blinkState);
render(0, 0, true, true);
break;
}
}