Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Clock

Status
Not open for further replies.

AtomSoft

Well-Known Member
hey some of you know i have been working on a clock using a DS1306 and i wanted to post this for anyone who wants to see it. at the moment you can only set the time. Its a huge file.

You can study about BCD conversions, SPI, Shift Registers(74LS165 & 74LS164). I say you can study mainly because it all works so far. And if it works then maybe it can help some people.

Im in the process of adding code to Set the Date including Day, month, year, actual day... take a look.

I rewrote my old code. This should be a nice read for some.
Code:
#include <p18F248.h>
#include <stdio.h>
#include <delays.h>
#include <spi.h>

#pragma config WDT = OFF, LVP = OFF, OSC = HS
/************************************
Prototypes
*************************************/
void initSPI(void);
void e_togg(void);
void lcd_line(char line);
void lcd_cmd(unsigned char letter);
void lcd_char(unsigned char letter);
void lcd_string(char *senpoint);
void lcd_init(void);
void delay_ms(int mS);
void delay_us(int uS);
void delay_s(int S);
void lcdByte(unsigned char dNyb,unsigned char rs);
char RTCRegRead(char adx);
void RTCRegWrite(char adx, char data);
void showMenu(void);
void setDate(void);
void setTime(char type);
static unsigned char uint2bcd(unsigned char ival);
char readBtn(void);
unsigned char bcd2dec(unsigned char aBCD, char type);
/************************************
Definitions
************************************/
#define CS LATCbits.LATC2
#define CS_T  TRISCbits.TRISC2
#define SCL_T TRISCbits.TRISC3
#define SDA_T TRISCbits.TRISC4
#define SDO_T TRISCbits.TRISC5

#define LCD_DAT_T TRISCbits.TRISC0
#define LCD_CLK_T TRISCbits.TRISC1

#define LCD_DAT LATCbits.LATC0
#define LCD_CLK LATCbits.LATC1

#define LCD_E_T TRISCbits.TRISC6
#define LCD_E   LATCbits.LATC6

#define PL  LATBbits.LATB0
#define CP2 LATBbits.LATB1
#define SDL  PORTBbits.RB2

#define PL_T  TRISBbits.TRISB0
#define CP2_T TRISBbits.TRISB1
#define SDL_T  TRISBbits.TRISB2

#define setupBtn  0x08
#define downBtn   0x02
#define upBtn     0x01
#define selectBtn 0x10
/************************************
Variables
************************************/
char string[] =  "                ";
char string2[] = "                ";

unsigned char time[17];
unsigned char theDay;
unsigned char theDate[2];
unsigned char theMonth[2];
unsigned char theYear[2];

unsigned char theHour[2];
unsigned char theMin[2];
unsigned char theSec[2];

unsigned char decHour;
unsigned char decMin;
unsigned char decSec;
unsigned char decAMPM;
/************************************
Main
************************************/
void main(void){
    unsigned char tmp,tmp2;
    unsigned char buff[40];
    char i;
    time[16] = 0;
    ADCON1 = 0x0E;

    initSPI();
	lcd_init();

    PL_T = 0;
    CP2_T = 0;
    SDL_T = 1;

	lcd_line(1);
	sprintf(string,"  AtomSoftTech",0);
	lcd_string(string);

	lcd_line(2);
	sprintf(string," 2-Wire 74LS164",0);
	lcd_string(string);

    delay_s(1);

	lcd_line(2);
	sprintf(string,"  DS1306 TEST.  ",0);
    lcd_string(string);

    delay_s(1);

    while(1){
	lcd_line(1);
	sprintf(string,"  AtomSoftTech",0);
	lcd_string(string);

    tmp = RTCRegRead(0x02);
    time[15] = (tmp >> 4) & 0x02;

    if(time[15] == 0) 
        time[15] = 'A' ;
    else 
        time[15] = 'P';

    time[14] = 0x20;

    tmp = RTCRegRead(0x00);
    decSec = tmp;
    theSec[1] = time[13] = (tmp & 0x0F) + 0x30;
    theSec[0] = time[12] = ((tmp >> 4) & 0x0F ) + 0x30;
    time[11] = ':';

    tmp = RTCRegRead(0x01);
    decMin = tmp;
    theMin[1] = time[10] = (tmp & 0x0F) + 0x30;
    theMin[0] = time[9] = ((tmp >> 4) & 0x0F ) + 0x30;
    time[8] = ':';

    tmp = RTCRegRead(0x02);
    decHour = tmp;
    theHour[1] = time[7] = (tmp & 0x0F) + 0x30;
    theHour[0] = time[6] = ((tmp >> 4) & 0x01 ) + 0x30;

    time[5] = 0x20;
    time[4] = ':';
    time[3] = 'e';
    time[2] = 'm';
    time[1] = 'i';
    time[0] = 'T';

	lcd_line(2);
	lcd_string(time);

    tmp = readBtn();

    if(tmp == setupBtn)
        showMenu();;

    delay_ms(10);

    }
}
/***********************************
RTC Tools
************************************
Convert Char(byte) to BCD
************************************/
unsigned char uint2bcd(unsigned char ival)
{
	return ((ival / 10) << 4) | (ival % 10);
}
/************************************
Convert BCD to Char(byte)
************************************/
unsigned char bcd2dec(unsigned char aBCD, char type)
{
    char lowNyb = aBCD & 0x0F;
    char highNyb = aBCD >> 4;
    char MyDec = lowNyb;
    char x;

    switch(type){
        case 0:
            highNyb &= 0x0F;
            break;
        case 1:
            highNyb &= 0x01;
            break;
        case 2:
            highNyb &= 0x03;
            break;
        case 3:
            highNyb &= 0x07;
            break;
    }

    for(x=0;x<highNyb;x++){
        MyDec += 10;    
    }

    return MyDec;
}
/************************************
Read a Register off the DS1306
************************************/
char RTCRegRead(char adx){
    char tmp;
    CS = 1;
        WriteSPI(adx); //sec
        tmp = ReadSPI();
    CS = 0;

    return tmp;
}
/************************************
Write to a Register on the DS1306
************************************/
void RTCRegWrite(char adx, char data){
    adx |= 0x80;

    CS = 1;
        WriteSPI(adx);
        WriteSPI(data);
    CS = 0;
}
/************************************
Initialize SPI
************************************/
void initSPI(void){
    CS_T = 0;       //CS is output
    SCL_T = 0;      //SCL is output
    SDA_T = 1;      //SDA is input
    SDO_T = 0;      //SDO is output

    OpenSPI(SPI_FOSC_4,MODE_10,SMPEND);         

    CS = 1;
        WriteSPI(0x8F);
        WriteSPI(0x00);
    CS = 0;

}
/***********************************
LCD Functions
************************************
Send String to LCD
************************************/
void lcd_string(char *senpoint){
    delay_ms(1);
	while(*senpoint != '\0'){
		lcd_char(*senpoint);
		senpoint++;
	}
}
/************************************
Send Data(Nybble) to LCD
************************************/
void lcdByte(unsigned char dNyb,unsigned char rs){
	int i;

	LCD_DAT=0;							//Clear 74LS164 set initial bit to 0
	for(i=0;i<8;i++){				    //repeat for 8 bits
		LCD_CLK=1;LCD_CLK=0;			//write 0's to the 164
	}

	for(i=0;i<4;i++){				    //output the nybble
		if((dNyb & 0x08) != 0)
			LCD_DAT=1;
		else
			LCD_DAT=0;

		LCD_CLK=1;LCD_CLK=0;
		dNyb=dNyb<<1;
	}

	LCD_DAT = rs;						    //output the RS bit value
	LCD_CLK=1;LCD_CLK=0;

	LCD_DAT = 0;
	LCD_CLK=1;LCD_CLK=0;
	LCD_CLK=1;LCD_CLK=0;
	LCD_CLK=1;LCD_CLK=0;

	e_togg();
}
/************************************
Toggle E Line
************************************/
void e_togg(void){
	LCD_E=1;LCD_E=0;
}
/************************************
Set LCD Line
************************************/
void lcd_line(char line){
    if(line == 0x01)
        lcd_cmd(0x80);
    else
	    lcd_cmd(0xc0);
}
/************************************
LCD Send Command
************************************/
void lcd_cmd(unsigned char letter){
	unsigned char temp;
	temp=letter;
	temp=temp>>4;
	lcdByte(temp,0);
	temp=letter;
	temp=temp&0x0f;
	lcdByte(temp,0);
}
/************************************
LCD Send Character
************************************/
void lcd_char(unsigned char letter){
	unsigned char temp;
	temp=letter;
	temp=temp>>4;
	lcdByte(temp,1);
	temp=letter;
	temp=temp&0x0f;
	lcdByte(temp,1);
}
/************************************
LCD Initialization
************************************/
void lcd_init(void){

    LCD_E_T = 0;
    LCD_CLK_T = 0;
    LCD_DAT_T = 0;

	lcdByte(0x03,0);
	delay_ms(5);
	e_togg();
	delay_us(160);
	e_togg();
	delay_us(160);
	lcdByte(0x02,0);
	delay_us(160);
	lcd_cmd(0x28);					//set 4-bit mode and 2 lines
	delay_us(160);
	lcd_cmd(0x10);					//cursor move & shift left
	delay_us(160);
	lcd_cmd(0x06);					//entry mode = increment
	delay_us(160);
	lcd_cmd(0x0d);					//display on - cursor blink on
	delay_us(160);
	lcd_cmd(0x01);					//clear display
	delay_ms(500);
}
/************************************
Delays (S,mS,uS) for 20Mhz
************************************/
/*********************
    Delay Second(s)
**********************/
void delay_s(int S){
    int y;
    char x;

    for(y=0;y<S;y++)
        for(x=0;x<4;x++)
            delay_ms(250);

}
/************************
    Delay MilliSecond(s)
*************************/
void delay_ms(int mS){
    int y;

    for(y=0;y<mS;y++)
        Delay1KTCYx(5);
}
/************************
    Delay MicroSeconds
*************************
Lowercase 'u' is common
symbol for Micro
*************************/
void delay_us(int uS){
    int y;
    char x;

    for(y=0;y<uS;y++)
        for(x=0;x<5;x++)
            Nop();
}
/***********************************
SN74LS165 Functions
************************************
Toggle Clock Pin
************************************/
void clk_togg(void){
	LCD_CLK=1;LCD_CLK=0;
}
/************************************
readBtn
************************************/
char readBtn(void){
    char x;
    char buff;
    delay_ms(100);
    PL=0;  PL=1;

    for(x=0;x<8;x++){
        buff <<= 1;             // shift composed byte by 1
        buff &= 0xFE;           // clear bit 0

        if (SDL)                 // is data line high
            buff |= 0x01;       // set bit 0 to logic 1

        CP2 = 0; CP2 = 1;
    }
    delay_ms(100);
    return buff & 0x1F;
}
/***********************************
LCD / RTC stuff
************************************
Show Menu
************************************/
void showMenu(void){
    char item, isEnter;
    isEnter = 0;
    item = 1;   //Default to first menu item

while(1){
/******************************
    Which item are we viewing
*******************************/
    switch(item){
        case 0:
            item=3; //user went do down too far start from 3
            break;
        case 1:
            sprintf(string2,"1.Set Time      ",0);
            break;
        case 2:
            sprintf(string2,"2.Set Date      ",0);
            break;
        case 3:
            sprintf(string2,"3.Set Alarm     ",0);
            break;
        case 4:
            item=1; //user went too up far start them from 1
            break;
    }
        
/*****************************
    Determine Button Pressed
******************************/
    switch(readBtn()){
        case upBtn:
            item++;
            break;
        case downBtn:
            item--;
            break;
        case selectBtn:
            isEnter = 1;
            break;
    }
/*****************************
    Enter is pressed
******************************/
    if(isEnter == 1){
        if(item==1)
            setTime('t');

        if(item==2){
            setDate();
        }
        if(item==3){
            //Set Alarm
        }

        item=9; //a overkill number incase i want to add more functions.
    }
/*************************
    Show Menu to the user
**************************/
    lcd_line(1);
    sprintf(string,"    Settings    ",0);
    lcd_string(&string);

    lcd_line(2);
    lcd_string(&string2);
/**************************
    Check if user is done.
***************************/
    if(item==9)
        break; //leave while(1) loop

}//end of while loop
}
/*****************
    Set the Date
******************/
void setDate(void){

}
/****************
    Set the Time
****************/
void setTime(char type){
    char bcdMin,bcdHour;
    char x = 0;

    decMin = bcd2dec(decMin,3);
    decHour = bcd2dec(decHour,1);
    decAMPM = (RTCRegRead(0x01) >> 5) & 0x01 ;
/********************
    Main while loop
*********************/    
while(1){
/**********************************
    Determine which title to show
***********************************/    
    switch(x){
        case 0:
            sprintf(string,"Set Time: Hour  ",0);
            break;
        case 1:
            sprintf(string,"Set Time: Min   ",0);
            break;
        case 2:
            sprintf(string,"Set Time: AM/PM ",0);
            break;
    }
/*****************************************
   Button pressed functions
******************************************/    
    switch(readBtn()){
        case upBtn:
            switch(x){
                case 0:
                    decHour++;
                    break;
                case 1:
                    decMin++;
                    break;
                case 2:
                    decAMPM++;
                    break;
            }
            break;
        case downBtn:
            switch(x){
                case 0:
                    decHour--;
                    break;
                case 1:
                    if(decMin == 0)
                        decMin = 59;
                    else
                        decMin--;
                    break;
                case 2:
                    decAMPM--;
                    break;
            }
            break;
        case selectBtn:
            x++;
            break;
        case setupBtn:
            return;         //User Pressed Setup aka Cancel

    }
/**********************************************
    Error Fix's:
***********************************************
ex: If hour is greater than 12 then set to 1
if its a 0 set it to 12 since this is 12 hr
there is no 00:00 hour only 1-12 AM/PM
***********************************************/    
    if(decHour >= 13)
        decHour = 1;

    if(decHour == 0)
        decHour = 12;

    if(decMin >= 60)
        decMin = 0;

    if(decAMPM >= 2)
        decAMPM = 0;

    if(decAMPM < 0)
        decAMPM = 0;

    if(x >= 3)
        break;

    if(x == 'c')
        break;
/*****************************************
    Convert to BCD for the RTC(DS1306)
******************************************/   
    bcdHour = uint2bcd(decHour);
    if(decAMPM >= 1)
        bcdHour = bcdHour | 0x20; //0b00100000

    bcdMin = uint2bcd(decMin);
/*********************************************
    Convert to seperate numbers or split BCD
    and add 0x30 so it can be displayed on
    LCD.
**********************************************/   
    time[6] = ((bcdHour >> 4) & 0x01) + 0x30;
    time[7] = (bcdHour & 0x0F) + 0x30;

    time[9] = (bcdMin >> 4) + 0x30;
    time[10] = (bcdMin & 0x0F) + 0x30;

    time[12] = time[13] = 0x30;

    if(decAMPM == 1)
        time[15] = 'P';
    else
        time[15] = 'A';
/*****************************************
    Update the LCD with new info.
******************************************/   
	lcd_line(1);
	lcd_string(string);
	
    lcd_line(2);
	lcd_string(&time);
}
/*****************************************
    Done! Now write to RTC the new Data
******************************************/   
    RTCRegWrite(0x80,0x00);     //Seconds = 00
    RTCRegWrite(0x81,bcdMin);   //Minutes
    RTCRegWrite(0x82,bcdHour);  //Hours
}
 
Last edited:
Hey all i need some more help. Im lost trying to figure out how to make the year for this like its 1 byte (8 bits) BCD of course.

How do i make it 2008? If the most a nybble can be is 15? Should i just make it a 02 and then multiply it by 10 when reading it? And to set it divide it by 10?
 
I believe the year register is a packed BCD value representing the last two decimal digits of the year in the range of 2001 through 2100 (decimal digits '01' through '99' and '00' for year 2100)...

Mike
 
Last edited:
oh ok so no matter what i put it will add to 2001? Like its internal is 2001 and if i set it to 07 it will be 2008?

Or do i need to still set it to 08?

EDIT: I guess i should have figured it out by it saying "10 Year" and "Year" but that starting point kills me...

Am i right?

So 2001 is the starting point mainly because 2001 + 00(min) = 2001 and 2001 + 99(max) = 2100 so it has to start @ 2001
 
Last edited:
I believe the year register is a packed BCD value representing the last two decimal digits of the year in the range of 2001 through 2100 (decimal digits '01' through '99' and '00' for year 2100)...

Mike

I think he meant, the first nibble (least significant) is the units, and the second one (most significant) is the tens. You then add 2000 to the result (except in the case of 00 which means 2100). For example:

Binary: 0110 0011
Decimal: 6 3

so 01100111 (0x63) represents 2063.

ahydra
 
I though i should post a schematic... i just made it so it may have errors lol. I have to double check it.
 

Attachments

  • ClockSchem.png
    ClockSchem.png
    33.3 KB · Views: 247
Ok finally done with code or well not 100% but like 95% i just need to mess with a buzzer for the alarm and have the enter/select button silence the alarm. I have not tested the alarm. I assume i can use the INT straight off the chip to drive a transistor in which will drive my buzzer.

I never had to use a PNP transistor gonna re-read some info on them since the INT0 goes low when active. Hence i need a pullup on it.

LOL the code is too long so i have to attach it lol
 

Attachments

  • main.c
    23.8 KB · Views: 212
Last edited:
lol i beeped one time which was a good sign! lol I will have to put it as a interrupt i guess. I was trying to avoid using interrupts but i guess they're here to haunt me. lol I never had to do interrupts in C. I dont know how to set the location. Can someone help out. Ill try something and see.
 
Last edited:
I tried:
Code:
    RCONbits.IPEN = 1;
    PIR1 = PIR2 = 0;
    INTCON2 = 0x01;
    INTCON = 0xC8;

//~~~~~~~~~~~~~~~~//
#pragma code high_vector_section=0x8
void high_vector (void)
{
    _asm GOTO alarm _endasm
}

#pragma interrupt alarm
void alarm (void){

    alarmOut = 1;
    INTCON = 0xC8;
}

//~~~~~~~~~~~~~//

The issue is it interrupts all the time. Halting everything. I thought maybe it was the debug line but i programmed the pic and removed ICSP wire and alarm never shuts up lol

and
 
Sorry the interrupt pin im using is RB4. RB7:RB5 isnt eing used other than to debug.

I thought doing: "INTCON = 0xC8" would clear the interrupt.
0xC8 = 1100 1000
Code:
GIEH:    1    GIE/GIEH: Global Interrupt Enable bit
GIEL:    1    PEIE/GIEL: Peripheral Interrupt Enable bit
TMR0IE:  0    TMR0IE: TMR0 Overflow Interrupt Enable bit
INT0IE:  0    INT0IE: INT0 External Interrupt Enable bit
RBIE:    1    RBIE: RB Port Change Interrupt Enable bit
TMR0IF:  0    TMR0IF: TMR0 Overflow Interrupt Flag bit
INT0IF:  0    INT0IF: INT0 External Interrupt Flag bit
RBIF:    0    RBIF: RB Port Change Interrupt Flag bit
 
Last edited:
Heh i dont care about the actual tone that comes out :D

I have the INT0 from the DS1306 going into RB4.
RB4 is input and i have the interrupts enabled but only for RBIE no others.

The issue is it turns on even when no alarm or pin change.
The alarm(buzzer) it self is tied to RB3 (which is output)
 
Last edited:
Oh, ok, I get it. You're using interrupt-on-change to detect the alarm signal from the RTC chip. In that case you need to clear the change condition that caused the interrupt by reading the port as well as clearing the RBIF interrupt flag.

Code:
void alarm()
{ unsigned char x;           // RBIF interrupt
  x = PORTB;                 // read PORTB
  INTCONbits.RBIF = 0;       // clear IOC interrupt flag
  alarmout = 1;              //
}
The INT0 pin on the DS1306 is an active low "open drain" output so you probably need a pull-up on that line to pull it to a '1'.
 
Last edited:
Thank you .. i will try it. I do have a pullup on it. I forgot about reading the port tho.
I would just do a:

tmp = PORTB;

you mean?
Just noticed your edit.. ill try it now.
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top