Diese Dokumentation ist aus deinem Sketch generiert und spiegelt die Pin-Definitionen aus deiner aktuellen clock_til311.ino wider.
- TIL311 BCD D0..D3 (shared):
A2, A3, A1, A0 - TIL311 STROBE (0..3):
5, 6, 7, 8 - PIN_BTN_H →
10 - PIN_BTN_M →
16
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.
arduino-cli lib update-index
arduino-cli lib install "Rtc by Makuna"
arduino-cli lib install ThreeWire- 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)
/*
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);
}
}
}