Author Topic: ATMega128A I2C code  (Read 7715 times)

0 Members and 1 Guest are viewing this topic.

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
ATMega128A I2C code
« on: July 01, 2013, 10:06:47 pm »
hello, i have been looking into the I2C bus over the last few days on an ATMega128A and i ahve come up with this code to read and write the time off of a DS1307 RTC ic. i mainly want to check to see if i have done it correctly as the debug is not showing any errors.

also is there a way of using a stimuli file to mimic a DS1307 in the atmel studio simulator so that i can test it? (i currently don't have the ATMega128A yet :D

Code: [Select]
/*
 * NixieTubeClockRev2_1_3.c
 *
 * Created: 30/06/2013 16:58:31
 *  Author: carbon dude oxide
 */

#define F_CPU 20000000UL
#define DS1307_R 0xd0
#define DS1307_W 0xd1

int second,minute,hour,day,date,month,year = 0x00;

#define second_W 0x00 //temporary second to write
#define minute_W 0x00 //temporary minute to write
#define hour_W 0x00 //temporary hour to write
#define day_W 0x00 //temporary day to write
#define date_W 0x00 //temporary date to write
#define month_W 0x00 //temporary month to write
#define year_W 0x00 //temporary year to write
#define control_W 0x10 //set RTC to output a 1Hz pulse on SQW/OUT pin to write

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <util/twi.h>

int debounce = 0;
int mode = 0;

int sec1,sec2,min1,min2,hou1,hou2,day1,dat1,dat2,mon1,mon2,yea1,yea2 = 0x00;

unsigned char i2c_read_ack()
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
while ((TWCR & (1<<TWINT)) == 0);
if ((TWSR & 0xF8) != TW_MR_DATA_ACK) i2c_stop(); //data has been received and send acknowledge bit

return TWDR;
}

unsigned char i2c_read_nack()
{
TWCR = (1<<TWINT) | (1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);
if ((TWSR & 0xF8) != TW_MR_DATA_NACK) i2c_stop(); //data has been received and did not send acknowledge bit.

return TWDR;
}

unsigned char i2c_write( unsigned char data)
{
TWDR = data;
TWCR = (1 << TWINT) | (1 << TWEN); //send address data packet
while (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is received
if ((TWSR & 0xF8) != TW_MT_SLA_ACK) {i2c_stop(); return 0x01;} //has data packet been acknowledged? if not goto error.

return 0x00;
}

unsigned char i2c_start()
{
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); //send start condition
while (!(TWCR & (1 << TWINT))); //wait until start condition has been sent
if ((TWCR & 0xF8) != TW_START) i2c_stop(); return 0x01;//if the status register is not TW_START go to error
return 0x00;
}

unsigned char i2c_stop()
{
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); //send stop condition
}

void readTime()
{
//read time from RTC
if (i2c_start() == 0x01) {i2c_stop(); return;}
if (i2c_write(DS1307_R) == 0x01) {i2c_stop(); return;}
second = i2c_read_ack();
minute = i2c_read_ack();
hour = i2c_read_ack();
day = i2c_read_ack();
date = i2c_read_ack();
month = i2c_read_ack();
year = i2c_read_nack();

i2c_stop(); //send stop condition
}
void updateTime()
{
if (i2c_start() == 0x01) {i2c_stop(); return;}
if (i2c_write(DS1307_W) == 0x01) {i2c_stop(); return;}
if (i2c_write(second_W) == 0x01) {i2c_stop(); return;}
if (i2c_write(minute_W) == 0x01) {i2c_stop(); return;}
if (i2c_write(hour_W) == 0x01) {i2c_stop(); return;}
if (i2c_write(day_W) == 0x01) {i2c_stop(); return;}
if (i2c_write(date_W) == 0x01) {i2c_stop(); return;}
if (i2c_write(month_W) == 0x01) {i2c_stop(); return;}
if (i2c_write(year_W) == 0x01) {i2c_stop(); return;}
if (i2c_write(control_W) == 0x01) {i2c_stop(); return;}

i2c_stop(); //send stop condition
}

int main(void)
{
DDRA = 0xFF; //digits 3 and 4 BCD lines
DDRB = 0xFF; //digits 5 and 6 BCD lines
DDRC = 0xFF; //digits 7 and 8 BCD lines
//DDRD I2C and UART bus interfaces
DDRE = (1 << PORTE3) | (1 << PORTE4); // nixie tube power and led pwm lines
DDRF = 0xFF; //digits 1 and 2 BCD lines
//DDRG
PORTE = (1 << PORTE6); // enable internal pull up resistor on PE6

TWBR = 0x17; // set bit rate register to 23 to reduce the I2C SCL to 100KHz

TCCR3A = (1 << WGM31) | (1 << WGM30) | (1 << CS31) | (1 << CS30); //set to fast pwm and pre scaler to 64 (1KHz ish)
TCCR3B = (1 << WGM31) | (1 << WGM30) | (1 << CS31) | (1 << CS30); //set to fast pwm and pre scaler to 64 (1KHz ish)

OCR3A = 255; // set to 100% duty cycle
OCR3B = 255; // set to 100% duty cycle

EIMSK = (1 << INT6); // set up external interrupt from 1Hz pulse from RTC

updateTime();
    while(1)
    {
if (debounce == 0) {sei();} else {cli(); _delay_ms(750); debounce = 0;} //debounce for interrupt from the 1Hz pulse from RTC
    }
}

ISR(INT6_vect)
{
if (mode == 0)
{
readTime();

min1 = (minute & 0x0F);
min2 = ((minute >> 4) & 0x07);

PORTE = hour;
PORTA = min2;
PORTB = (min1 << 4);
PORTC = second;
debounce = 1;
}
}

edit: added a not in there
« Last Edit: July 01, 2013, 10:16:19 pm by carbon dude oxide »
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline c4757p

  • Super Contributor
  • ***
  • Posts: 7799
  • Country: us
  • adieu
Re: ATMega128A I2C code
« Reply #1 on: July 01, 2013, 10:10:55 pm »
as the debug is showing any errors.

"is showing many" or "isn't showing any"? And if it's showing errors, what are they?
No longer active here - try the IRC channel if you just can't be without me :)
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: ATMega128A I2C code
« Reply #2 on: July 01, 2013, 10:15:54 pm »
is not showing any errors, sorry i did not notice that :D
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline c4757p

  • Super Contributor
  • ***
  • Posts: 7799
  • Country: us
  • adieu
Re: ATMega128A I2C code
« Reply #3 on: July 01, 2013, 10:36:23 pm »
OK. I'll just read along and comment as I go:

- readTime() --- you do check for an error on start and address select, but then you just return, with no indication that the read failed. Obviously in a real clock, it's not going to fail, and if it does, what would you even do? But during development, you want to know if there's an error. May I offer a suggestion? Dump error messages to the UART at some very slow "can't go wrong" baud rate. If something goes wrong, just whack one of those FTDI things on the line, or a logic analyzer or scope with serial decode features.

- Same with updateTime(). Don't you at least want to return an error code?

- EIMSK - have you considered edge polarity? Not sure if it matters here, just checking, since there's no code or comment to that effect.

- Not 100% sure what's going on with the debounce line. Are you debouncing for 750ms? That's really long...

- What are the globals min1...etc for? I only see min1/min2 being used, in the ISR. If that's how they're always going to be, declare them in the ISR. Globals are confusing.

- I'm not sure what the ISR has to do with the debounce variable.  You should explain that.
No longer active here - try the IRC channel if you just can't be without me :)
 

Offline c4757p

  • Super Contributor
  • ***
  • Posts: 7799
  • Country: us
  • adieu
Re: ATMega128A I2C code
« Reply #4 on: July 01, 2013, 10:37:44 pm »
The sequence of I2C commands looks right to me, but - grain of salt warning - I don't do much with I2C.
No longer active here - try the IRC channel if you just can't be without me :)
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: ATMega128A I2C code
« Reply #5 on: July 01, 2013, 10:58:44 pm »
yea its only reading a DS1307 every second.

the interrupt is from the SQW/OUT pin on the DS1307 with a frequency of 1Hz

once it goes low it will call the interupt and read the time.

i am setting up UART next so i can return errors once i have done that (should have done it first :)

the de-bouncing is just to make sure on the SQW/OUT pin is a single shot per second, it was meant to be 75 not 750.

the globals were for something i tried before then deleted aload of code as it was not needed, just forgot to delete those vars.
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: ATMega128A I2C code
« Reply #6 on: July 01, 2013, 11:07:29 pm »
Actualy instead of reading the DS1307 i could just use the 1Hz interupt to add 1 second to the time instead of constantly reading the time from the DS1307, and just syncing it every few hours or so.
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline c4757p

  • Super Contributor
  • ***
  • Posts: 7799
  • Country: us
  • adieu
Re: ATMega128A I2C code
« Reply #7 on: July 01, 2013, 11:11:28 pm »
Why add complexity? You don't wear out the DS1307 if you read it too much...
No longer active here - try the IRC channel if you just can't be without me :)
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: ATMega128A I2C code
« Reply #8 on: July 02, 2013, 08:11:47 am »
grain of salt warning - I don't do much with I2C.

but you said not to do much with I2C :D

do you mean from now on in future projects or now in this dont use the bus too much?
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: ATMega128A I2C code
« Reply #9 on: July 02, 2013, 04:28:54 pm »
how acurate is the internal RTC clock on the ATMega128?
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: ATMega128A I2C code
« Reply #10 on: July 02, 2013, 09:15:14 pm »
how acurate is the internal RTC clock on the ATMega128?

sorry i forgot to mention would it be easyer to use the internal clock or would it be better to use the external DS1307?
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline c4757p

  • Super Contributor
  • ***
  • Posts: 7799
  • Country: us
  • adieu
Re: ATMega128A I2C code
« Reply #11 on: July 02, 2013, 09:24:15 pm »
do you mean from now on in future projects or now in this dont use the bus too much?

I said I don't use it much.

how acurate is the internal RTC clock on the ATMega128?

Well, it doesn't have a dedicated one. If you implement it in software it's as accurate as the crystal you're timing it with, which depends on what you've bought and how properly you've connected it. (Note that the value of the capacitors affects the frequency as well.)

sorry i forgot to mention would it be easyer to use the internal clock or would it be better to use the external DS1307?

I'd say they're about equal. Myself, I'd probably go for an internal clock rather than external. One advantage of the DS1307 is that it can keep going on quite low power on a backup battery. Many of the older microcontrollers use a good bit of power when the clock is running constantly. Some of the newer ones (not the ATmega128) have a dedicated secondary oscillator for this purpose that uses less power.
No longer active here - try the IRC channel if you just can't be without me :)
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: ATMega128A I2C code
« Reply #12 on: July 02, 2013, 10:49:16 pm »
hmmm, i see :) thanks
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf