Σε αυτό το άρθρο θα σας δείξω πως θα κατασκευάσετε ένα ψηφιακό ρολόι με μεγάλα 2,3 ίντσες κόκκινα ψηφία, βασισμένο στο ολοκληρωμένο κύκλωμα DS1302. Το ρολόι είναι από τις πιο συνηθισμένες συσκευές, που μπορούμε να βρούμε παντού, στο σπίτι ή στο γραφείο μας. Είναι μια DIY (Do It Yourself) κατασκευή, που θα σας δώσει μεγάλη ευχαρίστηση από την στιγμή που θα την ολοκληρώσετε πλήρως. Το ρολόι που πρόκειται να φτιάξετε αποτελείται από τέσσερα μεγάλα LED ψηφία, που όταν το κρεμάσετε στο τοίχο θα έχετε την ένδειξη της ώρας από μακριά.

Περιγραφή του κυκλώματος
Το βασικό εξάρτημα του κυκλώματος είναι ένας AVR μικροελεγκτής τύπου ATmega328P, σε σειριακή επικοινωνία με το ολοκληρωμένο κύκλωμα DS1302 που υποστηρίζει την ώρα, απαρτίζουν το βασικό κορμό του κυκλώματος της κατασκευής.

Τα τμήματα των τεσσάρων ψηφίων συνδέονται μαζί με τον τρόπο της πολυπλεξίας, που οδηγούνται από το buffer/amplifier ULN2003 από την πόρτα Β του μικροελεγκτή. Κάθε στιγμή ένα ψηφίο ενεργοποιείται και έτσι τα τέσσερα ψηφία ανάβουν διαδοχικά, τα οποία ελέγχονται από τα PNP τρανζίστορ Q1, Q2, Q3 και Q4 των οποίων οι βάσεις οδηγούνται από τον μικροελεγκτή.

Η βάση σε καθένα από τα τρανζίστορ Q1, Q2, Q3 και Q4 σε σειρά με μια zener δίοδο 8,2V και μια αντίσταση τιμής 1,5ΚΩ συνδέονται με ένα από τους τέσσερις ακροδέκτες της πόρτας C του μικροελεγκτή. Όταν η τάση ενός από αυτούς τους ακροδέκτες είναι LOW (0V), η τάση που αναπτύσσεται μεταξύ της επαφής βάσης – εκπομπού του τρανζίστορ και την αντίσταση στη βάση είναι ίση με τη διαφορά των 12V (DC τάση τροφοδοσίας) μείον την τάση zener 8,2V το οποίο είναι ίσο με 3,8V και έτσι η επαφή βάσης – εκπομπού πολώνεται ορθά και το τρανζίστορ άγει στέλνοντας ρεύμα στο αντίστοιχο ψηφίο με αποτέλεσμα την εμφάνιση της ένδειξης του ψηφίου. Όταν η τάση του ακροδέκτη της πόρτας C είναι 5V τότε η επαφή βάσης – εκπομπού δεν μπορεί να πολωθεί ορθά διότι 8,2V (τάση zener) συν 5V (τάση ακροδέκτη της πόρτας του AVR) συν 0,6 (τάση εκπομπού – βάσης) είναι ίσο με 13,8V μεγαλύτερη από την τάση τροφοδοσίας κι έτσι το τρανζίστορ μπαίνει σε κατάσταση αποκοπής, συνεπώς την αμαύρωση του αντίστοιχου ψηφίου. Με αυτό το τρόπο ο AVR ενεργοποιεί ένα από τα τρανζίστορς διαδοχικά και στιγμιαία, με την κατάλληλη συχνότητα, με το αντίστοιχο ψηφίο και έτσι εμφανίζεται η ένδειξη της ώρας.

Για την τροφοδότηση των ολοκληρωμένων κυκλωμάτων (AVR, DS1302) απαιτείται σταθερή DC τάση 5V που την παίρνουμε από την έξοδο του σταθεροποιητή τάσης τύπου 7805, ενώ η όλη κατασκευή τροφοδοτείται με τάση DC 12V από ένα τροφοδοτικό πρίζας.
Το ολοκληρωμένο κύκλωμα DS1302 είναι υπεύθυνο για την παραγωγή της ώρας, το οποίο είναι συνδεμένο με μια μπαταρία λιθίου 3V, τύπου κουμπιού, για την τροφοδότηση του DS1302 σε περίπτωση διακοπής της τάσης τροφοδοσίας, για να μην σταματά η λειτουργία του ρολογιού. Επίσης στο ίδιο ολοκληρωμένο κύκλωμα είναι συνδεμένος ένας κρύσταλλος συχνότητας 32,768KHz για την παραγωγή ακριβή χρόνου αναφοράς.
Κατασκευή
Η συναρμολόγηση της κατασκευής δεν έχει ιδιαίτερη δυσκολία, που γίνεται σε δυο PCB διπλής όψης με επιμεταλλωμένες οπές, που μπορείτε να παραγγείλετε σε μια εταιρεία κατασκευής PCB διαμέσω του διαδικτύου. Στο μπροστινό PCB προσοχή πρέπει να δείξετε στην τοποθέτηση των μπουτόν με το σωστό προσανατολισμό. Πρέπει να είστε ιδιαίτερα προσεκτικοί με την τοποθέτηση των ψηφίων καθώς το τρίτο ψηφίο από αριστερά μπαίνει ανάποδα. Στο πίσω PCB πρέπει να είστε προσεκτικοί με την σωστή τοποθέτηση των ολοκληρωμένων κυκλωμάτων, τα τρανζίστορς και τις διόδους zener.

Τα ολοκληρωμένα κυκλώματα έχουν μια εγκοπή από τη μια μεριά και σε μια δίοδο ο ακροδέκτης από την μεριά του τυπωμένου δακτυλιδιού στο σώμα της δείχνει την κάθοδο. Ο ηλεκτρολυτικός πυκνωτής θα τον τοποθετήσετε με την σωστή πολικότητα.

Η κατασκευή μας τροφοδοτείται με DC τάση 12V από τροφοδοτικό πρίζας, που πρέπει να δίνει το λιγότερο 1Α ρεύμα. Η τάση του τροφοδοτικού δεν πρέπει να υπερβαίνει τα 13,5V, αλλιώς όλα τα ψηφία θα ανάβουν συνεχώς, χωρίς να είναι δυνατή η ένδειξη της ώρας.
Λειτουργία του ρολογιού
Για τη ρύθμιση της ώρας του ρολογιού που κατασκευάσαμε, γίνεται από τέσσερα μπουτόν που βρίσκονται στα αριστερά των ψηφίων. Η λειτουργία των μπουτόν είναι η ακόλουθη:
1] Πατούμε συνέχεια για δέκα δευτερόλεπτα το μπουτόν SEL και μπαίνουμε σε κατάσταση ρύθμισης, από όπου:
2] Πατώντας το μπουτόν IND επιλέγουμε 12ωρη ή 24ωρη μορφή ώρας.
3] Πατώντας το μπουτόν HR ρυθμίζουμε τις ώρες
4] Πατώντας το μπουτόν ΜΙΝ ρυθμίζουμε τα λεπτά
5] Πατώντας στιγμιαία το μπουτόν SEL το ρολόι επιστρέφει στην κανονική του λειτουργία.
Τα εξαρτήματα
Για το μπροστινό PCB
R1, R2 αντιστάσεις 820R 1/4W
C1 ηλεκτρολυτικός πυκνωτής 4700uF/25V
S1, S2, S3, S4, S5 pcb μπουτόν
DIS1, DIS2, DIS3, DIS4 Μεγάλα 2,3 ίντσες κόκκινα Led 7 segments displays
CON1 19 pin ακιδοσειρά θηλυκή
Για το πίσω PCB
R1, R2,…,R7 αντιστάσεις 220R 1/4W
R8, R9, R10, R11 αντιστάσεις 1K5 1/4W
R12, R13, R14, R15 αντιστάσεις 2K2 1/4W
R17 αντίσταση 4K7 1/4W
R18 αντίσταση 2K2 1/4W
R19 αντίσταση 10K 1/4W
C1, C2 κεραμικοί πυκνωτές 100nF or 220nF
D1, D2, D3, D4 1N4738 δίοδος Zener 1W 8.2V
Q1, Q2, Q3, Q4 PNP τρανζίστορς BC328
Q5 NPN τρανζίστορ BC547
XTAL κρύσταλλος 32.728KHz
IC1 ATmega328Ρ AVR προγραμματισμένος μικροελεγκτής
IC2 ολοκληρωμένο κύκλωμα ULN 2003
IC3 real clock DS1302
IC5 5V σταθεροποιητής 7805
CON1 19 pin ακιδοσειρά αρσενική
BAT CR2032 Coin Batter with holder
Ο κώδικας για τον μικροελεγκτή
Η κατασκευή βασίζεται σε ένα AVR μικροελεγκτή τύπου ATmega328P στον οποίο θα πρέπει να ανεβάσετε τον ακόλουθο κώδικα:
#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define BUTTON_PIN PIND
#define BUTTON_DDR DDRD
#define BUTTON_HR_PIN PINB
#define BUTTON_HR_DDR DDRB
#define BUTTON_SEL PD7
#define BUTTON_IND PD5
#define BUTTON_HR PB7
#define BUTTON_MIN PD3
#define BUTTON_MODE PD6
#define LAMP_PORT PORTC
#define LAMP_DDR DDRC
#define LAMP_CLOCK PC4
#define CLOCK_PORT PORTD
#define CLOCK_PIN PIND
#define CLOCK_DDR DDRD
#define CLOCK_CE PD2
#define CLOCK_IO PD1
#define CLOCK_SCLK PD0
#define SET_CLOCK_CE() CLOCK_PORT|=(1<<CLOCK_CE)
#define CLR_CLOCK_CE() CLOCK_PORT&= ~(1<<CLOCK_CE)
#define SET_CLOCK_IO() CLOCK_PORT|=(1<<CLOCK_IO)
#define CLR_CLOCK_IO() CLOCK_PORT&= ~(1<<CLOCK_IO)
#define SET_CLOCK_SCLK() CLOCK_PORT|=(1<<CLOCK_SCLK)
#define CLR_CLOCK_SCLK() CLOCK_PORT&= ~(1<<CLOCK_SCLK)
#define SET_CLOCK_DDRIO() CLOCK_DDR|=(1<<CLOCK_IO)
#define CLR_CLOCK_DDRIO() CLOCK_DDR&= ~(1<<CLOCK_IO)
#define CLOCK_SEC_REG 0x80
#define CLOCK_MIN_REG 0x82
#define CLOCK_HR_REG 0x84
#define CLOCK_CONTROL_REG 0x8e
#define BUTTON_INPUT_SEL() !(BUTTON_PIN& (1<<BUTTON_SEL))
#define BUTTON_INPUT_MIN() !(BUTTON_PIN& (1<<BUTTON_MIN))
#define BUTTON_INPUT_HR() !(BUTTON_HR_PIN& (1<<BUTTON_HR))
#define BUTTON_INPUT_IND() !(BUTTON_PIN& (1<<BUTTON_IND))
#define TCNT0_TIME 152
#define MAX_COUNT 150
#define MAX_COUNT_DIV2 75
#define PRINTING_TIME 30
uint8_t segment[]={0b01111101/*0*/, 0b00010001/*1*/, 0b01111010/*2*/,
0b01011011/*3*/, 0b00010111/*4*/, 0b01001111/*5*/, 0b01101111/*6*/,
0b00011001/*7*/, 0b01111111/*8*/, 0b01011111/*9*/, 0b0000010/*-*/,
0b00011110/*o*/, 0b01101100/*C*/, 0b00000000/*" "*/, 0b01101110/*E*/};
uint8_t digit_position[4]={0b1011, 0b1101, 0b0111, 0b1110};
uint8_t digit[4]={0, 1, 2, 3};
uint8_t digit_number=0, flash=0;
uint16_t count=0;
uint8_t hours=0, minutes=0, flag_hours=1;
void clock_delay(void){
asm("nop");
asm("nop");
asm("nop");
asm("nop");
}
void clock_write(uint8_t reg, uint8_t data){
uint8_t i;
cli();
CLR_CLOCK_CE();
SET_CLOCK_DDRIO();
CLR_CLOCK_CE();
clock_delay();
CLR_CLOCK_SCLK();
clock_delay();
SET_CLOCK_CE();
clock_delay();
for(i=8; i>0; i--){
if(reg&0x01) SET_CLOCK_IO(); else CLR_CLOCK_IO();
clock_delay();
SET_CLOCK_SCLK();
clock_delay();
CLR_CLOCK_SCLK();
clock_delay();
reg>>=1;
}
for(i=8; i>0; i--){
if(data&0x01) SET_CLOCK_IO(); else CLR_CLOCK_IO();
clock_delay();
SET_CLOCK_SCLK();
clock_delay();
CLR_CLOCK_SCLK();
clock_delay();
data>>=1;
}
CLR_CLOCK_CE();
clock_delay();
CLR_CLOCK_DDRIO();
sei();
}
uint8_t clock_read(uint8_t reg){
uint8_t data=0, i;
cli();
reg|=0x01;
CLR_CLOCK_CE();
SET_CLOCK_DDRIO();
CLR_CLOCK_CE();
clock_delay();
CLR_CLOCK_SCLK();
clock_delay();
SET_CLOCK_CE();
clock_delay();
for(i=8; i>0; i--){
if(reg&0x01) SET_CLOCK_IO(); else CLR_CLOCK_IO();
clock_delay();
SET_CLOCK_SCLK();
clock_delay();
CLR_CLOCK_SCLK();
clock_delay();
reg>>=1;
}
CLR_CLOCK_DDRIO();
for(i=8; i>0; i--){
data>>=1;
if(CLOCK_PIN&(1<<CLOCK_IO)) data|=0x80;
SET_CLOCK_SCLK();
clock_delay();
CLR_CLOCK_SCLK();
clock_delay();
}
CLR_CLOCK_CE();
clock_delay();
sei();
return data;
}
void print_minutes(uint8_t min){
digit[0]=min&0x0f;
min>>=4;
digit[1]=min&0x07;
}
void print_hours(uint8_t hr, uint8_t flag){
uint8_t h=0;
h = (hr&0x0f)+((hr>>4)&0x03)*10;
if(flag){
digit[2]=(h%10);
digit[3]=(h/10);
}else{
if(h==0) h=12;
if(h>12) h-=12;
digit[2]=(h%10);
if(h>=10) digit[3]=1; else digit[3]=13;
}
}
void clock_adj(void){
uint8_t hr, min, min0, min1, hr0, hr1;
clock_write(CLOCK_CONTROL_REG, 0x00);
clock_write(CLOCK_SEC_REG, 0x80);
clock_write(CLOCK_CONTROL_REG, 0x80);
hr = clock_read(CLOCK_HR_REG);
min = clock_read(CLOCK_MIN_REG);
print_hours(hr, flag_hours);
print_minutes(min);
while(BUTTON_INPUT_SEL()) _delay_ms(100);
min0 = min&0x0f;
min1 = (min>>4)&0x07;
hr0 = hr&0x0f;
hr1 = (hr>>4)&0x03; //!!
while(1){
if(BUTTON_INPUT_IND()){
if(flag_hours) flag_hours=0; else flag_hours=1;
print_hours(hr, flag_hours);
while(BUTTON_INPUT_IND()) _delay_ms(100);
}
if(BUTTON_INPUT_MIN()){
if(min0<9) min0++; else {min0=0; min1++; if(min1==6) min1=0;}
min=min0; min|=((min1&0x07)<<4);
clock_write(CLOCK_CONTROL_REG, 0x00);
clock_write(CLOCK_MIN_REG, min);
clock_write(CLOCK_CONTROL_REG, 0x80);
print_minutes(min);
while(BUTTON_INPUT_MIN()) _delay_ms(100);
}
if(BUTTON_INPUT_HR()){
if(hr0<9 && hr1<2) hr0++;
else if(hr0==9) {hr0=0; hr1++;}
else if(hr1==2) {if(hr0<3) hr0++; else hr0=hr1=0;}
hr=hr0; hr|=((hr1&0x03)<<4);
clock_write(CLOCK_CONTROL_REG, 0x00);
clock_write(CLOCK_HR_REG, hr);
clock_write(CLOCK_CONTROL_REG, 0x80);
print_hours(hr,flag_hours);
while(BUTTON_INPUT_HR()) _delay_ms(100);
}
if(BUTTON_INPUT_SEL()) break;
}
clock_write(CLOCK_CONTROL_REG, 0x00);
clock_write(CLOCK_SEC_REG, 0);
clock_write(CLOCK_CONTROL_REG, 0x80);
_delay_ms(100);
}
ISR(TIMER0_OVF_vect)
{
//TCNT0 = TCNT0_TIME;
count++;
if(count>MAX_COUNT) count=0;
PORTC |= 0b00001111;
if(count < MAX_COUNT_DIV2) PORTC &= (0xf0 | digit_position[digit_number]);
if(!flash) PORTC &= (0xf0 | digit_position[digit_number]);
PORTB = segment[digit[digit_number]];
digit_number++;
if(digit_number > 3) digit_number = 0;
TCNT0 = TCNT0_TIME;
}
int main(void)
{
uint8_t j=0;
DDRC=0b00001111;
DDRB=0b01111111;
BUTTON_DDR &= ~((1<<BUTTON_SEL)|(1<<BUTTON_IND)|(BUTTON_MODE)|(BUTTON_MIN));
BUTTON_HR_DDR &= ~(1<<BUTTON_HR);
CLOCK_DDR |= ((1<<CLOCK_CE)|(1<<CLOCK_SCLK));
LAMP_DDR |= (1<<LAMP_CLOCK);
LAMP_PORT |= (1<<LAMP_CLOCK);
TCNT0 = TCNT0_TIME;
TCCR0A = 0x00;
TCCR0B = 0x03;
TIMSK0 = (1<<TOIE0);
sei();
while (1)
{
//clock_write(CLOCK_CONTROL_REG, 0x00);
hours=clock_read(CLOCK_HR_REG);
minutes=clock_read(CLOCK_MIN_REG);
//clock_write(CLOCK_CONTROL_REG, 0x80);
print_hours(hours, flag_hours);
print_minutes(minutes);
for(j=0; j<PRINTING_TIME; j++){
if(BUTTON_INPUT_SEL()){
_delay_ms(500);
if(BUTTON_INPUT_SEL()){flash=1; clock_adj(); flash=0; break;}
}
_delay_ms(200);
}
_delay_ms(100);
}
}
…