ATmega88 I2C problem

For Adafruit customers who seek help with microcontrollers

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
luck93
 
Posts: 3
Joined: Mon Oct 21, 2013 12:52 am

ATmega88 I2C problem

Post by luck93 »

Hello, I'm trying to comunicate with another device using the ATmega88 I2C, I can write very well to the device, but when i try to read i send the stop bit and doesn't work at all, the sda should become high during the clock but it doesn't, so it also has problems during the next start bit.
Why does this happen?

mwilson
 
Posts: 46
Joined: Sun Oct 23, 2011 11:17 am

Re: ATmega88 I2C problem

Post by mwilson »

Something in your code. Without seeing your code, nobody can say. FWIW I've had good results working directly with the TWI hardware with the code below. It worked with a DS1307 time-of-day clock, and it illustrates how you write an internal device address, then read back from that address. The magic symbols (except the bus addresses DS1307_W and DS1307_R) are from the AVR Freaks include files.

Code: Select all

#define bit_set(F,B)	F |= (1<<(B))
#define bit_clr(F,B)	F &= ~(1<<(B))
#define bit_tst(F,B)	(F & (1<<(B)))

static unsigned char twi_op (unsigned char op)
{
	TWCR = op;
	while (! bit_tst (TWCR, TWINT)) ;	// wait for TWINT
	return TWSR & 0xF8;
} // twi_op

#define TWI_OP	(1<<TWINT | 1<<TWEN)

static int ds1307_read_buf (unsigned char addr, unsigned char *buf, unsigned char n)
// Read a block of bytes from a given address in the DS1307
{
	int result = -1;
	int j;
	if (twi_op (TWI_OP | 1<<TWSTA) != 0x08)	// START condition
		goto bailout;
	
	TWDR = DS1307_W;		// WRITE command
	if (twi_op (TWI_OP) != 0x18)	goto bailout;

	TWDR = addr;	// DS1307 address
	if (twi_op (TWI_OP) != 0x28)	goto bailout;

	if (twi_op (TWI_OP | 1<<TWSTA) != 0x10)	// Repeated START
		goto bailout;
	TWDR = DS1307_R;			// READ command
	if (twi_op (TWI_OP) != 0x40)	goto bailout;
	
	for (j=0; j < n-1; j++) {		// read all bytes except the last with following ACK
		if (twi_op (TWI_OP | 1<<TWEA) != 0x50)	goto bailout;
		buf[j] = TWDR;
	}
	if (twi_op (TWI_OP) != 0x50)	goto bailout;	// read the last byte without ACK
	buf[n-1] = TWDR;
	result = 0;
bailout:	
	TWCR = TWI_OP | 1<<TWSTO;
	return result;
} // ds1307_read_buf

luck93
 
Posts: 3
Joined: Mon Oct 21, 2013 12:52 am

Re: ATmega88 I2C problem

Post by luck93 »

readwrite=0 write
readwrite=1 read 1 register
readwrite=2 read 2 registers

I can write without problem, when i read 1 register i can read but i haven't the stop bit and if i try to read 2 registers the program stop work.

Code: Select all

#define START 0xA4
#define SEND 0x84
#define GET 0xC4
#define STOP 0x94
#define TWEN 0x04 
#define TWINT 0x80
#define SLA 0x13
#define READ 0x01
#define WRITE 0x00
#define TXC 0x40
#define NULL 0x00


void iquadratoc(char registro,char dato,char readwrite){ 
 TWCR=TWEN;
 error=0;
 TWCR=START;
 while((TWCR&TWINT)!=TWINT);
 if((TWSR&0xF8)!=0x08)error=TWSR;
 TWDR=SLA<<1|WRITE;
 TWCR=SEND;
 while((TWCR&TWINT)!=TWINT);
 if((TWSR&0xF8)!=0x18)error=TWSR; 
 TWDR=registro;
 TWCR=SEND;
 while((TWCR&TWINT)!=TWINT); 
 if((TWSR&0xF8)!=0x28)error=TWSR;
 if(readwrite==0){
   TWDR=dato;
   TWCR=SEND;
   while((TWCR&TWINT)!=TWINT); 
   if((TWSR&0xF8)!=0x28)error=TWSR; 
   } 
 if(readwrite>0){ 
   TWCR=START;
   while((TWCR&TWINT)!=TWINT);
   if((TWSR&0xF8)!=0x10)error=TWSR;
   TWDR=SLA<<1|READ;
   TWCR=SEND;
   while((TWCR&TWINT)!=TWINT); 
   if((TWSR&0xF8)!=0x40)error=TWSR;
   TWCR=GET;
   while((TWCR&TWINT)!=TWINT);   
   if((TWSR&0xF8)!=0x50)error=TWSR;
   status=TWDR;
   TWCR=TWEN;
   if(readwrite==2){    
     TWCR=START;
     while((TWCR&TWINT)!=TWINT);
     if((TWSR&0xF8)!=0x10)error=TWSR;
     TWDR=SLA<<1|READ;
     TWCR=SEND;
     while((TWCR&TWINT)!=TWINT); 
     if((TWSR&0xF8)!=0x40)error=TWSR;
     TWCR=GET;
     while((TWCR&TWINT)!=TWINT);   
     if((TWSR&0xF8)!=0x50)error=TWSR;
     status2=TWDR;
     }
   } 
 TWCR=STOP; 
 if(error!=0){
   UDR0=error;
   while((UCSR0A&0x40)==0);
   UCSR0A=UCSR0A|TXC;
   }
 TWCR=NULL;
 } 

mwilson
 
Posts: 46
Joined: Sun Oct 23, 2011 11:17 am

Re: ATmega88 I2C problem

Post by mwilson »

On a quick read-through it looks like one problem is at

Code: Select all

if(readwrite==2){   
     TWCR=START;
     while((TWCR&TWINT)!=TWINT);
where you do another repeated START after reading the first byte. Don't do that. Do your reads with TWEA set to enable ACK to continue the series of reads. Make sure that TWEA is reset on the last read.

That may fix things up.

Comment on style: don't be afraid of comments and whitespace. They can help you distinguish the sections of code in your mind, and help you focus your attention while you debug. After I'd finished "reading" it, the code looked like this:

Code: Select all

    #define START	0xA4	// TWCR: TWINT | TWSTA | TWEN
    #define SEND	0x84	// TWCR: TWINT | TWEN
    #define GET		0xC4	// TWCR: TWINT | TWEA | TWEN
    #define STOP	0x94	// TWCR: TWINT | TWSTO | TWEN
    // bits in TWCR:
    #define TWEN	0x04
    #define TWINT	0x80
    
    #define SLA		0x13	// I2C slave address unshifted
    #define READ	0x01
    #define WRITE	0x00
    
    #define TXC		0x40
    #define NULL	0x00


void iquadratoc (char registro, char dato, char readwrite)
{
	TWCR = TWEN;
	error = 0;
	TWCR = START;	// TWCR: TWINT | TWSTA | TWEN
	while ((TWCR & TWINT) != TWINT);
	if ((TWSR&0xF8) != 0x08)
	     error = TWSR;
	
	TWDR = SLA<<1 | WRITE;	// slave write command
	TWCR = SEND;	// TWCR: TWINT | TWEN
	while ((TWCR & TWINT) != TWINT);
	if ((TWSR&0xF8) != 0x18)
	     error = TWSR;
	
	TWDR = registro;	// write the slave internal address
	TWCR = SEND;	// TWCR: TWINT | TWEN
	while ((TWCR & TWINT) != TWINT);
	if ((TWSR&0xF8) != 0x28)
	     error = TWSR;
	
	if (readwrite == 0) {	// want to write one byte
		TWDR = dato;
		TWCR = SEND;	// TWCR: TWINT | TWEN
		while ((TWCR & TWINT) != TWINT);
		if ((TWSR&0xF8) != 0x28)
			error = TWSR;
	}
	if (readwrite > 0) {	// want to read one or more bytes
		TWCR = START;	// TWCR: TWINT | TWSTA | TWEN
		while ((TWCR & TWINT) != TWINT);
		if ((TWSR&0xF8) != 0x10)
			error = TWSR;
		
		TWDR = SLA<<1 | READ;	// slave read command
		TWCR = SEND;
		while ((TWCR & TWINT) != TWINT);
		if ((TWSR&0xF8) != 0x40)
			error = TWSR;
		
		TWCR = GET;	// TWCR: TWINT | TWEA | TWEN
		while ((TWCR & TWINT) != TWINT);   
		if ((TWSR&0xF8) != 0x50)
		       error = TWSR;
		
		status = TWDR;
		TWCR = TWEN;	// ???
		if (readwrite == 2) {	// want to read 2 bytes
			TWCR = START;	// TWCR: TWINT | TWSTA | TWEN
			while ((TWCR & TWINT) != TWINT);
			if ((TWSR&0xF8) != 0x10)
				error = TWSR;
			 
			TWDR = SLA<<1 | READ;
			TWCR = SEND;	// TWCR: TWINT | TWEN
			while ((TWCR & TWINT) != TWINT);
			if ((TWSR&0xF8) != 0x40)
				error = TWSR;
			 
			TWCR = GET;	// TWCR: TWINT | TWEA | TWEN
			while ((TWCR & TWINT) != TWINT);   
			if ((TWSR&0xF8) != 0x50)
				error = TWSR;
			status2 = TWDR;
		 }
	}
	TWCR = STOP;	// TWCR: TWINT | TWSTO | TWEN
	if (error != 0) {	// send the error status through the UART
		UDR0 = error;
		while ((UCSR0A & 0x40) == 0);
		UCSR0A = UCSR0A | TXC;
	}
	TWCR = NULL;	// disable TWI interface
} 

luck93
 
Posts: 3
Joined: Mon Oct 21, 2013 12:52 am

Re: ATmega88 I2C problem

Post by luck93 »

The row that you commented "????" is the one that resets the TWEA

Locked
Please be positive and constructive with your questions and comments.

Return to “Microcontrollers”