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


von Steffan (Gast)


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.
1
/*
2
 * ----------------------------------------------------------------------------
3
 * "THE BEER-WARE LICENSE" (Revision 42):
4
 * <joerg@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5
 * can do whatever you want with this stuff. If we meet some day, and you think
6
 * this stuff is worth it, you can buy me a beer in return.        Joerg Wunsch
7
 * ----------------------------------------------------------------------------
8
 */
9
10
#include <inttypes.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
14
#include <avr/io.h>
15
#include <util/twi.h>    /* Note [1] */
16
17
#define DEBUG 1
18
19
/*
20
 * System clock in Hz.
21
 */
22
#define F_CPU 1000000UL  /* Note [2] */
23
24
25
26
/*
27
 * Compatibility defines.  This should work on ATmega8, ATmega16,
28
 * ATmega163, ATmega323 and ATmega128 (IOW: on all devices that
29
 * provide a builtin TWI interface).
30
 *
31
 * On the 128, it defaults to USART 1.
32
 */
33
#ifndef UCSRB
34
# ifdef UCSR1A    /* ATmega128 */
35
#  define UCSRA UCSR1A
36
#  define UCSRB UCSR1B
37
#  define UBRR UBRR1L
38
#  define UDR UDR1
39
# else /* ATmega8 */
40
#  define UCSRA USR
41
#  define UCSRB UCR
42
# endif
43
#endif
44
#ifndef UBRR
45
#  define UBRR UBRRL
46
#endif
47
48
49
50
/*
51
 * Note [3]
52
 * TWI address for 24Cxx EEPROM:
53
 *
54
 * 1 0 1 0 E2 E1 E0 R/~W  24C01/24C02
55
 * 1 0 1 0 E2 E1 A8 R/~W  24C04
56
 * 1 0 1 0 E2 A9 A8 R/~W  24C08
57
 * 1 0 1 0 A10 A9 A8 R/~W  24C16
58
 */
59
#define TWI_SLA_24CXX  0xA2  /* E2 E1 E0 = 0 0 0 */
60
61
62
63
/*
64
 * Maximal number of iterations to wait for a device to respond for a
65
 * selection.  Should be large enough to allow for a pending write to
66
 * complete, but low enough to properly abort an infinite loop in case
67
 * a slave is broken or not present at all.  With 100 kHz TWI clock,
68
 * transfering the start condition and SLA+R/W packet takes about 10
69
 * µs.  The longest write period is supposed to not exceed ~ 10 ms.
70
 * Thus, normal operation should not require more than 100 iterations
71
 * to get the device to respond to a selection.
72
 */
73
#define MAX_ITER  200
74
75
76
77
/*
78
 * Number of bytes that can be written in a row, see comments for
79
 * ee24xx_write_page() below.  Some vendor's devices would accept 16,
80
 * but 8 seems to be the lowest common denominator.
81
 *
82
 * Note that the page size must be a power of two, this simplifies the
83
 * page boundary calculations below.
84
 */
85
#define PAGE_SIZE 8
86
87
88
89
/*
90
 * Saved TWI status register, for error messages only.  We need to
91
 * save it in a variable, since the datasheet only guarantees the TWSR
92
 * register to have valid contents while the TWINT bit in TWCR is set.
93
 */
94
uint8_t twst;
95
96
97
/*
98
 * Do all the startup-time peripheral initializations: UART (for our
99
 * debug/test output), and TWI clock.
100
 */
101
void ioinit(void)
102
{
103
104
  #if F_CPU <= 1000000UL
105
    /*
106
   * Note [4]
107
   * Slow system clock, double Baud rate to improve rate error.
108
   */
109
    UCSRA = _BV(U2X);
110
    UBRR = (F_CPU / (8 * 9600UL)) - 1; /* 9600 Bd */
111
  #else
112
    UBRR = (F_CPU / (16 * 9600UL)) - 1; /* 9600 Bd */
113
  #endif
114
    UCSRB = _BV(TXEN);    /* tx enable */
115
116
    /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
117
  #if defined(TWPS0)
118
    /* has prescaler (mega128 & newer) */
119
  TWSR = 0;
120
  #endif
121
122
  #if F_CPU < 3600000UL
123
  TWBR = 10;      /* smallest TWBR value, see note [5] */
124
  #else
125
    TWBR = (F_CPU / 100000UL - 16) / 2;
126
  #endif
127
}
128
129
130
131
/*
132
 * Note [6]
133
 * Send character c down the UART Tx, wait until tx holding register
134
 * is empty.
135
 */
136
int uart_putchar(char c, FILE *unused)
137
{
138
139
  if (c == '\n')
140
    uart_putchar('\r', 0);
141
  loop_until_bit_is_set(UCSRA, UDRE);
142
  UDR = c;
143
  return 0;
144
}
145
146
147
148
149
150
151
152
void write_eeprom(unsigned char adresse, unsigned char speicher_pos_x, unsigned char speicher_pos_y,
153
                  unsigned char daten)
154
{
155
  
156
  
157
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);    // Startbedingung
158
  
159
  
160
  TWDR = adresse;                  // Adresse
161
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden
162
163
164
  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
165
  
166
  TWDR = speicher_pos_x;              // Speicheradresse in x-Richtung
167
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden
168
169
170
  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
171
  
172
  TWDR = speicher_pos_y;              // Speicheradresse in y-Richtung
173
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden
174
175
176
  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
177
  
178
  TWDR = daten;                  // Daten
179
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden
180
181
182
  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
183
184
  TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);    // Stopbedingung
185
186
  printf("schreiben: %d\n", daten);
187
  putchar('\n');
188
}
189
190
191
192
193
unsigned char read_eeprom(unsigned char adresse, unsigned char speicher_pos_x, unsigned char speicher_pos_y)
194
{
195
  
196
  unsigned char daten;  // Hilfvariable die die Daten später zurückgibt
197
198
  // Dummy schreiben um lesen zu koennen
199
200
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);    // Startbedingung
201
202
203
  
204
205
  TWDR = adresse;                  // Adresse
206
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden
207
208
209
  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
210
211
212
213
  
214
  TWDR = speicher_pos_x;              // Speicheradresse in x-Richtung
215
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden
216
217
218
  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
219
220
221
222
223
  TWDR = speicher_pos_y;              // Speicheradresse in y-Richtung
224
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden
225
226
  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
227
  
228
229
230
231
  // Dummy beendet
232
233
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);    // Startbedingung
234
235
236
  TWDR = (adresse+0x01);              // Adresse
237
  TWCR = (1<<TWINT) | (1<<TWEN);          // senden
238
239
240
241
242
  
243
  TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);    // ACK-Impuls
244
245
  daten = TWDR;  
246
247
  TWCR = (1<<TWINT) | (0<<TWEA) | (1<<TWEN);    // NO_ACK-Impuls
248
249
  TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);    // Stopbedingung
250
251
  return daten;
252
  
253
}
254
255
256
257
258
259
260
261
262
263
264
265
266
FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
267
268
269
270
271
int main(void)
272
{
273
 
274
  int rv;
275
  int x;
276
  int b;
277
278
  ioinit();
279
280
  stdout = &mystdout;
281
  for (x = 0; x < 8;)
282
    {
283
      write_eeprom(TWI_SLA_24CXX, x, 0x00, x);
284
      x++;
285
  }
286
  
287
  for (b = 0; b < 8;)
288
    {
289
      rv = read_eeprom(TWI_SLA_24CXX, b, 0x00);  
290
      printf("Postion %d\n lesen: %d\n", b,rv);
291
      
292
    putchar('\n');
293
    b++;
294
  }
295
  printf("done.\n");
296
297
  return 0;
298
}

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?

von holger (Gast)


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.

von Steffan (Gast)


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.

von holger (Gast)


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.

von Steffan (Gast)


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
1
/*
2
 * ----------------------------------------------------------------------------
3
 * "THE BEER-WARE LICENSE" (Revision 42):
4
 * <joerg@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5
 * can do whatever you want with this stuff. If we meet some day, and you think
6
 * this stuff is worth it, you can buy me a beer in return.        Joerg Wunsch
7
 * ----------------------------------------------------------------------------
8
 */
9
10
#include <inttypes.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <util/delay.h>
14
15
#include <avr/io.h>
16
#include <util/twi.h>    /* Note [1] */
17
18
#define DEBUG 1
19
20
/*
21
 * System clock in Hz.
22
 */
23
#define F_CPU 8000000UL  /* Note [2] */
24
25
26
27
/*
28
 * Compatibility defines.  This should work on ATmega8, ATmega16,
29
 * ATmega163, ATmega323 and ATmega128 (IOW: on all devices that
30
 * provide a builtin TWI interface).
31
 *
32
 * On the 128, it defaults to USART 1.
33
 */
34
#ifndef UCSRB
35
# ifdef UCSR1A    /* ATmega128 */
36
#  define UCSRA UCSR1A
37
#  define UCSRB UCSR1B
38
#  define UBRR UBRR1L
39
#  define UDR UDR1
40
# else /* ATmega8 */
41
#  define UCSRA USR
42
#  define UCSRB UCR
43
# endif
44
#endif
45
#ifndef UBRR
46
#  define UBRR UBRRL
47
#endif
48
49
50
51
/*
52
 * Note [3]
53
 * TWI address for 24Cxx EEPROM:
54
 *
55
 * 1 0 1 0 E2 E1 E0 R/~W  24C01/24C02
56
 * 1 0 1 0 E2 E1 A8 R/~W  24C04
57
 * 1 0 1 0 E2 A9 A8 R/~W  24C08
58
 * 1 0 1 0 A10 A9 A8 R/~W  24C16
59
 */
60
#define TWI_SLA_24CXX  0xA2  /* E2 E1 E0 = 0 0 0 */
61
62
63
64
/*
65
 * Maximal number of iterations to wait for a device to respond for a
66
 * selection.  Should be large enough to allow for a pending write to
67
 * complete, but low enough to properly abort an infinite loop in case
68
 * a slave is broken or not present at all.  With 100 kHz TWI clock,
69
 * transfering the start condition and SLA+R/W packet takes about 10
70
 * µs.  The longest write period is supposed to not exceed ~ 10 ms.
71
 * Thus, normal operation should not require more than 100 iterations
72
 * to get the device to respond to a selection.
73
 */
74
#define MAX_ITER  200
75
76
77
78
/*
79
 * Number of bytes that can be written in a row, see comments for
80
 * ee24xx_write_page() below.  Some vendor's devices would accept 16,
81
 * but 8 seems to be the lowest common denominator.
82
 *
83
 * Note that the page size must be a power of two, this simplifies the
84
 * page boundary calculations below.
85
 */
86
#define PAGE_SIZE 8
87
88
89
90
/*
91
 * Saved TWI status register, for error messages only.  We need to
92
 * save it in a variable, since the datasheet only guarantees the TWSR
93
 * register to have valid contents while the TWINT bit in TWCR is set.
94
 */
95
uint8_t twst;
96
97
98
/*
99
 * Do all the startup-time peripheral initializations: UART (for our
100
 * debug/test output), and TWI clock.
101
 */
102
103
104
105
void ioinit(void)
106
{
107
108
  #if F_CPU <= 1000000UL
109
    /*
110
   * Note [4]
111
   * Slow system clock, double Baud rate to improve rate error.
112
   */
113
    UCSRA = _BV(U2X);
114
    UBRR = (F_CPU / (8 * 9600UL)) - 1; /* 9600 Bd */
115
  #else
116
    UBRR = (F_CPU / (16 * 9600UL)) - 1; /* 9600 Bd */
117
  #endif
118
    UCSRB = _BV(TXEN);    /* tx enable */
119
120
    /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
121
  TWBR = 0x20;
122
  TWSR = 0x00;
123
}
124
125
126
127
/*
128
 * Note [6]
129
 * Send character c down the UART Tx, wait until tx holding register
130
 * is empty.
131
 */
132
int uart_putchar(char c, FILE *unused)
133
{
134
135
  if (c == '\n')
136
    uart_putchar('\r', 0);
137
  loop_until_bit_is_set(UCSRA, UDRE);
138
  UDR = c;
139
  return 0;
140
}
141
142
143
144
145
146
147
148
149
150
unsigned char I2C_start(void)
151
{
152
  /*writing a one to TWINT clears it, TWSTA=Start, TWEN=TWI-enable*/
153
  TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
154
  /*wait, until start condition has been sent --> ACK*/
155
  while (!(TWCR & _BV(TWINT)));
156
//  printf("TWSR Start %#04x\n ", TWSR);
157
  return TWSR;
158
}
159
160
161
void I2C_stop(void)
162
{
163
  /*writing a one to TWINT clears it, TWSTO=Stop, TWEN=TWI-enable*/
164
  TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
165
//  printf("TWSR Stop %#04x\n ", TWSR);  
166
}
167
168
169
unsigned char eeprom_send_add_w (unsigned char address, unsigned char rw)
170
{
171
  
172
  unsigned char addr_byte = 0;
173
  
174
  addr_byte |= rw;
175
  
176
  addr_byte |= address;
177
  
178
  TWDR = addr_byte;
179
  
180
  TWCR = _BV(TWINT) | _BV(TWEN);
181
182
  /*wait, until address has been sent --> ACK*/
183
  while (TWSR !=0x18);
184
//  printf("TWSR Adresse %#04x\n ", TWSR);
185
  return TWSR;
186
}
187
188
189
unsigned char ext_eeprom_send_byte (unsigned char byte)
190
{
191
  /*TWDR contains byte to send*/
192
  TWDR = byte;
193
  /*send content of TWDR*/
194
  TWCR = _BV(TWINT) | _BV(TWEN);
195
  /*wait, until byte has been sent --> ACK*/
196
  while (!(TWCR & _BV(TWINT)));
197
//  printf("TWSR senden %#04x\n ", TWSR);
198
  return TWSR;
199
}
200
201
202
unsigned char ext_eeprom_read_byte(void)
203
{
204
  /*send content of TWDR; TWEA = enable ACK*/
205
206
  TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN);
207
  /*wait, until byte has been received --> ACK*/
208
209
  
210
  while (!(TWCR & _BV(TWINT)));
211
  
212
//  printf("TWSR lesen %#04x\n ", TWSR);
213
  return TWSR;
214
}
215
216
217
218
219
unsigned char eeprom_send_add_r (unsigned char address, unsigned char rw)
220
{
221
  
222
  unsigned char addr_byte = 0;
223
224
  addr_byte |= rw;
225
226
  addr_byte |= address;
227
228
  
229
  TWDR = addr_byte;
230
//  printf("TWDR Adresse %#04x\n ", TWDR);
231
  /*send content of TWDR*/
232
  TWCR = _BV(TWINT) | _BV(TWEN);
233
//  printf("TWCR Adresse %#04x\n ", TWCR);
234
//  printf("TWSR Adresse %#04x\n ", TWSR);
235
  /*wait, until address has been sent --> ACK*/
236
  while (TWSR !=0x40);
237
//  printf("TWSR Adresse %#04x\n ", TWSR);
238
  return TWSR;
239
}
240
241
242
243
244
245
void write_eeprom(unsigned char adresse, unsigned char speicher_pos_x, unsigned char speicher_pos_y,
246
                  unsigned char daten)
247
{
248
  
249
  
250
  I2C_start();    // Startbedingung
251
        
252
  eeprom_send_add_w (adresse,0);
253
254
  ext_eeprom_send_byte(speicher_pos_x);
255
256
  ext_eeprom_send_byte(speicher_pos_y);
257
258
  ext_eeprom_send_byte(daten);
259
260
  I2C_stop();
261
262
  printf("schreibe: %#04x\n", daten);
263
  putchar('\n');
264
}
265
266
267
268
269
unsigned char read_eeprom(unsigned char adresse, unsigned char speicher_pos_x, unsigned char speicher_pos_y)
270
{
271
  
272
  unsigned char daten;  // Hilfvariable die die Daten später zurückgibt
273
274
275
  I2C_start();    // Startbedingung
276
  
277
  eeprom_send_add_w (adresse,0);
278
279
  ext_eeprom_send_byte(speicher_pos_x);
280
281
  ext_eeprom_send_byte(speicher_pos_y);
282
283
  I2C_start();
284
285
  eeprom_send_add_r (adresse,1);
286
287
  ext_eeprom_read_byte();
288
289
  I2C_stop();
290
291
  daten = TWDR;
292
  return daten;
293
  
294
}
295
296
297
298
FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
299
300
301
302
303
int main(void)
304
{
305
 
306
  
307
  unsigned char rv;
308
  unsigned char x;
309
  unsigned char a;
310
  unsigned char b;
311
312
  ioinit();
313
314
  stdout = &mystdout;
315
  printf("Los geht es mit schreiben.\n");
316
317
for (a = 0; a < 2;)
318
  {
319
  for (x = 0; x < 16;)
320
    {
321
      write_eeprom(TWI_SLA_24CXX, a, x, 0xFF);
322
      printf("Postion %d schreiben: %d\n", x,x);
323
    x++;
324
    _delay_ms(20);
325
  }
326
a++;
327
}
328
  
329
  printf("Weiter geht es mit lesen.\n");
330
  for (a = 0; a < 2;)
331
  {
332
  for (b = 0; b < 16;)
333
    {
334
      rv = read_eeprom(TWI_SLA_24CXX, a, b);  
335
      printf("Postion %d , %d lesen: %#04x\n   ", a,b,rv);
336
      TWCR=0x00;
337
    
338
    b++;
339
  }
340
  
341
  a++;
342
  }
343
  printf("done.\n");
344
  return 0;
345
}

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
1
unsigned char I2C_start(void)
2
{
3
  /*writing a one to TWINT clears it, TWSTA=Start, TWEN=TWI-enable*/
4
  TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
5
  /*wait, until start condition has been sent --> ACK*/
6
  while (!(TWCR & _BV(TWINT)));
7
//  printf("TWSR Start %#04x\n ", TWSR);
8
  return TWSR;
9
}

Wie es scheint, bleibt es hier hängen:
1
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?

von holger (Gast)


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.

von Steffan (Gast)


Lesenswert?

Ah ok,

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

Nun klappt es!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.