Probleme beim Speichern und Lesen vom AT24C512 über TWI

Gast #736712
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?
Gast #736752
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.
Gast #736782
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.
Gast #737453
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?
Gast #737476
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren