www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Probleme beim Speichern und Lesen vom AT24C512 über TWI


Autor: Steffan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich hoffe ihr hattet alle ein schönes Fest ;)
ich bin gerade wieder etwas am verzweifeln.
Und zwar versuche ich einen EEPROM (AT24C512) über I2C mit Daten zu 
beschreiben.
Als uC dient ein ATMega128.
Ich habe Teile aus der C Routine von Jörg Wunsch übernommen.
Da ich allerdings nur byte weise schreiben und lesen wollte, habe ich 
meine eigenen Funktionen geschrieben, die aber nicht so recht 
funktionieren wollen.
Hier mal der C Code.
/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <joerg@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.        Joerg Wunsch
 * ----------------------------------------------------------------------------
 */

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

#include <avr/io.h>
#include <util/twi.h>    /* Note [1] */

#define DEBUG 1

/*
 * System clock in Hz.
 */
#define F_CPU 1000000UL  /* Note [2] */



/*
 * Compatibility defines.  This should work on ATmega8, ATmega16,
 * ATmega163, ATmega323 and ATmega128 (IOW: on all devices that
 * provide a builtin TWI interface).
 *
 * On the 128, it defaults to USART 1.
 */
#ifndef UCSRB
# ifdef UCSR1A    /* ATmega128 */
#  define UCSRA UCSR1A
#  define UCSRB UCSR1B
#  define UBRR UBRR1L
#  define UDR UDR1
# else /* ATmega8 */
#  define UCSRA USR
#  define UCSRB UCR
# endif
#endif
#ifndef UBRR
#  define UBRR UBRRL
#endif



/*
 * Note [3]
 * TWI address for 24Cxx EEPROM:
 *
 * 1 0 1 0 E2 E1 E0 R/~W  24C01/24C02
 * 1 0 1 0 E2 E1 A8 R/~W  24C04
 * 1 0 1 0 E2 A9 A8 R/~W  24C08
 * 1 0 1 0 A10 A9 A8 R/~W  24C16
 */
#define TWI_SLA_24CXX  0xA2  /* E2 E1 E0 = 0 0 0 */



/*
 * Maximal number of iterations to wait for a device to respond for a
 * selection.  Should be large enough to allow for a pending write to
 * complete, but low enough to properly abort an infinite loop in case
 * a slave is broken or not present at all.  With 100 kHz TWI clock,
 * transfering the start condition and SLA+R/W packet takes about 10
 * µs.  The longest write period is supposed to not exceed ~ 10 ms.
 * Thus, normal operation should not require more than 100 iterations
 * to get the device to respond to a selection.
 */
#define MAX_ITER  200



/*
 * Number of bytes that can be written in a row, see comments for
 * ee24xx_write_page() below.  Some vendor's devices would accept 16,
 * but 8 seems to be the lowest common denominator.
 *
 * Note that the page size must be a power of two, this simplifies the
 * page boundary calculations below.
 */
#define PAGE_SIZE 8



/*
 * Saved TWI status register, for error messages only.  We need to
 * save it in a variable, since the datasheet only guarantees the TWSR
 * register to have valid contents while the TWINT bit in TWCR is set.
 */
uint8_t twst;


/*
 * Do all the startup-time peripheral initializations: UART (for our
 * debug/test output), and TWI clock.
 */
void ioinit(void)
{

  #if F_CPU <= 1000000UL
    /*
   * Note [4]
   * Slow system clock, double Baud rate to improve rate error.
   */
    UCSRA = _BV(U2X);
    UBRR = (F_CPU / (8 * 9600UL)) - 1; /* 9600 Bd */
  #else
    UBRR = (F_CPU / (16 * 9600UL)) - 1; /* 9600 Bd */
  #endif
    UCSRB = _BV(TXEN);    /* tx enable */

    /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
  #if defined(TWPS0)
    /* has prescaler (mega128 & newer) */
  TWSR = 0;
  #endif

  #if F_CPU < 3600000UL
  TWBR = 10;      /* smallest TWBR value, see note [5] */
  #else
    TWBR = (F_CPU / 100000UL - 16) / 2;
  #endif
}



/*
 * Note [6]
 * Send character c down the UART Tx, wait until tx holding register
 * is empty.
 */
int uart_putchar(char c, FILE *unused)
{

  if (c == '\n')
    uart_putchar('\r', 0);
  loop_until_bit_is_set(UCSRA, UDRE);
  UDR = c;
  return 0;
}







void write_eeprom(unsigned char adresse, unsigned char speicher_pos_x, unsigned char speicher_pos_y,
                  unsigned char daten)
{
  
  
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);    // Startbedingung
  
  
  TWDR = adresse;                  // Adresse
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden


  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
  
  TWDR = speicher_pos_x;              // Speicheradresse in x-Richtung
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden


  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
  
  TWDR = speicher_pos_y;              // Speicheradresse in y-Richtung
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden


  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
  
  TWDR = daten;                  // Daten
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden


  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls

  TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);    // Stopbedingung

  printf("schreiben: %d\n", daten);
  putchar('\n');
}




unsigned char read_eeprom(unsigned char adresse, unsigned char speicher_pos_x, unsigned char speicher_pos_y)
{
  
  unsigned char daten;  // Hilfvariable die die Daten später zurückgibt

  // Dummy schreiben um lesen zu koennen

  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);    // Startbedingung


  

  TWDR = adresse;                  // Adresse
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden


  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls



  
  TWDR = speicher_pos_x;              // Speicheradresse in x-Richtung
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden


  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls




  TWDR = speicher_pos_y;              // Speicheradresse in y-Richtung
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden

  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
  



  // Dummy beendet

  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);    // Startbedingung


  TWDR = (adresse+0x01);              // Adresse
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden




  
  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls

  daten = TWDR;  

  TWCR = (1<<TWINT) | (0<<TWEA) | (1<<TWEN);    // NO_ACK-Impuls

  TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);    // Stopbedingung

  return daten;
  
}












FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);




int main(void)
{
 
  int rv;
  int x;
  int b;

  ioinit();

  stdout = &mystdout;
  for (x = 0; x < 8;)
    {
      write_eeprom(TWI_SLA_24CXX, x, 0x00, x);
      x++;
  }
  
  for (b = 0; b < 8;)
    {
      rv = read_eeprom(TWI_SLA_24CXX, b, 0x00);  
      printf("Postion %d\n lesen: %d\n", b,rv);
      
    putchar('\n');
    b++;
  }
  printf("done.\n");

  return 0;
}

Wenn ich das ganze über die RS232 an den PC sende, bekomme ich 
allerdings folgendes:

schreiben: 0

schreiben: 1

schreiben: 2

schreiben: 3

schreiben: 4

schreiben: 5

schreiben: 6

schreiben: 7

Postion 0
 lesen: 234

Postion 1
 lesen: 163

Postion 2
 lesen: 70

Postion 3
 lesen: 70

Postion 4
 lesen: 24

Postion 5
 lesen: 48

Postion 6
 lesen: 96

Postion 7
 lesen: 96

done.


Ich wollte eigentlich auf die Speicheradressen 0 bis 8 jeweils die Zahl 
8 bis acht schreiben und wieder auslesen.

Jemand von euch ne idee?

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schau dir noch mal an wer da wann ein ACK zu senden hat.
Ausserdem ist es eine gute Idee nach dem schreiben eines
Bytes zu warten. Meist reicht 20ms, oder per ACK Polling.

Autor: Steffan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

die ACK habe ich so gesetzt, wie sie im Datenblatt stehen, oder habe ich 
dich nun falsch verstanden?
ich habe nun noch eine _delay_ms von 20 eingebaut zwischen den 
Schreibvorgängen, aber das Ergebnis ist unverändert.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>die ACK habe ich so gesetzt, wie sie im Datenblatt stehen, oder habe ich
>dich nun falsch verstanden?

Du hast das mit dem ACK falsch verstanden:

Master schreibt was in den Slave, Slave sendet ACK.
Master liest etwas vom Slave, Master sendet ACK solange
bis er nichts mehr lesen möchte. Nach dem letzen Byte
sendet er kein ACK.

Autor: Steffan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, habe das ganze nochmal mit einem anderen Code zusammengestückelt 
bezüglihc des ACK allerdings habe ich noch ein kleines Problem.

Hier mal der aktuelle Code
/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <joerg@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.        Joerg Wunsch
 * ----------------------------------------------------------------------------
 */

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <util/delay.h>

#include <avr/io.h>
#include <util/twi.h>    /* Note [1] */

#define DEBUG 1

/*
 * System clock in Hz.
 */
#define F_CPU 8000000UL  /* Note [2] */



/*
 * Compatibility defines.  This should work on ATmega8, ATmega16,
 * ATmega163, ATmega323 and ATmega128 (IOW: on all devices that
 * provide a builtin TWI interface).
 *
 * On the 128, it defaults to USART 1.
 */
#ifndef UCSRB
# ifdef UCSR1A    /* ATmega128 */
#  define UCSRA UCSR1A
#  define UCSRB UCSR1B
#  define UBRR UBRR1L
#  define UDR UDR1
# else /* ATmega8 */
#  define UCSRA USR
#  define UCSRB UCR
# endif
#endif
#ifndef UBRR
#  define UBRR UBRRL
#endif



/*
 * Note [3]
 * TWI address for 24Cxx EEPROM:
 *
 * 1 0 1 0 E2 E1 E0 R/~W  24C01/24C02
 * 1 0 1 0 E2 E1 A8 R/~W  24C04
 * 1 0 1 0 E2 A9 A8 R/~W  24C08
 * 1 0 1 0 A10 A9 A8 R/~W  24C16
 */
#define TWI_SLA_24CXX  0xA2  /* E2 E1 E0 = 0 0 0 */



/*
 * Maximal number of iterations to wait for a device to respond for a
 * selection.  Should be large enough to allow for a pending write to
 * complete, but low enough to properly abort an infinite loop in case
 * a slave is broken or not present at all.  With 100 kHz TWI clock,
 * transfering the start condition and SLA+R/W packet takes about 10
 * µs.  The longest write period is supposed to not exceed ~ 10 ms.
 * Thus, normal operation should not require more than 100 iterations
 * to get the device to respond to a selection.
 */
#define MAX_ITER  200



/*
 * Number of bytes that can be written in a row, see comments for
 * ee24xx_write_page() below.  Some vendor's devices would accept 16,
 * but 8 seems to be the lowest common denominator.
 *
 * Note that the page size must be a power of two, this simplifies the
 * page boundary calculations below.
 */
#define PAGE_SIZE 8



/*
 * Saved TWI status register, for error messages only.  We need to
 * save it in a variable, since the datasheet only guarantees the TWSR
 * register to have valid contents while the TWINT bit in TWCR is set.
 */
uint8_t twst;


/*
 * Do all the startup-time peripheral initializations: UART (for our
 * debug/test output), and TWI clock.
 */



void ioinit(void)
{

  #if F_CPU <= 1000000UL
    /*
   * Note [4]
   * Slow system clock, double Baud rate to improve rate error.
   */
    UCSRA = _BV(U2X);
    UBRR = (F_CPU / (8 * 9600UL)) - 1; /* 9600 Bd */
  #else
    UBRR = (F_CPU / (16 * 9600UL)) - 1; /* 9600 Bd */
  #endif
    UCSRB = _BV(TXEN);    /* tx enable */

    /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
  TWBR = 0x20;
  TWSR = 0x00;
}



/*
 * Note [6]
 * Send character c down the UART Tx, wait until tx holding register
 * is empty.
 */
int uart_putchar(char c, FILE *unused)
{

  if (c == '\n')
    uart_putchar('\r', 0);
  loop_until_bit_is_set(UCSRA, UDRE);
  UDR = c;
  return 0;
}









unsigned char I2C_start(void)
{
  /*writing a one to TWINT clears it, TWSTA=Start, TWEN=TWI-enable*/
  TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
  /*wait, until start condition has been sent --> ACK*/
  while (!(TWCR & _BV(TWINT)));
//  printf("TWSR Start %#04x\n ", TWSR);
  return TWSR;
}


void I2C_stop(void)
{
  /*writing a one to TWINT clears it, TWSTO=Stop, TWEN=TWI-enable*/
  TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
//  printf("TWSR Stop %#04x\n ", TWSR);  
}


unsigned char eeprom_send_add_w (unsigned char address, unsigned char rw)
{
  
  unsigned char addr_byte = 0;
  
  addr_byte |= rw;
  
  addr_byte |= address;
  
  TWDR = addr_byte;
  
  TWCR = _BV(TWINT) | _BV(TWEN);

  /*wait, until address has been sent --> ACK*/
  while (TWSR !=0x18);
//  printf("TWSR Adresse %#04x\n ", TWSR);
  return TWSR;
}


unsigned char ext_eeprom_send_byte (unsigned char byte)
{
  /*TWDR contains byte to send*/
  TWDR = byte;
  /*send content of TWDR*/
  TWCR = _BV(TWINT) | _BV(TWEN);
  /*wait, until byte has been sent --> ACK*/
  while (!(TWCR & _BV(TWINT)));
//  printf("TWSR senden %#04x\n ", TWSR);
  return TWSR;
}


unsigned char ext_eeprom_read_byte(void)
{
  /*send content of TWDR; TWEA = enable ACK*/

  TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN);
  /*wait, until byte has been received --> ACK*/

  
  while (!(TWCR & _BV(TWINT)));
  
//  printf("TWSR lesen %#04x\n ", TWSR);
  return TWSR;
}




unsigned char eeprom_send_add_r (unsigned char address, unsigned char rw)
{
  
  unsigned char addr_byte = 0;

  addr_byte |= rw;

  addr_byte |= address;

  
  TWDR = addr_byte;
//  printf("TWDR Adresse %#04x\n ", TWDR);
  /*send content of TWDR*/
  TWCR = _BV(TWINT) | _BV(TWEN);
//  printf("TWCR Adresse %#04x\n ", TWCR);
//  printf("TWSR Adresse %#04x\n ", TWSR);
  /*wait, until address has been sent --> ACK*/
  while (TWSR !=0x40);
//  printf("TWSR Adresse %#04x\n ", TWSR);
  return TWSR;
}





void write_eeprom(unsigned char adresse, unsigned char speicher_pos_x, unsigned char speicher_pos_y,
                  unsigned char daten)
{
  
  
  I2C_start();    // Startbedingung
        
  eeprom_send_add_w (adresse,0);

  ext_eeprom_send_byte(speicher_pos_x);

  ext_eeprom_send_byte(speicher_pos_y);

  ext_eeprom_send_byte(daten);

  I2C_stop();

  printf("schreibe: %#04x\n", daten);
  putchar('\n');
}




unsigned char read_eeprom(unsigned char adresse, unsigned char speicher_pos_x, unsigned char speicher_pos_y)
{
  
  unsigned char daten;  // Hilfvariable die die Daten später zurückgibt


  I2C_start();    // Startbedingung
  
  eeprom_send_add_w (adresse,0);

  ext_eeprom_send_byte(speicher_pos_x);

  ext_eeprom_send_byte(speicher_pos_y);

  I2C_start();

  eeprom_send_add_r (adresse,1);

  ext_eeprom_read_byte();

  I2C_stop();

  daten = TWDR;
  return daten;
  
}



FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);




int main(void)
{
 
  
  unsigned char rv;
  unsigned char x;
  unsigned char a;
  unsigned char b;

  ioinit();

  stdout = &mystdout;
  printf("Los geht es mit schreiben.\n");

for (a = 0; a < 2;)
  {
  for (x = 0; x < 16;)
    {
      write_eeprom(TWI_SLA_24CXX, a, x, 0xFF);
      printf("Postion %d schreiben: %d\n", x,x);
    x++;
    _delay_ms(20);
  }
a++;
}
  
  printf("Weiter geht es mit lesen.\n");
  for (a = 0; a < 2;)
  {
  for (b = 0; b < 16;)
    {
      rv = read_eeprom(TWI_SLA_24CXX, a, b);  
      printf("Postion %d , %d lesen: %#04x\n   ", a,b,rv);
      TWCR=0x00;
    
    b++;
  }
  
  a++;
  }
  printf("done.\n");
  return 0;
}

mein Problem ist, das ich nach dem ich geschrieben habe bzw die Daten 
wieder lesen möchte, das er an folgender Stelle "hängen" bleibt
unsigned char I2C_start(void)
{
  /*writing a one to TWINT clears it, TWSTA=Start, TWEN=TWI-enable*/
  TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
  /*wait, until start condition has been sent --> ACK*/
  while (!(TWCR & _BV(TWINT)));
//  printf("TWSR Start %#04x\n ", TWSR);
  return TWSR;
}

Wie es scheint, bleibt es hier hängen:
while (!(TWCR & _BV(TWINT)));

Ich habe das ganze nun umgangen, indem ich in der Readschleife das 
Register TWCR=0x00 setze, aber ich denke mal, dass das nicht so ganz 
richitg ist. Wo ist der Gedankenfehler bzw woran liegt es?

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das könnte dein Problem sein:

>unsigned char ext_eeprom_read_byte(void)
>{
>  /*send content of TWDR; TWEA = enable ACK*/
>
>  TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN);
>  /*wait, until byte has been received --> ACK*/

>>Master liest etwas vom Slave, Master sendet ACK solange
>>bis er nichts mehr lesen möchte. Nach dem letzen Byte
>>sendet er kein ACK.

Du sendest ein ACK wenn du das Byte liest.
Du liest aber nur EIN Byte. Dieses Byte ist
also das letzte Byte, und folglich solltest
du KEIN ACK senden.

Autor: Steffan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ah ok,

danke das war es. Da hatte sich der Bus also aufgehangen bzw war noch 
nicht freigegeben.

Nun klappt es!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.