Κ115 ψηφιακό ρολόι γίγας

Σε αυτό το άρθρο θα σας δείξω πως να κατασκευάσετε ένα ψηφιακό ρολόι με μεγάλα 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 στον οποίο θα πρέπει να ανεβάσετε τον ακόλουθο κώδικα:

/*
 * big_clock_v1.c
 *
 * Created: 11/7/2023 7:09:28 μμ
 * Author : Yiannis Plevritakis
 */ 

#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>

#define CLOCK_CE   PD2
#define CLOCK_IO   PD1
#define CLOCK_SCLK PD0

#define LAMP_CLOCK  PC4
#define BUTTON_SEL   PD7
#define BUTTON_IND   PD5
#define BUTTON_HR    PB7
#define BUTTON_MIN   PD3
#define BUTTON_MODE  PD6


#define CLOCK_PORT   PORTD
#define CLOCK_PIN    PIND
#define CLOCK_DDR    DDRD

#define LAMP_PORT    PORTC
#define LAMP_DDR     DDRC
#define BUTTON_PIN   PIND
#define BUTTON_DDR   DDRD
#define BUTTON_HR_PIN PINB
#define BUTTON_HR_DDR DDRB

#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 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 BUTTON_INPUT_MODE()  !(BUTTON_PIN&(1<<BUTTON_MODE))

#define CLOCK_SEC_REG       0x80
#define CLOCK_MIN_REG       0x82
#define CLOCK_HR_REG        0x84
#define CLOCK_DATE_REG      0x86
#define CLOCK_MONTH_REG     0x88
#define CLOCK_DAY_REG       0x8a
#define CLOCK_YEAR_REG      0x8c
#define CLOCK_CONTROL_REG   0x8e
#define CLOCK_CHARGER_REG   0x90
#define CLOCK_CLKBURST_REG  0xbe

#define TCNT0_TIME -19
#define MAX_COUNT 240
#define T_DELAY 1000

// Πίνακας ορισμού των τμημάτων του ψηφίου για κάθε τιμή της ένδειξης
uint8_t seg[]={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*/};

// Πίνακας μετατροπής 24ώρας σε 12ωρης
uint8_t digit_mode[] = {0x12, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11};

uint8_t digit[4]={0,0,0,0}, k=0;
uint8_t digit_num[4]={0b1011, 0b1101, 0b0111, 0b1110};
uint8_t flash=0, count=0, mode=0;

// Καθηστέρηση χρησιμοποιόντας τον timer1
void delayloop(void){
	TCNT1H = 0xF3;
	TCNT1L = 0xCA;
	TCCR1A = 0x00;
	TCCR1B = 0x04;
	while((TIFR1 & (1<<TOV1))==0);
	TCCR1B = 0x00;
	TIFR1 = (1<<TOV1);
}

// Συνάρτηση που προκαλεί καθυστέρηση
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) ==1) 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();
}

int8_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)==1) 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;
}

uint8_t check_clock(void) {
	clock_write(CLOCK_CONTROL_REG, 0x80);
	uint8_t temp;
	temp=clock_read(CLOCK_CONTROL_REG);
	if( 0x80 == temp)
	return 1;
	else
	return 0;
}
void print_mode(void){
	digit[0] = 0x01&mode;
	digit[1]=digit[2]=digit[3]=13;
}

void print_time(uint8_t hour, uint8_t minute){
	digit[0] = minute&0x0f;
	digit[1] = (minute>>4)&0x07;
	uint8_t h = (((hour>>4)&0x03)*0x0A) + (hour&0x0f);
	if(mode==0){
		//Μορφή ώρας 00-23
		digit[2] = hour&0x0f;
		digit[3] = (hour>>4)&0x03;
	}
	else{
		//Μορφή ώρας 1-12
		digit[2]=digit_mode[h]&0x0f;
		if((digit_mode[h]>>4)&0x03) digit[3]=1; else digit[3]=13;
	}
		LAMP_PORT |= (1<<LAMP_CLOCK);
}

void clock_adj(void){
	uint8_t hr, min, min0, min1, hr0, hr1;
	flash=1;
	while(BUTTON_INPUT_SEL()) delayloop();
	
	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_time(hr, min);
	
	min0 = min&0x0f;
	min1 = (min>>4)&0x07;
	hr0 = hr&0x0f;
	hr1 = (hr>>4)&0x0f;
	
	while(1){
	   // Εάν το κουμπί MODE είναι πατημένο
	  if(BUTTON_INPUT_MODE()){
		if(mode) mode=0; else mode=1;
		print_mode();
		while(BUTTON_INPUT_MODE()) delayloop();
		continue;
	   }
			
	   //Εάν το κουμπί MIN είναι πατημένο
	   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_time(hr, min);
	       while(BUTTON_INPUT_MIN()) delayloop();
	       continue;
	    }
				
	    //Εάν το κουμπι HR είναι πατημένο
	    if(BUTTON_INPUT_HR()){
			
		//if hour is 00-23
		if((hr1&0x03)<2) { if(hr0<9) hr0++; else {hr0=0; hr1++;} }
		else{ if(hr0<3) hr0++; 
                        else {hr1=0; hr0=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_time(hr, min);
		while(BUTTON_INPUT_HR()) delayloop();
		continue;
		}
		
		if(BUTTON_INPUT_SEL()) break;
	} 
	print_time(hr, min);
	clock_write(CLOCK_CONTROL_REG, 0x00);
	clock_write(CLOCK_SEC_REG, 0); // Ξεκίνημα ρολογιού
	clock_write(CLOCK_CONTROL_REG, 0x80);
	
	//delayloop();  
}

int main(void)
{
    // Δήλωση pins σαν είσοδοι ή έξοδοι
    DDRC=0b00001111; 
    DDRB=0b01111111;		
  BUTTON_DDR &= ~((1<<BUTTON_SEL)|(1<<BUTTON_IND)|(1<<BUTTON_MODE)|(1<<BUTTON_MIN));
    BUTTON_HR_DDR &= ~(1<<BUTTON_HR);
    CLOCK_DDR|=((1<<CLOCK_CE)|(1<<CLOCK_SCLK));
    LAMP_DDR |= (1<<LAMP_CLOCK);
    	    	
    uint8_t hour, minute, j;
    	
    //clock_write(CLOCK_CONTROL_REG, 0x00);
    //clock_write(CLOCK_SEC_REG, 0b00000011);
    //clock_write(CLOCK_MIN_REG, 0b00010101);
    //clock_write(CLOCK_HR_REG, 0b00000011);
    //clock_write(CLOCK_CONTROL_REG, 0x80);
		
   // Αρχικοποίηση του timer0
   TCNT0 = TCNT0_TIME;
   TCCR0A = 0;
   TCCR0B = 0x04;
   TIMSK0 = (1<<TOIE0);
   sei();
		
    while (1) 
    {
	clock_write(CLOCK_CONTROL_REG, 0x00); 
	hour = clock_read(CLOCK_HR_REG); // Διάβασμα των ωρών από το ρολόι
	minute = clock_read(CLOCK_MIN_REG); // Διάβασμα των λεπτών από το ρολόι
	clock_write(CLOCK_CONTROL_REG, 0x80);
	print_time(hour, minute);
	j=0;
	for(j=0; j<10; j++){
	    if(BUTTON_INPUT_SEL()) {
		for(uint8_t i=1; i<=5; i++) delayloop();
		if(BUTTON_INPUT_SEL()) { clock_adj(); flash = 0; break; }
	    }
	    for(uint8_t i=1; i<=5; i++) delayloop();
	}
    }
}

 ISR (TIMER0_OVF_vect){
     count++;
     if(count>MAX_COUNT) count=0;
     PORTC |= 0b00001111;
     if(count < MAX_COUNT/2) PORTC &= (0xf0 | digit_num[k]);
     if(!flash) PORTC &= (0xf0 | digit_num[k]);
     PORTB=seg[digit[k]];
     k++;
     if(k>3) k=0; 
     TCNT0=TCNT0_TIME; 
 }