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


#include <Arduino.h>

// 74HC595 на multifunction shield
const byte DATA_PIN  = 8;   // SDI
const byte CLOCK_PIN = 7;   // SFTCLK
const byte LATCH_PIN = 4;   // LCHCLK

// Кнопки на shield
const byte BTN_INC   = A1;   // +1
const byte BTN_MODE  = A2;   // минуты / секунды
const byte BTN_START = A3;   // старт

// Таблица сегментов для common anode
// Если индикатор у тебя common cathode — скажи, заменю таблицу
const byte SEG_CA[10] = {
  0xC0, // 0
  0xF9, // 1
  0xA4, // 2
  0xB0, // 3
  0x99, // 4
  0x92, // 5
  0x82, // 6
  0xF8, // 7
  0x80, // 8
  0x90  // 9
};

// Выбор разрядов
const byte DIGIT_SEL[4] = {
  0x01,
  0x02,
  0x04,
  0x08
};

const unsigned long DEBOUNCE_MS = 40;
const unsigned long TICK_MS = 1000;
const unsigned long BLINK_MS = 500;

struct ButtonState {
  bool lastRead = HIGH;
  bool stable = HIGH;
  unsigned long lastChange = 0;
};

ButtonState incBtn, modeBtn, startBtn;

enum TimerMode {
  SETUP,
  RUNNING,
  DONE
};

TimerMode mode = SETUP;

// Редактируемое время
uint8_t setMinutes = 0;
uint8_t setSeconds = 0;

// Текущее время таймера
uint16_t remainingSeconds = 0;

// 0 = минуты, 1 = секунды
uint8_t editField = 0;

unsigned long lastTick = 0;
unsigned long lastBlink = 0;
bool blinkOn = true;

byte digits[4] = {0, 0, 0, 0};

void send595(byte firstByte, byte secondByte) {
  digitalWrite(LATCH_PIN, LOW);
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, firstByte);
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, secondByte);
  digitalWrite(LATCH_PIN, HIGH);
}

bool buttonPressed(ButtonState &btn, byte pin) {
  bool reading = digitalRead(pin);

  if (reading != btn.lastRead) {
    btn.lastChange = millis();
    btn.lastRead = reading;
  }

  if (millis() - btn.lastChange > DEBOUNCE_MS) {
    if (reading != btn.stable) {
      btn.stable = reading;
      if (btn.stable == LOW) return true; // нажатие
    }
  }

  return false;
}

void splitToDigits(uint16_t n) {
  // MMSS
  digits[3] = n % 10;
  n /= 10;
  digits[2] = n % 10;
  n /= 10;
  digits[1] = n % 10;
  n /= 10;
  digits[0] = n % 10;
}

void refreshDisplay(uint16_t value, bool blank = false) {
  if (!blank) {
    splitToDigits(value);
  }

  for (byte pos = 0; pos < 4; pos++) {
    if (blank) {
      send595(0xFF, 0x00);   // всё погасить
    } else {
      byte seg = SEG_CA[digits[pos]];
      byte dig = DIGIT_SEL[pos];
      send595(seg, dig);
    }
    delayMicroseconds(800);
  }
}

uint16_t presetToSeconds() {
  return (uint16_t)setMinutes * 60U + (uint16_t)setSeconds;
}

void startTimer() {
  remainingSeconds = presetToSeconds();
  if (remainingSeconds == 0) return;

  mode = RUNNING;
  lastTick = millis();
}

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);

  digitalWrite(LATCH_PIN, HIGH);
}

void loop() {
  // Кнопки
  if (buttonPressed(incBtn, BTN_INC)) {
    if (mode != RUNNING) {
      mode = SETUP;   // если были в DONE, возвращаемся в настройку

      if (editField == 0) {
        setMinutes++;
        if (setMinutes > 99) setMinutes = 0;
      } else {
        setSeconds++;
        if (setSeconds > 59) setSeconds = 0;
      }
    }
  }

  if (buttonPressed(modeBtn, BTN_MODE)) {
    if (mode != RUNNING) {
      mode = SETUP;
      editField ^= 1;   // переключение минут / секунд
    }
  }

  if (buttonPressed(startBtn, BTN_START)) {
    if (mode == RUNNING) {
      // ничего не делаем — таймер без паузы
    } else {
      startTimer();
    }
  }

  // Работа таймера
  if (mode == RUNNING) {
    unsigned long now = millis();

    if (now - lastTick >= TICK_MS) {
      lastTick += TICK_MS;

      if (remainingSeconds > 0) {
        remainingSeconds--;
      }

      if (remainingSeconds == 0) {
        mode = DONE;
        lastBlink = now;
        blinkOn = true;
      }
    }
  }

  // Мигающие нули после окончания
  if (mode == DONE) {
    unsigned long now = millis();
    if (now - lastBlink >= BLINK_MS) {
      lastBlink = now;
      blinkOn = !blinkOn;
    }
    refreshDisplay(0, !blinkOn);
    return;
  }

  // Обычный вывод времени
  if (mode == RUNNING) {
    refreshDisplay(remainingSeconds);
  } else {
    refreshDisplay(presetToSeconds());
  }
}