I got the package from China with some ICs I wanted to try before embedding them to the clock project I want to make. Have to warn: my point of interest was just a time, the date was out of the scope, so nothing about it will be investigated further.
Goal: To write the value of the time/read the time and output it thru UART from the DS1307 chip
Tools: PIC16f628a, DS1307, MAX232 level converter, devboard, proteus.

The DS1307 IC is a real-time clock module, controlled by the I2C protocol. There are plenty of microcontrollers with the RTC on board, but I wanted to try the most popular one with tons of info on the internet. First thing first – the datasheet application scheme and repeating it in proteus:

Also from the datasheet – the memory structure, we will need it for sure:

As I mentioned already, I am interested just in the time. Don’t care about the date, the month, or the year. Therefore, we need just 3 addresses – 00h, 01h and 02h. The data is saved in a BCD (binary coded decimal) format.
Shortly about BCD: The mixed data format: the 8 number is split into two parts, in the least significant part we have the usual binary representation, in the most significant part can be thought of as a digit in binary form, right shifted by 4 bits and multiplied by 10. The easiest way to think about the format is that each decimal position can be represented as 4 bits. Almost as hexadecimal, just here we are limited to 0..9. An example – 0b00110101 => 0b0011 = 3*10 = 30, 0b0101 = 5 – the resulting digit = 35.
Thus, we should preprocess our normal number before writing it to the register or reading it back. I have created two functions for this purpose: one is for decimal to BCD and another is for, obviously, converting BCD to decimal.
BCD to decimal conversion:
unsigned char BCDconv (unsigned char source) {
unsigned char temp_min=0; //ones
unsigned char temp_maj=0; //tens
temp_min = source&15; //lsb to variable (15 = 0b00001111)
temp_maj = source >> 4;
temp_maj *= 10;
return temp_maj+temp_min; //return the decimal number
}Decimal to BCD:
unsigned char DCBconv (unsigned char source) {
unsigned char temp_min=0;
unsigned char temp_maj=0;
temp_maj = source/10 ;
temp_min = source - temp_maj*10;
temp_maj <<= 4;
return temp_maj+temp_min;
}The next thing is to learn how to write/read to/from RTC IC. Again, the datasheet has a pretty picture:

This is a writing procedure, that looks fairly simple. Before that we need to know how to work with i2c standard, luckily I have it already.
void SetHour(unsigned char hours) {
i2c_start();
i2c_tx(0b11010000);
i2c_tx(0x02); //rtc hour address
i2c_tx(DCBconv(hours)); //push the right value
i2c_stop();
}
void SetMin(unsigned char minutes) { //write minutes
i2c_start();
i2c_tx(0b11010000);
i2c_tx(0x01); //rtc minuntes address
i2c_tx(DCBconv(minutes)); //push the right value
i2c_stop();
}
void SetSeconds(unsigned char seconds) { //write seconds
i2c_start();
i2c_tx(0b11010000);
i2c_tx(0x00); //rtc seconds address
i2c_tx(DCBconv(seconds)); //push the right value
i2c_stop();
}Reading has one tiny thing I hadn’t noticed at the moment:
The datasheet says: The DS1307 must receive a Not Acknowledge to end a read.
I completely missed that and actually was sending the ACK bitА. To just bring your attention again to it, there is a picture from the datasheet:

Remember: read the datasheet carefully, don’t be like me!
The time for reading procedures has come:
unsigned char ReadHour() {
unsigned char temp = 0;
i2c_start();
i2c_tx(0b11010000);
i2c_tx(0x02);
i2c_start();
i2c_tx(0b11010001);
temp = i2c_rx(0); //nack
i2c_stop();
return BCDconv(temp);
}
unsigned char ReadMin() {
unsigned char temp = 0;
i2c_start();
i2c_tx(0b11010000);
i2c_tx(0x01);
i2c_start();
i2c_tx(0b11010001);
temp = i2c_rx(0);
i2c_stop();
return BCDconv(temp);
}
unsigned char ReadSeconds() {
unsigned char temp = 0;
i2c_start();
i2c_tx(0b11010000);
i2c_tx(0x00);
i2c_start();
i2c_tx(0b11010001);
temp = i2c_rx(0);
i2c_stop();
return BCDconv(temp);
}Now we need something to understand if we have a proper time, I chose the USART (UART) protocol for this procedure. First, something to display the time:
void ShowTime() {
printf("\fTime - %d :", ReadHour());
printf(" %d :", ReadMin());
printf(" %d ", ReadSeconds());
printf("\r\n*********************");
printf("\r\n Menu");
printf("\r\n 1 - Reload time");
printf("\r\n 2 - Setup time");
}The function displays the next menu:

“1” – the time is refreshed, I am not refreshing the data constantly for purpose, did not want to catch any extra bugs.
“2” – the setup time procedure call.
void SetupTime() {
unsigned char state = 1;
unsigned char temp_min = 0;
unsigned char temp_maj = 0;
unsigned char temp = 0;
while(state){ //set hours
printf("\fEnter hours - ");
temp_maj = getch();
printf(" %c", temp_maj);
temp_min = getch();
printf("%c", temp_min);
temp = getch();
if (temp == 13) {
temp_maj -= 48;
temp_min -= 48;
temp = temp_maj*10 + temp_min;
if (temp < 24) {
SetHour(temp);
state = 0;
}
}
}
state = 1;
while(state){ //set minutes
printf("\fEnter minutes - ");
temp_maj = getch();
printf(" %c", temp_maj);
temp_min = getch();
printf("%c", temp_min);
temp = getch();
if (temp == 13) {
temp_maj -= 48;
temp_min -= 48;
temp = temp_maj*10 + temp_min;
if (temp < 61) {
SetMin(temp);
state = 0;
}
}
}
state = 1;
while(state){ //set seconds
printf("\fEnter seconds - ");
temp_maj = getch();
printf(" %c", temp_maj);
temp_min = getch();
printf("%c", temp_min);
temp = getch();
if (temp == 13) {
temp_maj -= 48;
temp_min -= 48;
temp = temp_maj*10 + temp_min;
if (temp < 61) {
SetSeconds(temp);
state = 0;
}
}
}
ShowTime();
}and the main.c file of course:
void main(void){
unsigned char input;
CMCON = 0x07;
INTCON=0; // no interrups
init_comms(); // USART init
SetHour(15); // random value for the time
SetMin(38);
SetSeconds(23);
ShowTime(); //show the men
for(;;){
input = getch(); //key polling
switch (input) {
case 49 : ShowTime(); //1 pressed - refresh the screen
break;
case 50 : SetupTime(); //2 pressed - setup the time
break;
}
}
}That’s it, the video with real device work example is shown below: