Reading from EEPROM using AVR I2C

Discussion in 'Embedded Systems and Microcontrollers' started by Rafael Adel, Dec 27, 2015.

  1. Rafael Adel

    Thread Starter New Member

    Aug 7, 2015
    11
    0
    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):
    [​IMG]
    [​IMG]

    Here's my code: (MCU 1 - writing)
    Code (Text):
    1. /*
    2. * basic.c
    3. *
    4. * Created: 12/23/2015 1:43:21 PM
    5. * Author : Rafael
    6. */
    7.  
    8. #include <avr/io.h>
    9.  
    10. char i2c_send_start();
    11. char i2c_send_addr(char);
    12. char i2c_send_data(char);
    13. char i2c_send_stop();
    14. char i2c_check_status(char);
    15.  
    16. int main(void)
    17. {
    18.     DDRA &= (~(1 << PA0) & ~(1 << PA5));
    19.     while (1)
    20.     {
    21.         if(PINA & (1 << PA0))
    22.         {
    23.             while(PINA & (1 << PA0)){}
    24.             if(i2c_send_start())
    25.             {
    26.                 if(i2c_send_addr(0xa0))
    27.                 {
    28.                     i2c_send_data(0x00);
    29.                     if(i2c_send_data(0x01))
    30.                     {
    31.                         i2c_send_stop();
    32.                     }
    33.                 }
    34.             }
    35.         }
    36.     }
    37. }
    38.  
    39. char i2c_send_start()
    40. {
    41.     TWCR |= (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);
    42.  
    43.     while(!(TWCR & (1 << TWINT))){}
    44.    
    45.     return i2c_check_status(0x08);
    46.  
    47. }
    48.  
    49. char i2c_send_addr(char addr)
    50. {
    51.     TWDR = addr;
    52.     TWCR &= ~(1 << TWSTA);
    53.     TWCR |= (1 << TWINT) | (1 << TWEN);
    54.  
    55.  
    56.     while(!(TWCR & (1 << TWINT))){}
    57.    
    58.     return i2c_check_status(0x18);
    59.  
    60. }
    61.  
    62.  
    63. char i2c_send_data(char data)
    64. {
    65.     TWDR = data;
    66.  
    67.     TWCR |= (1 << TWINT) | (1 << TWEN);
    68.  
    69.  
    70.     while(!(TWCR & (1 << TWINT))){}
    71.    
    72.     return i2c_check_status(0x28);
    73. }
    74.  
    75. char i2c_send_stop()
    76. {
    77.     TWCR |= (1 << TWSTO) | (1 << TWEN);
    78.  
    79.     //start
    80.     TWCR |= (1 << TWINT);
    81.  
    82.     while(!(TWCR & (1 << TWINT))){}
    83.  
    84.     return 1;
    85. }
    86.  
    87. char i2c_check_status(char status)
    88. {
    89.     if((TWSR & 0xf8) == status) {
    90.         return 1;
    91.     } else {
    92.         return 0;
    93.     }
    94. }
    (MCU 2 - reading) :
    Code (Text):
    1. /*
    2. * receiver.c
    3. *
    4. * Created: 12/23/2015 1:43:21 PM
    5. * Author : Rafael
    6. */
    7.  
    8. #include <avr/io.h>
    9.  
    10. char i2c_send_start();
    11. char i2c_send_addr(char);
    12. void i2c_send_data(char);
    13. char i2c_receive_data();
    14. char i2c_send_stop();
    15. char i2c_check_status(char);
    16.  
    17. int main(void)
    18. {
    19.     DDRA &= ~(1 << PA0);
    20.     DDRD = (1 << PD0) | (1 << PD3);
    21.     char status = '\0';
    22.     while (1)
    23.     {
    24.         if(PINA & (1 << PA0))
    25.         {
    26.             while(PINA & (1 << PA0)){}
    27.             if(i2c_send_start())
    28.             {
    29.                 if(i2c_send_addr(0xa1))
    30.                 {
    31.                     i2c_send_data(0x00);
    32.                     status = i2c_receive_data();
    33.                     i2c_send_stop();
    34.                    
    35.                    
    36.                 }
    37.             }
    38.             if(0x01 == status) {
    39.                 PORTD = 1 << PD0;
    40.             } else if(0x02 == status) {
    41.                 PORTD = 1 << PD3;
    42.             }
    43.         }
    44.     }
    45. }
    46.  
    47. char i2c_send_start()
    48. {
    49.     TWCR |= (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);
    50.  
    51.     while(!(TWCR & (1 << TWINT))){}
    52.    
    53.     return i2c_check_status(0x08);
    54.  
    55. }
    56.  
    57. char i2c_send_addr(char addr)
    58. {
    59.     TWDR = addr;
    60.     TWCR &= ~(1 << TWSTA);
    61.     TWCR |= (1 << TWINT) | (1 << TWEN);
    62.  
    63.  
    64.     while(!(TWCR & (1 << TWINT))){}
    65.    
    66.     return i2c_check_status(0x40);
    67.  
    68. }
    69.  
    70. void i2c_send_data(char data)
    71. {
    72.     TWDR = data;
    73.  
    74.     TWCR |= (1 << TWINT) | (1 << TWEN);
    75.  
    76.  
    77.     while(!(TWCR & (1 << TWINT))){}
    78.    
    79. }
    80.  
    81.  
    82. char i2c_receive_data()
    83. {
    84.     TWCR |= (1 << TWINT) | (1 << TWEN);
    85.  
    86.     while(!(TWCR & (1 << TWINT))){}
    87.     return TWDR;
    88.    
    89. }
    90.  
    91. char i2c_send_stop()
    92. {
    93.     TWCR |= (1 << TWSTO) | (1 << TWEN);
    94.  
    95.     //start
    96.     TWCR |= (1 << TWINT);
    97.  
    98.     while(!(TWCR & (1 << TWINT))){}
    99.  
    100.     return 1;
    101. }
    102.  
    103. char i2c_check_status(char status)
    104. {
    105.     if((TWSR & 0xf8) == status) {
    106.         return 1;
    107.     } else {
    108.         return 0;
    109.     }
    110. }
     
    • i2c.zip
      File size:
      56.4 KB
      Views:
      8
  2. DickCappels

    Moderator

    Aug 21, 2008
    2,647
    632
    Where are the pull-up resistors for SCL and SDA?
     
  3. nerdegutta

    Moderator

    Dec 15, 2009
    2,515
    785
    Each I2C IC needs to have its own address. Are you dealing with that?

    Writing:
    Line 26:
    Reading:
    Line 29:
    And you should have at least 4K7 pull-up resistors.

    I might be wrong... :)
     
  4. sailorjoe

    Member

    Jun 4, 2013
    361
    63
    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
     
  5. Rafael Adel

    Thread Starter New Member

    Aug 7, 2015
    11
    0
    I think Protues takes care of this.

    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.
     
  6. Rafael Adel

    Thread Starter New Member

    Aug 7, 2015
    11
    0
    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.
     
  7. Rafael Adel

    Thread Starter New Member

    Aug 7, 2015
    11
    0
    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 (Text):
    1. int main(void)
    2. {
    3.     DDRA &= ~(1 << PA0);
    4.     DDRD = (1 << PD0) | (1 << PD3);
    5.     unsigned char *status = malloc(sizeof(unsigned char));;
    6.     while (1)
    7.     {
    8.         if(PINA & (1 << PA0))
    9.         {
    10.             while(PINA & (1 << PA0)){}
    11.             if(i2c_send_start(0x08))
    12.             {
    13.                 if(i2c_send_addr(0xa0, 0x18))
    14.                 {
    15.                     i2c_send_data(0x00);
    16.                
    17.                     //Repeated start
    18.                     if(i2c_send_start(0x10)) {
    19.                         if(i2c_send_addr(0xa1, 0x40)) {
    20.  
    21.                             if(i2c_receive_data(status)){
    22.                                 if(0x05 == *status) {
    23.                                     PORTD = 1 << PD0;
    24.                                 } else if(0x10 == *status) {
    25.                                     PORTD = 1 << PD3;
    26.                                 }
    27.                                 i2c_send_stop();
    28.                             }
    29.                         }
    30.                     }    
    31.        
    32.                 }
    33.             }
    34.        
    35.        
    36.         }
    37.     }
    38. }
    39.  
    40. char i2c_send_start(char cond)
    41. {
    42.     TWCR |= (1 << TWSTA) | (1 << TWEN) | (1 << TWINT);
    43.  
    44.     while(!(TWCR & (1 << TWINT))){}
    45.  
    46.     return i2c_check_status(cond);
    47.  
    48. }
    49.  
    50. char i2c_send_addr(char addr, char cond)
    51. {
    52.     TWDR = addr;
    53.     TWCR &= ~(1 << TWSTA);
    54.     TWCR |= (1 << TWINT) | (1 << TWEN);
    55.  
    56.  
    57.     while(!(TWCR & (1 << TWINT))){}
    58.  
    59.     return i2c_check_status(cond);
    60.  
    61. }
    62.  
    63. void i2c_send_data(char data)
    64. {
    65.     TWDR = data;
    66.  
    67.     TWCR |= (1 << TWINT) | (1 << TWEN);
    68.  
    69.  
    70.     while(!(TWCR & (1 << TWINT))){}
    71.  
    72. }
    But still getting an error inside Proteus log:

     
  8. sailorjoe

    Member

    Jun 4, 2013
    361
    63
    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: Dec 28, 2015
Loading...