Reading from EEPROM using AVR I2C

Thread Starter

Rafael Adel

Joined Aug 7, 2015
11
I'm quite a beginner in embedded systems. I was trying to create a simple system that consists of two ATMega32 micro controllers.

One of them stores a value in an EEPROM and the others reads it from the EEPROM and lights a led depending on this value.

I've done the writing part successfully. But the reading part (the other micro controller) is not working and I don't know exactly whats wrong.

Unfortunately I don't know what's exactly wrong so I'll post the schema and errors. (I've included the files too, if you wanted to test it yourself).

Here's my circuit in Proteus (system is not 100% complete, only writing a value and reading is written in code):



Here's my code: (MCU 1 - writing)
Code:
/*
* basic.c
*
* Created: 12/23/2015 1:43:21 PM
* Author : Rafael
*/

#include <avr/io.h>

char i2c_send_start();
char i2c_send_addr(char);
char i2c_send_data(char);
char i2c_send_stop();
char i2c_check_status(char);

int main(void)
{
    DDRA &= (~(1 << PA0) & ~(1 << PA5));
    while (1)
    {
        if(PINA & (1 << PA0))
        {
            while(PINA & (1 << PA0)){}
            if(i2c_send_start())
            {
                if(i2c_send_addr(0xa0))
                {
                    i2c_send_data(0x00);
                    if(i2c_send_data(0x01))
                    {
                        i2c_send_stop();
                    }
                }
            }
        }
    }
}

char i2c_send_start()
{
    TWCR |= (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);

    while(!(TWCR & (1 << TWINT))){}
   
    return i2c_check_status(0x08);

}

char i2c_send_addr(char addr)
{
    TWDR = addr;
    TWCR &= ~(1 << TWSTA);
    TWCR |= (1 << TWINT) | (1 << TWEN);


    while(!(TWCR & (1 << TWINT))){}
   
    return i2c_check_status(0x18);

}


char i2c_send_data(char data)
{
    TWDR = data;

    TWCR |= (1 << TWINT) | (1 << TWEN);


    while(!(TWCR & (1 << TWINT))){}
   
    return i2c_check_status(0x28);
}

char i2c_send_stop()
{
    TWCR |= (1 << TWSTO) | (1 << TWEN);

    //start
    TWCR |= (1 << TWINT);

    while(!(TWCR & (1 << TWINT))){}

    return 1;
}

char i2c_check_status(char status)
{
    if((TWSR & 0xf8) == status) {
        return 1;
    } else {
        return 0;
    }
}
(MCU 2 - reading) :
Code:
/*
* receiver.c
*
* Created: 12/23/2015 1:43:21 PM
* Author : Rafael
*/

#include <avr/io.h>

char i2c_send_start();
char i2c_send_addr(char);
void i2c_send_data(char);
char i2c_receive_data();
char i2c_send_stop();
char i2c_check_status(char);

int main(void)
{
    DDRA &= ~(1 << PA0);
    DDRD = (1 << PD0) | (1 << PD3);
    char status = '\0';
    while (1)
    {
        if(PINA & (1 << PA0))
        {
            while(PINA & (1 << PA0)){}
            if(i2c_send_start())
            {
                if(i2c_send_addr(0xa1))
                {
                    i2c_send_data(0x00);
                    status = i2c_receive_data();
                    i2c_send_stop();
                   
                   
                }
            }
            if(0x01 == status) {
                PORTD = 1 << PD0;
            } else if(0x02 == status) {
                PORTD = 1 << PD3;
            }
        }
    }
}

char i2c_send_start()
{
    TWCR |= (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);

    while(!(TWCR & (1 << TWINT))){}
   
    return i2c_check_status(0x08);

}

char i2c_send_addr(char addr)
{
    TWDR = addr;
    TWCR &= ~(1 << TWSTA);
    TWCR |= (1 << TWINT) | (1 << TWEN);


    while(!(TWCR & (1 << TWINT))){}
   
    return i2c_check_status(0x40);

}

void i2c_send_data(char data)
{
    TWDR = data;

    TWCR |= (1 << TWINT) | (1 << TWEN);


    while(!(TWCR & (1 << TWINT))){}
   
}


char i2c_receive_data()
{
    TWCR |= (1 << TWINT) | (1 << TWEN);

    while(!(TWCR & (1 << TWINT))){}
    return TWDR;
   
}

char i2c_send_stop()
{
    TWCR |= (1 << TWSTO) | (1 << TWEN);

    //start
    TWCR |= (1 << TWINT);

    while(!(TWCR & (1 << TWINT))){}

    return 1;
}

char i2c_check_status(char status)
{
    if((TWSR & 0xf8) == status) {
        return 1;
    } else {
        return 0;
    }
}
 

Attachments

sailorjoe

Joined Jun 4, 2013
365
I don't know if the proteus simulator needs the pull-up resistors or not.
Seems like you're trying to do all the bit fiddling directly in your code. You may have it all correct, but it takes a lot of time to reverse engineer it, so I'll try to avoid that. Have you considered using an Atmel libc I2C library routine instead of home grown? One of the issues you'll face in these kinds of setups are interrupts from the clock or timers that interfere with the loops you have in your main() and subroutines. Well done libraries take these issues into account and avoid problems.
Your proteus readout looks like it transferred 306 bytes successfully. Then it got an error. Are the bytes received correct, or garbage? Are they all the same, or random values?
What does the proteus documentation tell you about the error number and message?
http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__twi__demo.html
 

Thread Starter

Rafael Adel

Joined Aug 7, 2015
11
Where are the pull-up resistors for SCL and SDA?
And you should have at least 4K7 pull-up resistors.
I think Protues takes care of this.

Each I2C IC needs to have its own address. Are you dealing with that?
Yes. In writing I send two bytes as the address for EEPROM. The first one is 0xa0 and the seconde is 0x00. the 1010 in the first byte is the id of the EEPROM. the rest is the memory location inside it as well as the writing bit.

The same goes to reading. I'm sending two byes, 0xa1 and 0x00. Just the same as writing except that i'm enabling reading instead of writing.
 

Thread Starter

Rafael Adel

Joined Aug 7, 2015
11
I don't know if the proteus simulator needs the pull-up resistors or not.
Seems like you're trying to do all the bit fiddling directly in your code. You may have it all correct, but it takes a lot of time to reverse engineer it, so I'll try to avoid that. Have you considered using an Atmel libc I2C library routine instead of home grown? One of the issues you'll face in these kinds of setups are interrupts from the clock or timers that interfere with the loops you have in your main() and subroutines. Well done libraries take these issues into account and avoid problems.
Your proteus readout looks like it transferred 306 bytes successfully. Then it got an error. Are the bytes received correct, or garbage? Are they all the same, or random values?
What does the proteus documentation tell you about the error number and message?
http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__twi__demo.html
I thing bytes are not received correctly.
I was writing: 0x01 and i've read 0xff.
The problem is that in writing, I have to send two bytes as address 0xa0 and 0x00. And in reading I've to send 0xa1 and 0x00. But I think I'm sending the SLA+R incorrectly it gets mixed up with the actual data or something.
 

Thread Starter

Rafael Adel

Joined Aug 7, 2015
11
Ok, I got it working. Or at least got the data written by the first MCU.

I had to write SLA+W first then send a REPEATED START and send SLA+R and then read the data.

Here's the changes code inside the MCU2:
Code:
int main(void)
{
    DDRA &= ~(1 << PA0);
    DDRD = (1 << PD0) | (1 << PD3);
    unsigned char *status = malloc(sizeof(unsigned char));;
    while (1)
    {
        if(PINA & (1 << PA0))
        {
            while(PINA & (1 << PA0)){}
            if(i2c_send_start(0x08))
            {
                if(i2c_send_addr(0xa0, 0x18))
                {
                    i2c_send_data(0x00);
                
                    //Repeated start
                    if(i2c_send_start(0x10)) {
                        if(i2c_send_addr(0xa1, 0x40)) {

                            if(i2c_receive_data(status)){
                                if(0x05 == *status) {
                                    PORTD = 1 << PD0;
                                } else if(0x10 == *status) {
                                    PORTD = 1 << PD3;
                                }
                                i2c_send_stop();
                            }
                        }
                    }    
        
                }
            }
        
        
        }
    }
}

char i2c_send_start(char cond)
{
    TWCR |= (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);

    while(!(TWCR & (1 << TWINT))){}

    return i2c_check_status(cond);

}

char i2c_send_addr(char addr, char cond)
{
    TWDR = addr;
    TWCR &= ~(1 << TWSTA);
    TWCR |= (1 << TWINT) | (1 << TWEN);


    while(!(TWCR & (1 << TWINT))){}

    return i2c_check_status(cond);

}

void i2c_send_data(char data)
{
    TWDR = data;

    TWCR |= (1 << TWINT) | (1 << TWEN);


    while(!(TWCR & (1 << TWINT))){}

}
But still getting an error inside Proteus log:

PC=0x00AE. [AVR TWI] Internal error #1 [U1]
 

sailorjoe

Joined Jun 4, 2013
365
The error is troubling, because it *could* represent a bug in proteus. Recommend you talk to them about it.
In this article https://en.m.wikipedia.org/wiki/I²C there is a section called "Messaging example: 24c32 EEPROM" that describes some of the difficulties with messaging an eeprom. Something here might clear things up for you.
I think you're on the right track, but your solution of writing, restarting, and then reading is more complicated than it should be. You should be able to send an address and a read bit and get the returned byte immediately.
Also, take a look at this datasheet, particularly fig 5.1. http://pdf1.alldatasheet.com/datash...4AA16/+0J__2_VKhwSP.KLufvwIKCH+/datasheet.pdf
Maybe your repeat start routine is actually correct.
 
Last edited:
Top