ATMega128A I2C code - Page 1
Forum section: electronics / general

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: July 01, 2013, 10:06:47 pm
content: 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 yetCode:/** 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 0xd1int 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 bitreturn 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 packetwhile (!(TWCR & (1 << TWINT))); //wait until ACK/NACK is receivedif ((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 conditionwhile (!(TWCR & (1 << TWINT))); //wait until start condition has been sentif ((TWCR & 0xF8) != TW_START) i2c_stop(); return 0x01;//if the status register is not TW_START go to errorreturn 0x00;}unsigned char i2c_stop(){TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); //send stop condition}void readTime(){//read time from RTCif (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 linesDDRB = 0xFF; //digits 5 and 6 BCD linesDDRC = 0xFF; //digits 7 and 8 BCD lines//DDRD I2C and UART bus interfacesDDRE = (1 << PORTE3) | (1 << PORTE4); // nixie tube power and led pwm linesDDRF = 0xFF; //digits 1 and 2 BCD lines//DDRGPORTE = (1 << PORTE6); // enable internal pull up resistor on PE6TWBR = 0x17; // set bit rate register to 23 to reduce the I2C SCL to 100KHzTCCR3A = (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 cycleOCR3B = 255; // set to 100% duty cycleEIMSK = (1 << INT6); // set up external interrupt from 1Hz pulse from RTCupdateTime();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

author: c4757p
author_rank: Super Contributor
timestamp: July 01, 2013, 10:10:55 pm
content: Quote from: carbon dude oxide on July 01, 2013, 10:06:47 pm"is showing many" or "isn't showing any"? And if it's showing errors, what are they?

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: July 01, 2013, 10:15:54 pm
content: is not showing any errors, sorry i did not notice that

author: c4757p
author_rank: Super Contributor
timestamp: July 01, 2013, 10:36:23 pm
content: 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 theminthe ISR. Globals are confusing.- I'm not sure what the ISR has to do with the debounce variable. You should explain that.

author: c4757p
author_rank: Super Contributor
timestamp: July 01, 2013, 10:37:44 pm
content: The sequence of I2C commands looks right to me, but - grain of salt warning - I don't do much with I2C.

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: July 01, 2013, 10:58:44 pm
content: yea its only reading a DS1307 every second.the interrupt is from the SQW/OUT pin on the DS1307 with a frequency of 1Hzonce 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 firstthe 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.

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: July 01, 2013, 11:07:29 pm
content: 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.

author: c4757p
author_rank: Super Contributor
timestamp: July 01, 2013, 11:11:28 pm
content: Why add complexity? You don't wear out the DS1307 if you read it too much...

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: July 02, 2013, 08:11:47 am
content: Quote from: c4757p on July 01, 2013, 10:37:44 pmbut you said not to do much with I2Cdo you mean from now on in future projects or now in this dont use the bus too much?

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: July 02, 2013, 04:28:54 pm
content: how acurate is the internal RTC clock on the ATMega128?

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: July 02, 2013, 09:15:14 pm
content: Quote from: carbon dude oxide on July 02, 2013, 04:28:54 pmsorry i forgot to mention would it be easyer to use the internal clock or would it be better to use the external DS1307?

author: c4757p
author_rank: Super Contributor
timestamp: July 02, 2013, 09:24:15 pm
content: Quote from: carbon dude oxide on July 02, 2013, 08:11:47 amI saidIdon't use it much.Quote from: carbon dude oxide on July 02, 2013, 04:28:54 pmWell, 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.)Quote from: carbon dude oxide on July 02, 2013, 09:15:14 pmI'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.

author: carbon dude oxide
author_rank: Frequent Contributor
timestamp: July 02, 2013, 10:49:16 pm
content: hmmm, i seethanks