7 Segement Uhr 100mm Bauhöhe mit Attiny84

Mein Projektvorstellung:
Eine 4 Ziffern 7Segementuhr mit 100mm Bauhöhe der Segemente.
Benötigte Hardware:
1* Attiny84
1* DS1307 RTC von Maxim
1* Batteriehalter
4* 74HC595 Schieberegister
4* ULN2803 Darlington Array
1* IRFL024 N-Channel Mosfet
1* 7805CV Festspannungregler 5V
1* Batteriehalter CR20xx
1* Ribbon IKEA Bilderrahmen
Möbefolie weiß zu Beziehen des Glases.

Ziel war es alles auf einen Attiny84(44) CPU umzusetzten. Die Dateien der Cores sind von hier http://hlt.media.mit.edu/?p=1695 Diese laufen aber nur unter der 1.0.1 auf der 1.0.2 habe ich es durch einiges an probieren nicht hinbekommen.
Ich nutzte die TinyWireM-Libraries mit Erweiterungen von mir der defines für die x4er Serie, da die USI Interface auf anderen Pins bzw. Ports sitzt als bei den x5ern. Die Kommuniktion zu RTC läuft über I2C, wobei die externen Pullups zwingend erforderlich sind.
Ich wollte kein Multiplexing nutzten damit ich die volle Helligkeit der Anzeigen nutzten kann, deswegen die 4 Schieberegister+ 4 ULN2803.
Mit dem IRFD024 Mosfet habe ich die Output-Enable der Schiebregister mit PWM angesteuert um die Helligkeit der Segemente zu handlen.
Die Steuerung läuft via One-Button Steuerung.
Kurzer Druck im Normalmodus stellt die Helligkeit in 6 Stufen regeln. Ein Langer Druck bewirkt die beiden Setmodes (1= Stellen Minuten 2= Stellen Stunden).
Gruß
Der Dani

Hier der Code

/*
Attiny84 Big 7 Segment Uhr mit 74HC595
 
 */


#include <TinyWireM.h>
#include <USI_TWI_Master.h>


#define Shiftdata  8          // Pin 8 Datenleiung Schieberegister connected to Data in (DS) of 74HC595
#define Shiftclock 10           // Pin 9 Clockleitung connected to clock pin (SH_CP) of 74HC595
#define Shiftlatch 9         // Pin 10 Latchleitung connected to latch pin (ST_CP) of 74HC595
#define PWMOUT 7
#define BUTTON 1
#define RTCPuls 2
#define LDR 3
#define RTC_ADDRESS 0x68
#define TIME 750

byte ie,iz,hz,he,mz,me=0;  // Modulespeicher der stunden Minuten
boolean ldruck,druck=false;   // Stellknopf für Zeit 
boolean longpress,shortpress=false;  // Unterscheidung langer kurzer druck
boolean blinktakt=false;             // Blinktakt 500ms
boolean lpuls,puls=false;            // Sekundenpuls
boolean hell=false;
unsigned long lmillis,start,ptime=0;
byte ss=0;
byte mm=0;
byte hh=0;
byte mEiner,mZehner,hEiner,hZehner=0;
byte setmode=0;
byte PWMArray[7]={
  10,20,40,80,160,255};
byte Pointer=4;


void setup(){
  pinMode(Shiftdata,OUTPUT);
  pinMode(Shiftclock,OUTPUT);
  pinMode(Shiftlatch,OUTPUT);
  pinMode(BUTTON,INPUT_PULLUP);
  pinMode(RTCPuls,INPUT);
  pinMode(PWMOUT,OUTPUT);
  TinyWireM.begin();                                  // Start I2C Kommunikation
  TinyWireM.beginTransmission(0x68);                  // Beginn Kommunikation auf  Adresse 0x68 
  TinyWireM.send(0x07);                               // Pointer auf Control Register 0x07   
  TinyWireM.send(0x10);                               // Steuerwort Ex "0x30" Bin110000 Ausgang RTC Ausgang auf 1Hz Logic normal Levels setzn
  TinyWireM.endTransmission();                        // Beenden der I2C Kommunitkation
  HourRead();                                         // RTC Funktion lesen der aktuellen zeit Stunden
  MinuteRead();                                       // RTC Funktion lesen der aktuellen zeit Minuten
  RTC_Start();                                        // Start der Uhr
}


void loop(){
  /*=>Routine OneButton Control<=*/
  druck=digitalRead(BUTTON);            // Einlesen des Button (Achtung invertierte Logik da intrener Pull Up Widerstand aktiviert)
  if (druck==false&&ldruck==true){      // Steigende Flanke des Button
    start=millis();                     // Startzeit des Bettätigen
    ldruck=false;                       // Hilfsflag zur Flanken erkennung
  }
  if (druck==true&&ldruck==false){      // Falende Flanke des Buttons
    ptime=millis()-start;               // Die Länge in ms des Gedückten Zustandes
    ldruck=true;
  }
  if (ptime>=TIME){                    // Wenn der Druck länger (Hier750ms) 
    longpress=true;                    // War ein Langer druck
    ptime=0;                           // Zeit wieder Zurücksetzten
  }
  if (ptime<TIME&&ptime>=50) {         // Wenn der Druck kürzer 750ms und länger 50ms (gegen prellen)
    shortpress=true;                   // War ein kurzer Durck
    ptime=0;                           // Zeit wieder zurücksetzuten
  }
  /*One Button End*/

  puls=digitalRead(RTCPuls);                 // Einlessen des RTC Sekunden Puls
  if (puls==true&&lpuls==false){             // wenn Sekundenpuls da und vorher nicht da war (steigende Flanke)
    lpuls=true;
    ss++;                                    // intern Sekunden zählen
  }
  if (ss>=60&&setmode==0){                   // Nach 60 Sekunden
    HourRead();                              // RTC Funktion lesen der aktuellen zeit Stunden
    MinuteRead();                            // RTC Funktion lesen der aktuellen zeit Minuten
    ss=0;                                    // Rücksetzzeten internen Sekunden Zähler auf 0
  }
  if (puls==false){                          // RTC Sekunden Hilfspuls
    lpuls=false;
  }
  if (shortpress==true){                    // Routine Kurzes drücken
    if (setmode==0){                        // In Normalmodus 
      Pointer++;                            // ändern des Pointer der Helligkeit
      if (Pointer>=6){                      // Bei Pointer 6 und größer auf Pointer Null
        Pointer=0;
      }
    }
    if (setmode==1){                        // Minuten stellen
      mm++;
      if (mm>=60){
        mm=0;
      }
    }
    if (setmode==2){                        // Stunden stellen
      hh++;
      if(hh>=24){
        hh=0;
      }
    }
    shortpress=false;
  }
  if (longpress==true){                      // Routine Langes drücken
    setmode++;                               // Stellmodus erhöhen
    if (setmode==3){                         // Bei erreichen des Modus 3
      RTC_Set();                             // Neue Uhrzeit übernehmen in die RTC
      RTC_Start();                           // RTC Starten
      setmode=0;                             // Rücksetzten des Setmodes auf 0 (Normale Anzeige)
    }
    longpress=false;                         // Rücksetzten des Flags Langes Drücken
  }
  if (millis()-lmillis>=500){                // Aktalisiernung alle halbe Sekunden
    blinktakt= !blinktakt;                   // Blinktakt der Stellen anzeige
    mEiner=mm%10;                            // Einer Minuten
    mZehner=(mm-mEiner)/10;                  // Zehner Minuten
    hEiner=hh%10;                            // Einer Stunden
    hZehner=(hh-hEiner)/10;                  // Zehner Stunden
    hz=Dec2Shift(hZehner);                   // Wandeln der Zahlen in 7 Segment Ansteuerbytes
    he=Dec2Shift(hEiner);                    // dto.
    mz=Dec2Shift(mZehner);                   // dto.
    me=Dec2Shift(mEiner);                    // dto
    if (blinktakt==false){                  // Blinkroutine des Stellmodus
      if(setmode==1){                       // Minuten stellen
        me=0;
        mz=0;
      }
      if (setmode==2){                      // Stunden Stellen
        he=0;
        hz=0;
      }
    }
    digitalWrite(Shiftlatch, LOW);     // Latch aussetzten um in den Storage speicher zu schreiben 
    shiftOut(Shiftdata, Shiftclock, MSBFIRST,  me);                    // Shift out der einzelenen Puffer wobei im letzten
    shiftOut(Shiftdata, Shiftclock, MSBFIRST,  mz);                    // Shift out der einzelenen Puffer wobei im letzten
    shiftOut(Shiftdata, Shiftclock, MSBFIRST,  he);                    // Shift out der einzelenen Puffer wobei im letzten
    shiftOut(Shiftdata, Shiftclock, MSBFIRST,  hz);                    // Shift out der einzelenen Puffer wobei im letzten
    digitalWrite(Shiftlatch, HIGH);                                        // Aktiverien des Latches (Schieben des Shift ind Storageregister) zum Anzeigen
    lmillis=millis();  
  }
  if (hh>=22&&hh<=8&&hell==true){                         // wenn Später 22Uhr und Früher als 8Uhr und es vorher Hell war dann dunkelschlaten
    Pointer=1;
    hell=false;
  }
  if (hh<22&&hh>8&&hell==false){                       // Wenn vor 22Uhr und nach 8 Uhr und vorher dunkel war dann Hell Schalten
    Pointer=5;
    hell=true;
  }
  analogWrite(PWMOUT,PWMArray[Pointer]);
}

byte Dec2Shift(byte x){
  byte shiftdata[11]={
    63,6,91,79,102,109,125,7,127,111};               // Daten für die 7 Segement anzeigen entspircht 0,1,2,3,4,5,6,7,8,9
  return shiftdata[x];
}

void HourRead(){
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x02);               // Pointer auf Sekunden
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(RTC_ADDRESS, 1);
  hh= bcdToDec(TinyWireM.receive());
}

void MinuteRead(){
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x01);               // Pointer auf Sekunden
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(RTC_ADDRESS, 1);
  mm= bcdToDec(TinyWireM.receive());
}

void RTC_Start(){
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x00);
  TinyWireM.send(decToBcd(ss));    // 0 to bit 7 starts the clock
  TinyWireM.endTransmission();
}


void RTC_Set(){
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x01);
  TinyWireM.send(decToBcd(mm));
  TinyWireM.send(decToBcd(hh));      // If you want 12 hour am/pm you need to set
  TinyWireM.endTransmission();
}

byte decToBcd(byte val)
{
  return ((val/10)<<4)+(val%10);
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ((val>>4)*10)+val%16;
}

Kompiliert sind das 2980 bytes also passt auch auf einen Attiny44
Gruß
Der Dani

Hallo Dani,ich möchte die Uhr nachbauen.Kannst du bitte einen besseren Plan auch für den Attiny erstellen.Kann auch mt der Hand sein.Was ist mit dem LDR ?

Gruß Martin

Ich habe mich mittlerweile dazu entschlossen den LDR nicht zu implementieren habe es "fest" über die zeiten gemacht da die Uhr im Schlafzimmer hängt. es wird jetzt immer die Helligkeit um 10Uhr von Dunkel auf Hell gesellt und um 22Uhr von Hell auf Dunkel.
Den Plan kann ich nochmal verfeinern.
Wenn du willst kannst du 4 von LED Segmenten haben habe noch welche über 100mm Grün Kingbright SA40-11SGWB (5€stck * Porto)
Falls intresse besteht kurz per PM. Helfe gerne aus.
Gruß
DerDani

Du schreibst nicht mit welcher Spannung die Displays betrieben werden. Da ist aufzupassen da größere Displays häufig mehrere LED pro Segment in Serie geschaltet haben und darum nicht mit 5V betrieben werden können.
Deshalb die 24V Spannungsversorgung?
Der LM7805 schafft zwar 24V Eingangsspannung aber ein DC/DC Wandler wäre besser.

Grüße Uwe

Also die Uhr arbeitet mit mindestens 22V Eingangsspannung.
Ich nutzte 12 Volt die ich mit sowas >klick hier< auf 22V Hochwandel.
Die 5V sind nur für den Attiny und die Schieberegister. der Strombedarf gemessen liegt zwischen 6-10mA gesamt. Da ich keine zwei Wandler nutzten wollte habe ich einen "großen" LM7805 genommen.
P=U*I (22V-5V)*0.01A=0,17W die verkraftet ein LM7805 locker auch ohne Kühlkörper.
Schaltplan folgt....
Gruß
DerDani

Ich weiß es ist mein alte Thread aber ich habe die Uhr nochmal angepackt da wir ein zeitweise Baby im Schlafzimmer haben und Tagsüber die Uhr zu hell war.
Sie hat jetzt mittlerweile doch ein LDR. Hier die angepackt Code dazu.

/*
Attiny84 Big 7 Segment Uhr mit 74HC595

 */


#include <TinyWireM.h>
#include <USI_TWI_Master.h>


#define Shiftdata  8          // Pin 8 Datenleiung Schieberegister connected to Data in (DS) of 74HC595
#define Shiftclock 10           // Pin 9 Clockleitung connected to clock pin (SH_CP) of 74HC595
#define Shiftlatch 9         // Pin 10 Latchleitung connected to latch pin (ST_CP) of 74HC595
#define PWMOUT 7
#define BUTTON 1
#define RTCPuls 2
#define LDRin A0
#define RTC_ADDRESS 0x68
#define TIME 750

byte ie, iz, hz, he, mz, me = 0; // Modulespeicher der stunden Minuten
boolean ldruck, druck = false; // Stellknopf für Zeit
boolean longpress, shortpress = false; // Unterscheidung langer kurzer druck
boolean blinktakt = false;           // Blinktakt 500ms
boolean lpuls, puls = false;         // Sekundenpuls
boolean hell = false;
unsigned long lmillis, start, ptime = 0;
byte ss = 0;
byte mm = 0;
byte hh = 0;
byte mEiner, mZehner, hEiner, hZehner = 0;
byte setmode = 0;
int LDRwert = 0;
byte PWMwert = 0;

void setup() {
  pinMode(Shiftdata, OUTPUT);
  pinMode(Shiftclock, OUTPUT);
  pinMode(Shiftlatch, OUTPUT);
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(RTCPuls, INPUT);
  pinMode(PWMOUT, OUTPUT);
  pinMode(LDRin, INPUT);
  TinyWireM.begin();                                  // Start I2C Kommunikation
  TinyWireM.beginTransmission(0x68);                  // Beginn Kommunikation auf  Adresse 0x68
  TinyWireM.send(0x07);                               // Pointer auf Control Register 0x07
  TinyWireM.send(0x10);                               // Steuerwort Ex "0x30" Bin110000 Ausgang RTC Ausgang auf 1Hz Logic normal Levels setzn
  TinyWireM.endTransmission();                        // Beenden der I2C Kommunitkation
  HourRead();                                         // RTC Funktion lesen der aktuellen zeit Stunden
  MinuteRead();                                       // RTC Funktion lesen der aktuellen zeit Minuten
  RTC_Start();                                        // Start der Uhr
}


void loop() {
  /*=>Routine OneButton Control<=*/
  druck = digitalRead(BUTTON);          // Einlesen des Button (Achtung invertierte Logik da intrener Pull Up Widerstand aktiviert)
  if (druck == false && ldruck == true) { // Steigende Flanke des Button
    start = millis();                   // Startzeit des Bettätigen
    ldruck = false;                     // Hilfsflag zur Flanken erkennung
  }
  if (druck == true && ldruck == false) { // Falende Flanke des Buttons
    ptime = millis() - start;           // Die Länge in ms des Gedückten Zustandes
    ldruck = true;
  }
  if (ptime >= TIME) {                 // Wenn der Druck länger (Hier750ms)
    longpress = true;                  // War ein Langer druck
    ptime = 0;                         // Zeit wieder Zurücksetzten
  }
  if (ptime < TIME && ptime >= 50) {   // Wenn der Druck kürzer 750ms und länger 50ms (gegen prellen)
    shortpress = true;                 // War ein kurzer Durck
    ptime = 0;                         // Zeit wieder zurücksetzuten
  }
  /*One Button End*/

  puls = digitalRead(RTCPuls);               // Einlessen des RTC Sekunden Puls
  if (puls == true && lpuls == false) {      // wenn Sekundenpuls da und vorher nicht da war (steigende Flanke)
    lpuls = true;
    ss++;                                    // intern Sekunden zählen
  }
  if (ss >= 60 && setmode == 0) {            // Nach 60 Sekunden
    HourRead();                              // RTC Funktion lesen der aktuellen zeit Stunden
    MinuteRead();                            // RTC Funktion lesen der aktuellen zeit Minuten
    ss = 0;                                  // Rücksetzzeten internen Sekunden Zähler auf 0
  }
  if (puls == false) {                       // RTC Sekunden Hilfspuls
    lpuls = false;
  }
  if (shortpress == true) {                 // Routine Kurzes drücken
    if (setmode == 1) {                     // Minuten stellen
      mm++;
      if (mm >= 60) {
        mm = 0;
      }
    }
    if (setmode == 2) {                     // Stunden stellen
      hh++;
      if (hh >= 24) {
        hh = 0;
      }
    }
    shortpress = false;
  }
  if (longpress == true) {                   // Routine Langes drücken
    setmode++;                               // Stellmodus erhöhen
    if (setmode == 3) {                      // Bei erreichen des Modus 3
      RTC_Set();                             // Neue Uhrzeit übernehmen in die RTC
      RTC_Start();                           // RTC Starten
      setmode = 0;                           // Rücksetzten des Setmodes auf 0 (Normale Anzeige)
    }
    longpress = false;                       // Rücksetzten des Flags Langes Drücken
  }
  if (millis() - lmillis >= 500) {           // Aktalisiernung alle halbe Sekunden
    blinktakt = !blinktakt;                  // Blinktakt der Stellen anzeige
    mEiner = mm % 10;                        // Einer Minuten
    mZehner = (mm - mEiner) / 10;            // Zehner Minuten
    hEiner = hh % 10;                        // Einer Stunden
    hZehner = (hh - hEiner) / 10;            // Zehner Stunden
    hz = Dec2Shift(hZehner);                 // Wandeln der Zahlen in 7 Segment Ansteuerbytes
    he = Dec2Shift(hEiner);                  // dto.
    mz = Dec2Shift(mZehner);                 // dto.
    me = Dec2Shift(mEiner);                  // dto
    if (blinktakt == false) {               // Blinkroutine des Stellmodus
      if (setmode == 1) {                   // Minuten stellen
        me = 0;
        mz = 0;
      }
      if (setmode == 2) {                   // Stunden Stellen
        he = 0;
        hz = 0;
      }
    }
    digitalWrite(Shiftlatch, LOW);										// Latch aussetzten um in den Storage speicher zu schreiben
    shiftOut(Shiftdata, Shiftclock, MSBFIRST,  me);						// Shift out der einzelenen Puffer wobei im letzten
    shiftOut(Shiftdata, Shiftclock, MSBFIRST,  mz);						// Shift out der einzelenen Puffer wobei im letzten
    shiftOut(Shiftdata, Shiftclock, MSBFIRST,  he);						// Shift out der einzelenen Puffer wobei im letzten
    shiftOut(Shiftdata, Shiftclock, MSBFIRST,  hz);						// Shift out der einzelenen Puffer wobei im letzten
    digitalWrite(Shiftlatch, HIGH);										// Aktiverien des Latches (Schieben des Shift ind Storageregister) zum Anzeigen
    lmillis = millis();
  }
  LDRwert = analogRead(LDRin);
  LDRwert = constrain(LDRwert, 50, 500);
  PWMwert = map(LDRwert, 50, 500, 1, 255);
  analogWrite(PWMOUT, PWMwert);
}

byte Dec2Shift(byte x) {
  byte shiftdata[11] = {
    63, 6, 91, 79, 102, 109, 125, 7, 127, 111
  };
  return shiftdata[x];
}

void HourRead() {
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x02);               // Pointer auf Sekunden
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(RTC_ADDRESS, 1);
  hh = bcdToDec(TinyWireM.receive());
}

void MinuteRead() {
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x01);               // Pointer auf Sekunden
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(RTC_ADDRESS, 1);
  mm = bcdToDec(TinyWireM.receive());
}

void RTC_Start() {
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x00);
  TinyWireM.send(decToBcd(ss));    // 0 to bit 7 starts the clock
  TinyWireM.endTransmission();
}


void RTC_Set() {
  TinyWireM.beginTransmission(RTC_ADDRESS);
  TinyWireM.send(0x01);
  TinyWireM.send(decToBcd(mm));
  TinyWireM.send(decToBcd(hh));      // If you want 12 hour am/pm you need to set
  TinyWireM.endTransmission();
}

byte decToBcd(byte val)
{
  return ((val / 10) << 4) + (val % 10);
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ((val >> 4) * 10) + val % 16;
}

Wie sieht es mit der Ganggenauigkeit der DS1307 im Langzeittest aus?

Die Uhr beeilt sich in 4 Wochen nicht ganz eine Minute sie ist noch eine der genaueren mit dem RTC1307. Es gib zwei Verschiedene Kapazitäten bei den 32kHz Quarzen die mit der geringeren lauft genauer. Die Wordclock V1 mit der gleichen RTC und dem anderen Quart läuft in 4 Wochen ce 2.5 min vor. Meine Nixie clock hat auch das quarz der Segment Clock und läuft auch so ca 1 min/4wochen vor. Wordclcok V2 mit 3231 RTC ist es bei den Minutenanzeigen in 4 Monaten noch nicht sichtbar.
Gruß
DerDani

Es gib zwei Verschiedene Kapazitäten bei den 32kHz Quarzen die mit der geringeren lauft genauer.

Da ist nicht der Quarz schuld, daß die mit kleinerer Kapazität genauer sind, sondern der Oszillator im DS1307 ist für einen optimiert, bei anderen geht er mehr falsch.

@volvodani
Was ist das für eine Möbefolie?
Zu was brauchst Du den MOSFET? Auf dem Schaltplan ist er nicht vorhanden.

Grüße Uwe

@uwefed
Diese Möbelfolie gab es irgendwann mal in weiß beim Lil oder Ali, ist also nix besoderes.
Leider ist diese Uhr mal runtergefallen und hatte die Frontscheibe kaputt (mehr als dieser eine Sprung auf dem Bild). Diese habe ich dann gegen eine Acrylglasheibe milchig mit 30% Durchlass ausgetauscht.

Der Mosfet hat alle !OE (OutputEnable !nicht) der Schieberegister damit bekomme ich die Uhr dann gedimmt.
Die Taktfrequenz von ca 1kHz konnte man gelegentlich hören bei so ca 50-60% Ontime der PWM. Dies war auf einen zu kleinen Ausgangskondenator am Schaltregler zurückzuführen, zusätzliche 1000µF haben abhilfe geschaffen.

Mir ist schon klar das der Quarz nicht Schuld ist sondern nur indirekt.

Gruß
DerDani