Загрузка данных


#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;
  }
}