Skip to content

Latest commit

 

History

History
273 lines (220 loc) · 7.08 KB

File metadata and controls

273 lines (220 loc) · 7.08 KB

Retro-Uhr: Pro Micro + DS1302 + 4× TIL311

Diese Dokumentation ist aus deinem Sketch generiert und spiegelt die Pin-Definitionen aus deiner aktuellen clock_til311.ino wider.

Verkabelung (aus dem Code extrahiert)

  • TIL311 BCD D0..D3 (shared): A2, A3, A1, A0
  • TIL311 STROBE (0..3): 5, 6, 7, 8
  • PIN_BTN_H10
  • PIN_BTN_M16

Hinweis: LT/BL der TIL311 typ. an +5V (inaktiv), Vcc/GND wie üblich. DS1302: VBAT an CR2032, 32.768 kHz-Quarz an X1/X2, 100 nF an Vcc/GND.

Bibliotheken (arduino-cli)

arduino-cli lib update-index
arduino-cli lib install "Rtc by Makuna"
arduino-cli lib install ThreeWire

Bedienung (falls enthalten)

  • Kurz drücken: +1 Stunde bzw. +1 Minute
  • Halten: Auto-Repeat im Sekundentakt
  • Kein Übertrag zwischen Minute↔Stunde
  • Zeit wird nach jeder Änderung in die RTC geschrieben (Sekunden = 0)

Quellcode

/*
  Pro Micro (ATmega32U4 5V/16MHz)
  DS1302 RTC -> 4x TIL311 (HHMM) + 2 Taster zum Stellen

  DS1302: CLK=D2, DAT=D3, CE=D4
  TIL311 BCD: D0..D3 -> D5,D6,D7,D8  (parallel zu allen Anzeigen)
  TIL311 STROBES (links->rechts): D9, D10, A0(D18), A1(D19)
  TIL311 LT/BL: auf +5V (inaktiv)
  Taster (gegen GND, interner PullUp aktiv):
    BTN_H (Hour) -> A2 (D14)
    BTN_M (Min)  -> A3 (D15)
*/

#include <ThreeWire.h>
#include <RtcDS1302.h>

// ---------- DS1302 Pins ----------
#define PIN_DS1302_CLK   2   // SCLK
#define PIN_DS1302_DAT   3   // I/O
#define PIN_DS1302_CE    4   // CE/RST

ThreeWire myWire(PIN_DS1302_DAT, PIN_DS1302_CLK, PIN_DS1302_CE); // IO, SCLK, CE
RtcDS1302<ThreeWire> Rtc(myWire);

// ---------- TIL311 Pins ----------
const uint8_t BCD_PINS[4]    = {A2, A3 ,A1 ,A0};           // D0..D3
const uint8_t STROBE_PINS[4] = {5 ,6 ,7 ,8};        // 4 Stellen links->rechts


// ---------- Buttons ----------
const uint8_t PIN_BTN_H = 10; // D14
const uint8_t PIN_BTN_M = 16; // D15

// Button-Parameter
const unsigned long DEBOUNCE_MS   = 20;
const unsigned long REPEAT_FIRST  = 1000; // nach erstem Inkrement 1 s warten
const unsigned long REPEAT_RATE   = 200; // im Sekundentakt wiederholen

// internes Button-Tracking (active LOW)
struct BtnState {
  uint8_t  pin;
  bool     stableLevel;      // zuletzt stabiler Pegel (true=HIGH=unpressed, false=LOW=pressed)
  bool     lastRead;         // roher letzter Pegel
  unsigned long lastChangeMs;
  bool     pressed;          // logisch gedrückt (debounced)
  bool     didInitialStep;   // hat initial bereits einmal inkrementiert?
  unsigned long lastRepeatMs;// Zeitpunkt letztes Auto-Inkrement
};

BtnState btnH { PIN_BTN_H, true, true, 0, false, false, 0 };
BtnState btnM { PIN_BTN_M, true, true, 0, false, false, 0 };

// Option: einmalig Uhr auf Kompilierzeit setzen
bool SET_TIME_FROM_COMPILE = false;

// Zeitpuffer
uint8_t curH = 0, curM = 0, curS = 0;

// ---- Hilfsfunktionen ----
RtcDateTime compileTime()
{
  return RtcDateTime(__DATE__, __TIME__);
}

void til311_writeDigit(uint8_t digit, uint8_t pos)
{
  if (digit > 9) digit = 0; // nur 0..9 anzeigen

  // BCD setzen (D0..D3)
  for (uint8_t i = 0; i < 4; i++)
    digitalWrite(BCD_PINS[i], (digit >> i) & 0x01);

  // kurzer Strobe-Impuls für die gewählte Stelle
  digitalWrite(STROBE_PINS[pos], LOW);
  delayMicroseconds(5);           // 2..10 µs reichen
  digitalWrite(STROBE_PINS[pos],HIGH);
}

void til311_showTime(uint8_t hh, uint8_t mm)
{
  uint8_t d0 = (hh / 10) % 10;
  uint8_t d1 = hh % 10;
  uint8_t d2 = (mm / 10) % 10;
  uint8_t d3 = mm % 10;

  til311_writeDigit(d0, 0);
  til311_writeDigit(d1, 1);
  til311_writeDigit(d2, 2);
  til311_writeDigit(d3, 3);
}

void rtcWrite(uint8_t hh, uint8_t mm, uint8_t ss)
{
  // Sekunden auf 0 setzen, wenn wir stellen (ss-Param ignorieren)
  RtcDateTime dt = RtcDateTime(2024, 1, 1, hh, mm, 0); // Datum dummy (DS1302 braucht eines)
  // Besser: aktuelles Datum holen, nur Zeit ersetzen:
  RtcDateTime now = Rtc.GetDateTime();
  RtcDateTime dt2(now.Year(), now.Month(), now.Day(), hh, mm, 0);
  Rtc.SetDateTime(dt2);
}

void rtcReadToCache()
{
  RtcDateTime now = Rtc.GetDateTime();
  curH = now.Hour();
  curM = now.Minute();
  curS = now.Second();
}

void updateDisplay()
{
  til311_showTime(curH, curM);
}

// Debounce + Auto-Repeat-Handling (active LOW)
void handleButton(BtnState &b, void (*onStep)())
{
  bool raw = digitalRead(b.pin); // HIGH = nicht gedrückt (PullUp), LOW = gedrückt
  unsigned long now = millis();

  if (raw != b.lastRead) {
    b.lastRead = raw;
    b.lastChangeMs = now; // Flanke erkannt, Debounce-Fenster starten
  }

  // stabil nach Debounce?
  if ((now - b.lastChangeMs) >= DEBOUNCE_MS) {
    if (raw != b.stableLevel) {
      // stabiler Zustand hat gewechselt
      b.stableLevel = raw;

      if (b.stableLevel == LOW) {
        // jetzt gedrückt -> sofort 1x inkrementieren
        b.pressed = true;
        b.didInitialStep = false;
        b.lastRepeatMs = now;

        onStep();               // sofortiger Schritt
        b.didInitialStep = true;
        b.lastRepeatMs = now;   // Zeitstempel für Repeat
      } else {
        // jetzt losgelassen
        b.pressed = false;
        b.didInitialStep = false;
      }
    }
  }

  // Auto-Repeat im Sekundentakt solange gedrückt
  if (b.pressed) {
    unsigned long wait = b.didInitialStep ? REPEAT_RATE : REPEAT_FIRST;
    if (now - b.lastRepeatMs >= wait) {
      onStep();
      b.lastRepeatMs = now;
      b.didInitialStep = true;
    }
  }
}

// Schritt-Funktionen: ohne Übertrag zwischen Minute/Hour
void stepHour()
{
  curH = (curH + 1) % 24; // 0..23
  rtcWrite(curH, curM, 0);
  updateDisplay();
}

void stepMinute()
{
  curM = (curM + 1) % 60; // 0..59 (kein Übertrag auf Stunde)
  rtcWrite(curH, curM, 0);
  updateDisplay();
}

void setup()
{
  // TIL311 Pins
  for (uint8_t i = 0; i < 4; i++) {
    pinMode(BCD_PINS[i], OUTPUT);
    digitalWrite(BCD_PINS[i], LOW);
    pinMode(STROBE_PINS[i], OUTPUT);
    digitalWrite(STROBE_PINS[i], LOW);
  }

  // optionale "Herzschlag"-LED (TX-LED invers)
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH); // aus

  // Buttons
  pinMode(PIN_BTN_H, INPUT_PULLUP);
  pinMode(PIN_BTN_M, INPUT_PULLUP);

  // RTC init
  Rtc.Begin();
  Rtc.SetIsWriteProtected(false);
  Rtc.SetIsRunning(true);

  if (SET_TIME_FROM_COMPILE) {
    Rtc.SetDateTime(compileTime());
  }

  if (!Rtc.IsDateTimeValid()) {
    Rtc.SetDateTime(compileTime());
  }

  // Startanzeige
  rtcReadToCache();
  updateDisplay();
}

unsigned long lastSecCheck = 0;
uint8_t lastShownSec = 255;
bool ledState = false;

void loop()
{
  // Buttons bearbeiten (inkl. Debounce & Repeat)
  handleButton(btnH, stepHour);
  handleButton(btnM, stepMinute);

  // 1x pro Sekunde aus RTC nachziehen, falls keine Tastenaktivität
  unsigned long nowMs = millis();
  if (nowMs - lastSecCheck >= 50) { // kurze Periode
    lastSecCheck = nowMs;

    RtcDateTime now = Rtc.GetDateTime();
    if (now.Second() != lastShownSec && !btnH.pressed && !btnM.pressed) {
      lastShownSec = now.Second();
      curH = now.Hour();
      curM = now.Minute();
      curS = now.Second();
      updateDisplay();

      // Herzschlag (TX-LED invertiert)
      ledState = !ledState;
      digitalWrite(LED_BUILTIN, ledState ? LOW : HIGH);
    }
  }
}