DS1307 with hardware TWI interface ATmega8A

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

Im writing a code to use DS1307 with ATmega8A using TWI interface. As reference im using ATmeaga datasheet (mosty a sample code that is there) and DS1307 datasheet.

This is what i wrote so far (everything that is needed just for sending byte to RTC i think)

void ds1307_init()
{
	/*
	SCL Clock frequency
	SCLf=FCPU/(16+2*TWBR*(4^TWPS))
	so with 4MHz clock it will be
	14,7 kHZ?
	*/
	TWBR = 2;
	TWSR |=((1<<TWPS1)|(1<<TWPS0));

	/*
	Enable the TWI Module
	*/
	TWCR|=(1<<TWEN);
} 

void ds1307_start();
{
	/*
	Send start condition
	Wait for ACK
	*/
	TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
	while(!(TWCR & (1<<TWINT)));
}

void ds1307_stop();
{
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}

void ds1307_writebyte(uint8_t byte);
{
	/*
	check if start sucesfully, if not start error
	*/
	if((TWSR & 0xF8) != START) ERROR(); 
	/*
	Load byte to TWDR (adress) and
	Start transmission of adress
	*/
	TWDR = byte;
	TWCR = (1<<TWINT) | (1<<TWEN);
	/*
	Wait for ACK
	*/
	while(!(TWCR & (1<<TWINT)));
	/*
	check status
	*/
	if((TWSR & 0xF8) !=MT_SLA_ACK) ERROR();
}

void ds1307_sendbyte(uint8_t address, uint8_t data)
{
	/*
	Send start condition
	DS1307 in receiver mode
	Address()DS receiver
	1101000()0
	*/
	ds1307_start();
	ds1307_writbyte(11010000);
	/*
	Send address of desired register
	Send data to desired register
	Send stop condition
	*/
	ds1307_writbyte(address);
	ds1307_writbyte(data);
	ds1307_stop();
}
void ERROR()
{
	/*
	Will write error MSG on LCD
	*/
}

Im using code from ATmega8 datasheet but there are few things that i dont understand, what are those START and MT_SLA_ACK? Are those supposed to be variables or i should replace them with appropriate constants? I read those are statuses but statuses of what?

Also any advices, something can be done better or changed?

Thank you in advance for help (and in meantime im starting to write data read functions).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a Project here by Jorge Wunsch that does ds1307 as I recall. Search for it?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are many, many implementations of DS1307, and a Mega8 implementation won't be much different if not identical.

General I2C advice: Check your return values.

Have you searched this forum and the site (projects area) for prior discussions?
http://www.lmgtfy.com/?q=+site%3...
https://www.avrfreaks.net/index.p...
https://www.avrfreaks.net/index.p...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

KonradIC13 wrote:
what are those START and MT_SLA_ACK? Are those supposed to be variables or i should replace them with appropriate constants?

START and MT_SLA_ACK are predefined numerical values.
They should have been defined using a #define tatement, if not in the .c file then in a .h file that was included.

TWSR is the Status Register for the Two Wire interface (see 20.8.3 in the data sheet). The five most significant bits define the status of the interface. (TWSR & 0xF8) in the code isolates those 5 bits.

The various values for the TWSR are listed in table 20-3.

Edit: For AVR Studio 5.1 -
TW_START and TW_MT_SLA_ACK are defined in twi.h in folder Atmel\AVR Studio 5.1\extensions\Atmel\AVRGCC\3.3.1.27\AVRToolchain\avr\include\util

Edit 2: TWI example from avr-libc manual -
http://www.nongnu.org/avr-libc/u...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you, that last link was most helpful from all of them.

With help of this:
http://www.nongnu.org/avr-libc/u...
https://www.avrfreaks.net/index.p...

I finished writing code for DS1307. Its reading, its writing its working quite good. There are just two problems that im encountering from time to time.

First one is that sometimes, chip is becoming unresponsive, in my program i have one switch that toggles LED and LCD that displays current time, date. Thats how i know its unresponsive, it does not react to switch and does not update time. To fix it i have to power it down and power it up or reset it (possible workaround, use watchdog timer).

Second problem is DS1307 apparently locking himself up (switching CH to 1) and stopping timecounting operation. To fix it i have to unlock it again by applying function that writes appropriate data to it (as for now it means reprogramming mcu).

What can cause it? Is it because of my code? Or could it be caused by electric interferences (all circuits are in prototype board) caused for example by relays operating from same power source or servomotors?

Below is my main code

/*
 * ds1307other.c
 *
 * Created: 09-02-2013 15:00:41
 *  Author: Konrad
 */ 
#define F_CPU 4000000UL
#include 
#include "ds1307.c"
#include "lcd.c"
#include 



int main(void)
{
	DDRB&=~(1<<PB1);
	PORTB|=(1<<PB1);
	DDRB|=(1<<PB2);
	PORTB&=~(1<<PB2);
	
	lcd_init(LCD_DISP_ON);
	TWI_Init();
	//DS1307_Unlock(true);
	//DS1307_Set(14,29,25,1,10,2,13);
	uint8_t value;
	char Time[9], Date[9];
	char UP;
    while(1)
    {
		UP=(PINB&(1<<PB1));
		if(UP==0)
		{
			PORTB|=(1<<PB2);
		}
		if(UP!=0)
		{
			PORTB&=~(1<<PB2);
		}			
        lcd_gotoxy(0,0);
		DS1307_Read(0x00,&value);
		Time[8]='\0';
		Time[7]=(0x30)+(value & 0b00001111);
		Time[6]=(0x30)+((value & 0b01110000)>>4);
		Time[5]=':';
		DS1307_Read(0x01,&value);
		Time[4]=(0x30)+(value & 0b00001111);
		Time[3]=(0x30)+((value & 0b01110000)>>4);
		Time[2]=':';
		DS1307_Read(0x02,&value);
		Time[1]=(0x30)+(value & 0b00001111);
		Time[0]=(0x30)+((value & 0b00110000)>>4);
		lcd_puts(Time);
		
		lcd_gotoxy(0,1);
		DS1307_Read(0x06,&value);
		Date[8]='\0';
		Date[7]=(0x30)+(value & 0b00001111);
		Date[6]=(0x30)+((value & 0b11110000)>>4);
		Date[5]='.';
		DS1307_Read(0x05,&value);
		Date[4]=(0x30)+(value & 0b00001111);
		Date[3]=(0x30)+((value & 0b00010000)>>4);
		Date[2]='.';
		DS1307_Read(0x04,&value);
		Date[1]=(0x30)+(value & 0b00001111);
		Date[0]=(0x30)+((value & 0b00110000)>>4);
		lcd_puts(Date);
		
		DS1307_Read(0x03,&value);
		lcd_gotoxy(13,1);
		switch(value) {
			case 1:
			lcd_puts("NIE");
			break;
			case 2:
			lcd_puts("PON");
			break;
			case 3:
			lcd_puts("WTO");
			break;
			case 4:
			lcd_puts("SRO");
			break;
			case 5:
			lcd_puts("CZW");
			break;
			case 6:
			lcd_puts("PIA");
			break;
			case 7:
			lcd_puts("SOB");
			break;
		}
		_delay_ms(100);
    }
}

And here functions for DS1307


#include 
#define DS_SLA 0b11010000
#define DS_CH 7
#define DS_24H 6

/************************************************
	DS1307 FUNCTIONS
*************************************************/

void TWI_Init()
{
	/*
	Initiate TWI interface and select
	SCL Clock frequency according to:
	SCLf=FCPU/(16+2*TWBR*(4^TWPS))
	For ATmega8A max freq is 400kHZ
	When choosing SCL freq remember
	that devices attached via TWI interface
	may have limitation (example DS1307 can operate
	on max 100kHZ SCL frequency)
	
	TWPS1|TWPS0|TWPS(prescaler val)
	  0  |  0  |       1
	  0  |  1  |       4
	  1  |  0  |      16
	  1  |  1  |      64
	*/
	TWBR=0b00001000; //TWBR->8
	TWSR&=~(1<<TWPS1);
	TWSR&=~(1<<TWPS0);
	// With FCPU 4MHZ SCL freq is 50kHZ
	TWCR|=(1<<TWEN);
} 

void TWI_Deactivate()
{
	TWCR&=~(1<<TWEN);
}

void TWI_Start()
{
	// Send start condition and wait for finish
	TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
	while(!(TWCR&(1<<TWINT)));
}

void TWI_Stop()
{
	//Send stop condition
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}

bool TWI_WriteByte(uint8_t byte)
{
	TWDR=byte;
	//Start data transfer and wait for finish
	TWCR=(1<<TWINT)|(1<<TWEN);
	while(!(TWCR&(1<<TWINT)));
	/*
	Check if completed successfully one of statuses: SLA+W transmitted
	and ACK received, SLA+R transmitted and ACK received, DATA transmitted
	and ACK received
	*/
	if ((TW_STATUS==TW_MT_SLA_ACK)||(TW_STATUS==TW_MT_DATA_ACK)||(TW_STATUS==TW_MR_SLA_ACK))
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool DS1307_Write(uint8_t address, uint8_t data)
{
	bool result;
	TWI_Start();
	result=TWI_WriteByte((DS_SLA|TW_WRITE));
	if (result==false) return false;
	result=TWI_WriteByte(address);
	if (result==false) return false;
	result=TWI_WriteByte(data);
	if (result==false) return false;
	TWI_Stop();
	return true;
}

bool TWI_ReadByte(uint8_t *data, bool ack)
{
	if(ack==false)
	{
		/*
		Returns NACK after reception
		signals slave to stop giving data
		used for last read byte
		*/
		TWCR&=~(1<<TWEA);
	}
	else
	{
		// Return ACK after reception
		TWCR|=(1<<TWEA);
	}
	//Start data reception and wait for finish
	TWCR|=(1<<TWINT);
	while(!(TWCR&(1<<TWINT)));
	/*
	Check if completed successfully one of statuses: DATA received
	and ACK returned, DATA received and NACK returned
	*/
	if ((TW_STATUS==TW_MR_DATA_ACK)||(TW_STATUS==TW_MR_DATA_NACK))
	{
		*data=TWDR;
		return true;
	}
	else
	{
		return false;
	}
}

bool DS1307_Read(uint8_t address, uint8_t *data)
{
	bool result;
	TWI_Start();
	result=TWI_WriteByte((DS_SLA|TW_WRITE));
	if (result==false) return false;
	result=TWI_WriteByte(address);
	if (result==false) return false;
	TWI_Start();
	result=TWI_WriteByte((DS_SLA|TW_READ));
	if (result==false) return false;
	result=TWI_ReadByte(data,false);
	if (result==false) return false;
	TWI_Stop();
	return true;
}

void DS1307_Unlock(bool onoff)
{
	uint8_t unlock;
	if (onoff==true)
	{
		DS1307_Read(0x00,&unlock);
		unlock&=~(1<<DS_CH);
		DS1307_Write(0x00,unlock);
	}
	else
	{
		DS1307_Read(0x00,&unlock);
		unlock|=(1<<DS_CH);
		DS1307_Write(0x00,unlock);
	}
}

void DS1307_24hMode()
{
	uint8_t mode;
	DS1307_Read(0x02,&mode);
	mode&=~(1<<DS_24H);
	DS1307_Write(0x02,mode);
}

void DS1307_Set(uint8_t hour, uint8_t min, uint8_t sec, uint8_t day, uint8_t date, uint8_t month, uint8_t year)
{
	DS1307_Write(0x00,((sec/10)<<4)+(sec%10)); //seconds=20 //0-59
	DS1307_Write(0x01,((min/10)<<4)+(min%10)); //minute=19 //0-59
	DS1307_Write(0x02,((hour/10)<<4)+(hour%10)); //hour= 18 //0-23
	DS1307_Write(0x03,day); //day=6 //1- Sunday, 2 Monday,... 1-7
	DS1307_Write(0x04,((date/10)<<4)+(date%10)); //date=8 //1-31
	DS1307_Write(0x05,((month/10)<<4)+(month%10)); //month=2 //1-12
	DS1307_Write(0x06,((year/10)<<4)+(year%10)); //year=13 //0- 2000, 01- 2001,... 0-99
}

Also i noticed that my DS1307 circuit clock is faster than my reference clock (from computer). When i synchronise them, DS1307 clock is usually 0.5-1s too late but after ~24 hours its too fast by 2-3 seconds. I hope it will be better when i transfer it to PCB with proper ground planes around DS1307 crystal.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you connected a battery to the Vbat pin?
If not the pin should be grounded.

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Im using DS1307 with backup 3.3v 'button' battery. Battery + is connected to Vbat and battery - is connected to DS1307 ground and MCU gnd. pullup resistors on SCL and SDA are 4.7kOhm

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, I'm making a project that involves a ds1307 and ATmega644 and I was watching what you guys know about it and I saw your code. I dont understand why do you put this "Time[7]=(0x30)+(value & 0b00001111);" to simply obtain the seconds value..Can you explain to me?

For example, in my project I want show the clock in a 4*7 segment display, how do I obtain the minutes and the hours?if I used all your functions?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

The 0x30 is ascii value for the 0 character. 0x35 is the 5 character.
0b00001111 masks the bottom four bits of the seconds. e.g. if the value is 0x45 you would get 0x05.
Add 5 to 0x30 to get the ascii 0x35 i.e. display the "5"
.
Yes, it would be clearer if they said '0' + value instead of 0x30 + value.
.
But it is even simpler if it used sprintf(time, "%02x:%02x:%02x", hour, min, sec);
The DS1307 stores everything in BCD which displays nicely with %x
.
David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
0b00001111 masks the bottom four bits of the seconds

Note that 0b... is a non-standard notation for binary numbers - not all 'C' compilers support it.

 

The standard way would be to use hex - where it would be 0x0F.

 

For a tutorial on this bit masking stuff, see: https://www.avrfreaks.net/forum/tut-c-bit-manipulation-aka-programming-101

 

it would be clearer if they said '0' + value instead of 0x30 + value

+1

 

The DS1307 stores everything in BCD

So what David says applies to anything in BCD - it is not specific to DS1307 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

diogofvc wrote:
"Time[7]=(0x30)+(value & 0b00001111);" to simply obtain the seconds value

Note that it does not obtain the entire seconds value - as David explained, it only obtains the bottom digit of the seconds value

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have this function to write on the 4*7 segment display the values of the minutes and hours:

 

"void digits_on()

{

PORTC = 0b00010000; //Liga o dígito das unidades 

display_num(Tempo[4]); //Mostra o valor que estiver na variável dig4

_delay_ms(5);

PORTC = 0b10000000; //Liga o dígito das dezenas

display_num(Tempo[3]); //Mostra o valor que estiver na variável dig3

_delay_ms(5);

 

PORTC = 0b01000000; //Liga o dígito das centenas

display_num(Tempo[1]); //Mostra o valor que estiver na variável dig2

_delay_ms(5);

PORTC = 0b00100000; //Liga o dígito dos milhares

display_num(Tempo[0]); //Mostra o valor que estiver na variável dig1

_delay_ms(5);

}"

 

My question is: What do I have to put on Tempo[...] to get what I want? display_num type is "display_num(unsigned char number)"   

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From the comments in the code, and presuming we are still talking about time:

Tempo[0] = tens of hours

Tempo[1] = ones of hours

Tempo[3] = tens of minutes

Tempo[4] = ones of minutes

 

Edit: typo

 

David

Last Edited: Sat. Oct 14, 2017 - 12:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

How to properly post source code: https://www.avrfreaks.net/comment...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

diogofvc wrote:
display_num type is "display_num(unsigned char number)" 

And does it expect the 'number' parameter to be an actual number, 0-9, or an ASCII-coded character ?

 

EDIT

 

This now has nothing specifically to do with the DS1307 & hardware TWI - which is the topic of this thread.

 

This would be entirely general for displaying any digit on a 7-segment display.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Sat. Oct 14, 2017 - 12:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void display_num(unsigned char number)
{
	if (tipo_7segment == COMMON_CATHODE)
	{
		if (number == 0)
			port_7segment = 0xFA;
		else if (number == 1)
			port_7segment = 0x22;
		else if (number == 2)
			port_7segment = 0xB9;
		else if (number == 3)
			port_7segment = 0xAB;
		else if (number == 4)
			port_7segment = 0x63;
		else if (number == 5)
			port_7segment = 0xCB;
		else if (number == 6)
			port_7segment = 0xDB;
		else if (number == 7)
			port_7segment = 0xA2;
		else if (number == 8)
			port_7segment = 0xFB;
		else if (number == 9)
			port_7segment = 0xEB;
	}
}

this is my "display_num" function, what I would like to know is what do I have to put in Tempo[0] (tens of hours), instead of "(0x30)+((value & 0b00110000)>>4)"

thanks for your dedication so far, and I'm sorry for my english not so good, I'm portuguese

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

0x00 for zero
0x01 for one
...
0x09 for nine

Edit: assuming that function is outputting correctly for the segments.

David

Last Edited: Sat. Oct 14, 2017 - 11:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

why did you commented these two lines?

//DS1307_Unlock(true);

	//DS1307_Set(14,29,25,1,10,2,13);

the program need them doesnt it?