Help reading AT24C512 serial eeprom module

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

Hello,
I am working with an ATMEGA16 and an AT24C512 serial eeprom chip. I am able to write to the chip successfully (at least according to the status codes - no errors). But reading from the chip has been difficult. I have a couple of questions. This first is for a current address read. The order of operations is this:

1. Send start from uC
2. Wait for start acknowledge from eeprom
3. Send device address for slave read (master transmit mode)
4. Wait for device acknowledge

This is where I am lost. When does the uC know that the data register has been loaded? Can I assume that once the device acknowledge has been sent that the data is already in the register?

Second question is for a sequential address read. Order of operations is the same as steps 1-4 as noted above. Except...after the uC receives the first data byte, there is an acknowledge to be sent from the uC to the eeprom. This is how the eeprom knows to load another value into the TWDR register. So, how does this work? What register is loaded with what value in the ATMEGA16 which will be used to acknowledge the eeprom?

thanks for your help.

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

There are many examples of code for a 24Cxx eeprom. The 24C512 needs a two-byte address_register instead of the single byte of the smaller 24Cxx eeproms.

Once the address_register is set all reads or writes increment automatically. So you can read forever if you wish. However you MUST clear TWEA for the last byte that you read.

David.

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

Thanks David. So I assume that you are familiar with the chip. So the question is... when can the uC read the TWDR register? How does the uC know when the next byte is ready to read? Somewhere in my code I have to put in a line that says data=TWDR. I need to know how to time this so that I am getting the new data as it is being clocked in.

thanks

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

Try downloading example code by googling "Fleury TWI" or "Procyon TWI" or just reading the Atmel application notes.

The AVR data sheets also show the sequences.

As far as the Slave is concerned once it has received a SLAVE_R address, it knows that it is going to return the next data to you.

Study the working example code, and THEN ask quesions about any part you do not understand.

David.

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

David,
Read the question, THEN try to answer it.

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

Quote:
Read the question, THEN try to answer it.

This isn't the attitude that a person asking for help should take.
Knowledgable viewers seeing this may decide to not get involved.

Rather than directly answering a question, many of us try to direct inquirers to information sources
where the answer to the question may be found along with an explanation of operation that will
give the inquirer a better understanding, which may help with issues developing later in a project.

We do this >>hoping<< that the inquirer will gain skills in researching to be able to find answers
without asking for help. Being dependent on others for solutions is not very satisfying.

Stan

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

Stan,
Point well taken. My response was rude. But you probably shouldn't assume so much about the questioner.

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

Is this a double post? I'm sure I answered this elsewhere. :roll:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Hello,
If you have experience with this chip, please take a moment to look at my code below. I am trying to read data from a serial eeprom AT24C512. All of the status codes indicate that my code is working properly. This is also true for my WRITE function. But when I try to read, I get a value of FF for every memory location. I have included my WRITE code and my READ code. Can someone please explain what I am missing in my READ code? Any help would be appreciated.

PS.. Thanks in advance, but I am not looking for a pre-made function to read and write serial eeprom. I am trying to understand how the chip interfaces with the microprocessor.

Here is my write code:

void write_to_memory(unsigned int byte_addr, unsigned char data)
{
unsigned char addrH,addrL;
addrH=(unsigned char)(byte_addr>>8);
addrL=(unsigned char)byte_addr;

line_fd(2);
printf("trying to send %i to address 0x%X",(int)test,(int)samples);
line_fd(1);
// Send start
TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

while(!(TWCR & (1<<TWINT)))
;
printf("START acknowledge: TWSR code 0x%X",(int)TWSR);
line_fd(1);

// Send device address
TWDR=SLA_W;
TWCR=(1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)))
;
printf("Device address ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

// Send address high byte
TWDR=addrH;
TWCR=(1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)))
;
printf("Address high byte: = 0x%X",(int)addrH);
line_fd(1);
printf("Address high byte ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

// Send address low byte
TWDR=addrL;
TWCR=(1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)))
;
printf("Address low byte: = 0x%X",(int)addrL);
line_fd(1);
printf("Address low byte ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

// Send data
TWDR=data;
TWCR=(1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)))
;
printf("Value to memory: = 0x%X",(int)data);
line_fd(1);
printf("Data recieved ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

// Send stop
TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}

And here is my read code:

TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); // Send start condition to read
while(!(TWCR & (1<<TWINT))) // Wait for interrupt flag
;
printf("START acknowledge: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

TWDR=SLA_W; // Send device address as slave write
TWCR=(1<<TWINT)|(1<<TWEN); // Set interrupt flag and TWI enable
while(!(TWCR & (1<<TWINT))) // Wait for interrupt flag
;
printf("Slave write ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

TWDR=SLA_W; // Send device address as slave write
TWCR=(1<<TWINT)|(1<<TWEN); // Set interrupt flag and TWI enable
while(!(TWCR & (1<<TWINT))) // Wait for interrupt flag
;
printf("Slave write ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

TWDR=addrH; // Send address high byte
TWCR=(1<<TWINT)|(1<<TWEN); // Set interrupt flag and TWI enable
while(!(TWCR & (1<<TWINT))) // Wait for interrupt flag
;
printf("data send ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

TWDR=addrL; // Send address low byte
TWCR=(1<<TWINT)|(1<<TWEN); // Set interrupt flag and TWI enable
while(!(TWCR & (1<<TWINT))) // Wait for interrupt flag
;
printf("data send ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); // Send repeated start condition
while(!(TWCR & (1<<TWINT))) // Wait for interrupt flag
;
printf("Repeated START acknowledge: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

TWDR=SLA_R; // Send device address as slave receive
TWCR=(1<<TWINT)|(1<<TWEN); // Set interrupt flag and TWI enable
while(!(TWCR & (1<<TWINT))) // Wait for interrupt flag
;
printf("Device slave-read ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);
*****NOTE - up to here, all status codes are correct***

for(k=1;k {
TWCR=(1<<TWINT)|(1<<TWEN); // Set interrupt flag and TWI enable
while(!(TWCR & (1<<TWINT))) // Wait for interrupt flag
;

printf("Device read ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);
printf("Memory value: %X:",(int)TWDR);
line_fd(1);
}
// Send stop
TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);[/code][code]

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

1. Please can you edit your posting so that the <<code>> tags are in the correct place.

2. You will find debugging a lot easier with the help of a function or debug macro. e.g.

    printf("data send ack: TWSR code = 0x%X",(int)TWSR);
line_fd(1);

could be D("data send ack") where this does your debug display. When it works, you just make an empty function or macro.

3. You will also find it easier to write the TWI primitives as separate functions ( or macros ) . Just look to see how Fleury or others do it.

4. Things like setting the "address pointer registers" are the same for reading or writing. So if you put this code in ONE function, then your code looks clearer and any mistakes are only in ONE place.

5. Your actual problem is that you should use TWEA for a multiple read. And the last read should have NO TWEA. In fact the TWSR should show the problem although I have never actually looked at TWSR after a read.

6. Good Luck. There is no shame in copying / learning from other people's (public) code.

David.

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

David,
I believe that I found the missing TWEA last night, but unfortunately I am still reading FF for all memory spaces. This might mean that I am not writing, or that I am not reading. I would think that the status codes would give me some indication. In case in comes up, I have check that the write protect is disabled on the chip.

I will post below all of the code relevant to my project. I have written out as I did because it is easier for me to see the current status of the TWSR register. Once I understand how this works, I will gladly write this out into smaller functions. FYI, I have tried downloading the AVRlib as recommended. Aside from the fact that I couldn't get anything to compile, I was unable to find any code which actually explained how the uC communicates with the eeprom.

As you will see below, the status codes as sent to the USART show that the eeprom interface is working properly. But apparently I am missing something.

Any assistance would be appreciated.

Code. The first chunk will be my initialization. The second chunk will be my write code, and the third chunk will be the USART debugging responses, as should be obvious by following the printf statements. For this example, I wrote the numbers 1, 2, and 3, to memory locations, 1, 2 and 3. Or at least I tried to.

// 2 Wire Bus initialization
TWSR=0x00;  // pre-scaler bits set to 1
TWBR=0x0A;  // this is required minimum according to manual (see note on p 176)
TWAR=0x00;
TWCR=0x05;
// I2C Bus functions
#asm
   .equ __i2c_port=0x15 ;PORTC
   .equ __sda_bit=1
   .equ __scl_bit=0
#endasm
#include 

Here is the write function, including debug statements to the USART.

void write_to_memory(unsigned int byte_addr, unsigned char data)
{   
    unsigned char addrH,addrL;
    addrH=(unsigned char)(byte_addr>>8);
    addrL=(unsigned char)byte_addr;
    
    line_fd(2);
    printf("trying to send %i to address 0x%X",(int)test,(int)samples);
    line_fd(1);
    // Send start
    TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
    
    while(!(TWCR & (1<<TWINT)))
    ;
    printf("START acknowledge: TWSR code 0x%X",(int)TWSR);
    line_fd(1);
    
    // Send device address
    TWDR=SLA_W;
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)))
    ;         
    printf("Device address ack: TWSR code = 0x%X",(int)TWSR);
    line_fd(1);         
    
    // Send address high byte                
    TWDR=addrH;
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)))
    ;  
    printf("Address high byte: = 0x%X",(int)addrH);
    line_fd(1);    
    printf("Address high byte ack: TWSR code = 0x%X",(int)TWSR);
    line_fd(1);
      
    // Send address low byte                
    TWDR=addrL;
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)))
    ;
    printf("Address low byte: = 0x%X",(int)addrL);
    line_fd(1);    
    printf("Address low byte ack: TWSR code = 0x%X",(int)TWSR);
    line_fd(1);                    
    
    // Send data                
    TWDR=data;
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)))
    ;
    printf("Value to memory: = 0x%X",(int)data);
    line_fd(1);    
    printf("Data received ack: TWSR code = 0x%X",(int)TWSR);
    line_fd(1);
    
    // Send stop
    TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}

And here is the read code. I am trying to do a sequential read for memory locations 1, 2 and 3.

TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);     // Send start condition to read
    while(!(TWCR & (1<<TWINT)))               // Wait for interrupt flag
    ;
    printf("START acknowledge: TWSR code = 0x%X",(int)TWSR);
    line_fd(1);
    
    TWDR=SLA_W;                                // Send device address as slave write
    TWCR=(1<<TWINT)|(1<<TWEN);                 // Set interrupt flag and TWI enable
    while(!(TWCR & (1<<TWINT)))                // Wait for interrupt flag
    ;
    printf("Slave write ack: TWSR code = 0x%X",(int)TWSR);
    line_fd(1);    
    
    TWDR=SLA_W;                                // Send device address as slave write
    TWCR=(1<<TWINT)|(1<<TWEN);                 // Set interrupt flag and TWI enable
    while(!(TWCR & (1<<TWINT)))                // Wait for interrupt flag
    ;
    printf("Slave write ack: TWSR code = 0x%X",(int)TWSR);
    line_fd(1);    
     
    TWDR=addrH;                                // Send address high byte
    TWCR=(1<<TWINT)|(1<<TWEN);                 // Set interrupt flag and TWI enable
    while(!(TWCR & (1<<TWINT)))                // Wait for interrupt flag
    ;
    printf("Data send ack: TWSR code = 0x%X",(int)TWSR);
    line_fd(1);    
    
    TWDR=addrL;                                // Send address low byte
    TWCR=(1<<TWINT)|(1<<TWEN);                 // Set interrupt flag and TWI enable
    while(!(TWCR & (1<<TWINT)))                // Wait for interrupt flag
    ;
    printf("Data send ack: TWSR code = 0x%X",(int)TWSR);
    line_fd(1);  
    
    TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);     // Send repeated start condition
    while(!(TWCR & (1<<TWINT)))               // Wait for interrupt flag
    ;
    printf("Repeated START acknowledge: TWSR code = 0x%X",(int)TWSR);                                          
    line_fd(1);
    
    TWDR=SLA_R;                                // Send device address as slave receive
    TWCR=(1<<TWINT)|(1<<TWEN);                 // Set interrupt flag and TWI enable
    while(!(TWCR & (1<<TWINT)))                // Wait for interrupt flag
    ;
    printf("Device slave-read ack: TWSR code = 0x%X",(int)TWSR);
    line_fd(1);
    
    
    for(k=1;k

And here is the USART output for this example.

trying to send 1 to address 0x1
START acknowledge: TWSR code 0x8
Device address ack: TWSR code = 0x18
Address high byte: = 0x0
Address high byte ack: TWSR code = 0x28
Address low byte: = 0x1
Address low byte ack: TWSR code = 0x28
Value to memory: = 0x1
Data received ack: TWSR code = 0x28

trying to send 2 to address 0x2
START acknowledge: TWSR code 0x8
Device address ack: TWSR code = 0x18
Address high byte: = 0x0
Address high byte ack: TWSR code = 0x28
Address low byte: = 0x2
Address low byte ack: TWSR code = 0x28
Value to memory: = 0x2
Data received ack: TWSR code = 0x28


trying to send 3 to address 0x3
START acknowledge: TWSR code 0x8
Device address ack: TWSR code = 0x18
Address high byte: = 0x0
Address high byte ack: TWSR code = 0x28
Address low byte: = 0x3
Address low byte ack: TWSR code = 0x28
Value to memory: = 0x3
Data received ack: TWSR code = 0x28

Repeated START acknowledge: TWSR code = 0x10
Device slave-read ack: TWSR code = 0x40
TWSR code = 0x50
Memory value: 255:
TWSR code = 0x50
Memory value: 255:
TWSR code = 0x50
Memory value: 255:
TWSR code = 0x58
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You may be able to sift the wood from the trees but it makes it very difficult for anyone else.

You appear to have CV which has perfectly good i2c functions albeit bit-banged.

So why not write the four or five lines using the CV library just so you know your EEPROM is ok?

Then you can develop as much long-winded code as you like to replace the bit-bang with your TWI code.

David.

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

Thanks for your help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
i2c_init();
if (i2c_start() && i2c_write(0xA0)) {
    i2c_write(0);    // adshi
    i2c_write(0);    // adslo
    i2c_write(1);    // ads00 = 1
    i2c_write(2);    // ads01 = 2
    i2c_write(3);
    i2c_stop();      // store in eeprom
    delay_ms(5);     // alow time for the write
}
if (i2c_start() && i2c_write(0xA0)) {
    i2c_write(0);    // set the address again
    i2c_write(0);
    if (i2c_start() && i2c_write(0xA1)) {
    printf("%02x ", i2c_read(1);
    printf("%02x ", i2c_read(1);
    printf("%02x ", i2c_read(0);
    i2c_stop();
}

try this and see if it works.

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

David,
Thanks. I will try this. I would much rather understand how the registers are working, but I think I am going to have to give up on this ideal and take the easy way out.

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

It is not the easy way out at all. You first start with a working program and then make incremental modifications.

Of course sometimes you can just get something working first time. But if you can't, just have a look to see how others did it.

You may well be able to improve on their efforts. This is how everyone else does it. The TWI is not difficult, you just need to follow the sequence in the data sheet.

David.

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

Again, thanks for your help. But it must be apparent by now that I have pretty much memorized the datasheet and the uC manual. My code is directly from table 75 in the atmega16 manual. And if the TWI were simple, someone would have answered my question by now without referring me to pre-made functions. But I do appreciate the help. And I will gladly use the pre-made functions.

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

Ok. I have read through your code. It looks as if you are doing everything in the correct order. Since you have a lot of UART debug code, there will be at least a 5ms wait for the WRITE to complete at 9600 baud.

Your debug output implies that you can address the Slave so the WRITE must have completed.

Which is why I suggest that you use some working code to at least confirm you have christened the EEPROM. As I say, the read code looks fine too.

David.

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

David,
I tried to implement your code. I don't have the libraries for your functions. I know you think that it's simple to implement these functions, but I would have to load all the libraries and figure out how to use all the functions.

I have re-tried my code with a 20 ms delay after every command. Same results. All status codes say that there is nothing wrong. I tried changing my pullup resistors from 10k to 1k. I've replaced the chip. I'm out of ideas.

You have said that this is easy. Is this easy for you? If so then make my code work and I will pay you. Please let me know.

thanks again for your help.

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

I believe those functions are part of Codevision so if you have that compiler then you can add the header file to use the functions.

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

Does your printf() function work via interrupts? or will it hog the processor until the string is finished being sent? Your EEPROM might be timing out waiting for printf() to do it's thing.

Try removing those debug lines, and simply read several data locations into an array, then send the contents of the array after the TWI session is complete.

"A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools."
-- Douglas Adams

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

agentile wrote:
David,
I tried to implement your code. I don't have the libraries for your functions. I know you think that it's simple to implement these functions, but I would have to load all the libraries and figure out how to use all the functions.

Just add :

#include 

to your program to use all the i2c libraries.

As far as I know, every CodeVision evaluation version can do
Of course the full licensed version will include special libraries for several hardware devices as well.

I can understand non-native English speakers having a little trouble with the avr-gcc documentation. But the CodeVision package has all the Help, Libraries, Include files and EXAMPLES all in one program.

I forgot to ask. Have you got WP on your eeprom. I cannot remember exactly what happens if you try to write to a WP chip. But obviously the data remains a virgin 0xFF.

David.

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

Dont know if it is right to post here, i wish to know what does a eeprom chip lile AT24C512 do ? I mean what is it meant for ?

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

Do you go fishing?   The hook is pulled off the lake bottom by a float that is attached to the fishing line.  The fish lets you know that it is there by pulling the bobber, the dobber, or the float below the surface of the water.

 

The TWI is like that.  It has two lines that are pulled up to Vcc by external resistors.  When one of these lines is at logic high, it has been made into an input pin that is pulled high by the resistor.   This is called 'the line released'.  To make logic low, the line is made into an output, and logic zero is sent to it.  The voltage at the end of the resistor is zero.  This is called 'asserting'.

 

TWI is a dance of both the AVR and EEPROM asserting and releasing the SDA and SCL lines.

 

Your code should work?  What are the TWSR results from each stage of the process?

 

Is your EEPROM wired correctly?  The WP pin has to be at ground.

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

Thanks, but im not using it. I saw a friend of mine using the eeprom chip, i just wanted to know what it is ? Im sorry, but i could not understand, what is AT24C512 ? I know its an eeprom chip but what is it used for?

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

I would think that if you know that the AT24C512 is an EEPROM device, and if you know what an EEPROM device is, then the answer to that would be pretty obvious...?

 

If not, have you tried Google?  Or Wikipedia?

https://en.wikipedia.org/wiki/EEPROM

http://www.atmel.com/devices/at24c512c.aspx

http://www.atmel.com/Images/doc1116.pdf

 

EEPROM (also written E2PROM and pronounced "e-e-prom", "double-e prom", "e-squared", or simply "e-prom") stands for Electrically Erasable Programmable Read-Only Memory and is a type of non-volatile memory used in computers and other electronic devices to store small amounts of data that must be saved when power is removed, e.g., calibration tables or device configuration.

Most AVR have onboard EEPROM, but not much.  For example the ATmega328P has 1K.  The chip you mention has 64K.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sat. Sep 19, 2015 - 11:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, i got it.