Forum: Mikrocontroller und Digitale Elektronik SD-Card schreiben auf Arduino due mit Atmel-Studio


von Andreas W. (andy_w)


Lesenswert?

Hallo,
auf dem Arduino Due ist der SAM3X8E, den ich schon erfolgreich mit dem 
Atmel Studio programmiere. Alles läuft inzwischen wie gewünscht, auch 
der SPI-Bus. Mit den angeblich vorhandenen Bibliotheken des Atmle Studio 
für SD-Karten komme ich gar nicht klar, offensichtlich sind die wohl 
auch nicht für einen einfachen SPI-Bus der SD-Karte vorgesehen. Ein 
voller Anschluß mit 4 Bit Breite geht bei mir nicht, da schon viele 
Ports vergeben sind.

Also habe ich die Blbliotheken vom LPCXpresso genommen, genauer gesagt 
die Files ff.c und mmc.c. ff.c ist unverändert übernommen worden, mmc.c 
habe ich an meine Hardware und meine eigenen SPI-Bus Finktionen 
angepaßt, alles andere bleieb ebenfalls unverändert. Damit funktioniert 
auch der Zugriff aud SD-Karten - aber nur lesend. Sobald ich versuche, 
eine Datei zu schreiben, funktioniert das fopen, später ist auch eine 
Datei mit dem Manen vorhanden, aber die Datei ist leer, fwrite 
funktioniert nicht.

Mit dem Debugger habe ich das fwrite verfolgt und konnte feststellen, 
daß der Fehler in der Funktion xmit_datablock stattfindet.

Hier mal der Inhalt meiner mmc.c (langer Code), die Funktion ist bei 
etwa Zeile 230:
1
/*-----------------------------------------------------------------------*/
2
/* MMCv3/SDv1/SDv2 (in SPI mode) control module  (C)ChaN, 2007           */
3
/*-----------------------------------------------------------------------*/
4
/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros         */
5
/* are platform dependent.                                               */
6
/*-----------------------------------------------------------------------*/
7
//#include "lpc17xx_ssp.h"
8
//#include "lpc17xx_gpio.h"
9
#include "diskio.h"
10
#include "AW_spi.h"
11
#include "globals.h"
12
13
/* Definitions for MMC/SDC command */
14
#define CMD0  (0x40+0)  /* GO_IDLE_STATE */
15
#define CMD1  (0x40+1)  /* SEND_OP_COND (MMC) */
16
#define  ACMD41  (0xC0+41)  /* SEND_OP_COND (SDC) */
17
#define CMD8  (0x40+8)  /* SEND_IF_COND */
18
#define CMD9  (0x40+9)  /* SEND_CSD */
19
#define CMD10  (0x40+10)  /* SEND_CID */
20
#define CMD12  (0x40+12)  /* STOP_TRANSMISSION */
21
#define ACMD13  (0xC0+13)  /* SD_STATUS (SDC) */
22
#define CMD16  (0x40+16)  /* SET_BLOCKLEN */
23
#define CMD17  (0x40+17)  /* READ_SINGLE_BLOCK */
24
#define CMD18  (0x40+18)  /* READ_MULTIPLE_BLOCK */
25
#define CMD23  (0x40+23)  /* SET_BLOCK_COUNT (MMC) */
26
#define  ACMD23  (0xC0+23)  /* SET_WR_BLK_ERASE_COUNT (SDC) */
27
#define CMD24  (0x40+24)  /* WRITE_BLOCK */
28
#define CMD25  (0x40+25)  /* WRITE_MULTIPLE_BLOCK */
29
#define CMD55  (0x40+55)  /* APP_CMD */
30
#define CMD58  (0x40+58)  /* READ_OCR */
31
32
33
/* Port Controls  (Platform dependent) */
34
//#define CS_LOW()    GPIO_ClearValue( 2, 1<<2 )      // for eval board
35
//#define CS_HIGH()   GPIO_SetValue(2, 1<<2)
36
//#define CS_SETDIR() GPIO_SetDir(2, 1<<2, 1)
37
#define CS_LOW()    GPIO_ClearValue(0, 1<<6)      // for Andys hardware
38
#define CS_HIGH()   GPIO_SetValue(0, 1<<6)
39
#define CS_SETDIR() GPIO_SetDir(0, 1<<6, 1)
40
41
42
#define  FCLK_SLOW()          /* Set slow clock (100k-400k) */
43
#define  FCLK_FAST()          /* Set fast clock (depends on the CSD) */
44
45
46
/*--------------------------------------------------------------------------
47
48
   Module Private Functions
49
50
---------------------------------------------------------------------------*/
51
52
//static volatile
53
//DSTATUS Stat = STA_NOINIT;  /* Disk status */
54
55
//static volatile
56
//BYTE Timer1, Timer2;  /* 100Hz decrement timer */
57
58
static
59
BYTE CardType;      /* Card type flags */
60
61
static void SSPSend(uint8_t *buffer, uint32_t Length)
62
{
63
   aw_spi_transmit(0, Length, buffer);
64
}
65
66
void SSPReceive( uint8_t *buffer, uint32_t Length )
67
{
68
   aw_spi_receive(0, Length, buffer);
69
}
70
71
/*-----------------------------------------------------------------------*/
72
/* Transmit a byte to MMC via SPI  (Platform dependent)                  */
73
/*-----------------------------------------------------------------------*/
74
75
//#define xmit_spi(dat) (SSPSend((uint8_t*)&(dat), 1))
76
static void xmit_spi(BYTE dat)
77
{
78
    SSPSend(&dat, 1);
79
}
80
81
82
/*-----------------------------------------------------------------------*/
83
/* Receive a byte from MMC via SPI  (Platform dependent)                 */
84
/*-----------------------------------------------------------------------*/
85
86
static
87
BYTE rcvr_spi (void)
88
{
89
    BYTE data = 0;
90
91
    SSPReceive(&data, 1);
92
93
    return data;
94
}
95
96
/* Alternative macro to receive data fast */
97
98
#define rcvr_spi_m(dst) \
99
    do { \
100
        SSPReceive((uint8_t*)(dst), 1); \
101
    } while(0)
102
103
104
105
106
/*-----------------------------------------------------------------------*/
107
/* Wait for card ready                                                   */
108
/*-----------------------------------------------------------------------*/
109
110
static
111
BYTE wait_ready (void)
112
{
113
  BYTE res;
114
115
  Timer2 = 50;  /* Wait for ready in timeout of 500ms */
116
  rcvr_spi();
117
  do
118
    res = rcvr_spi();
119
  while ((res != 0xFF) && Timer2);
120
121
  return res;
122
}
123
124
125
126
/*-----------------------------------------------------------------------*/
127
/* Deselect the card and release SPI bus                                 */
128
/*-----------------------------------------------------------------------*/
129
130
static
131
void deselect (void)
132
{
133
   aw_pio_set  (PIOB,  0x00080000);                // SD_CS = 1;
134
  rcvr_spi();
135
}
136
137
138
139
/*-----------------------------------------------------------------------*/
140
/* Select the card and wait ready                                        */
141
/*-----------------------------------------------------------------------*/
142
143
static
144
BOOL select (void)  /* TRUE:Successful, FALSE:Timeout */
145
{
146
   uint8_t     res;
147
148
   aw_pio_clear(PIOB, 0x00080000);               // SD_CS = 0;
149
150
  if (wait_ready() != 0xFF) {
151
    deselect();
152
    return FALSE;
153
  }
154
  return TRUE;
155
}
156
157
158
159
/*-----------------------------------------------------------------------*/
160
/* Power Control  (Platform dependent)                                   */
161
/*-----------------------------------------------------------------------*/
162
/* When the target system does not support socket power control, there   */
163
/* is nothing to do in these functions and chk_power always returns 1.   */
164
165
static
166
void power_on (void)
167
{
168
}
169
170
static
171
void power_off (void)
172
{
173
}
174
175
static
176
int chk_power(void)    /* Socket power state: 0=off, 1=on */
177
{
178
  return 1;
179
}
180
181
182
183
/*-----------------------------------------------------------------------*/
184
/* Receive a data packet from MMC                                        */
185
/*-----------------------------------------------------------------------*/
186
187
static
188
BOOL rcvr_datablock (
189
  BYTE *buff,    /* Data buffer to store received data */
190
  UINT btr    /* Byte count (must be multiple of 4) */
191
)
192
{
193
  BYTE token;
194
195
  Timer1 = 20;
196
  do {    /* Wait for data packet in timeout of 200ms */
197
    token = rcvr_spi();
198
  } while ((token == 0xFF) && Timer1);
199
  if(token != 0xFE) return FALSE;
200
            /* If not valid data token, retutn with error */
201
202
  do {    /* Receive the data block into buffer */
203
    rcvr_spi_m(buff++);
204
    rcvr_spi_m(buff++);
205
    rcvr_spi_m(buff++);
206
    rcvr_spi_m(buff++);
207
  } while (btr -= 4);
208
  rcvr_spi();      /* Discard CRC */
209
  rcvr_spi();
210
211
  return TRUE      /* Return with success */
212
}
213
214
215
216
/*-----------------------------------------------------------------------*/
217
/* Send a data packet to MMC                                             */
218
/*-----------------------------------------------------------------------*/
219
220
static
221
BOOL xmit_datablock (
222
  const BYTE *buff,   /* 512 byte data block to be transmitted */
223
  BYTE token    /* Data/Stop token */
224
)
225
{
226
  BYTE resp, wc;
227
228
  if (wait_ready() != 0xFF) return FALSE;
229
230
  xmit_spi(token);  /* Xmit data token */
231
  if (token != 0xFD) {  /* Is data token */
232
    wc = 0;
233
    do {    // Xmit the 512 byte data block to MMC
234
      xmit_spi(*buff++);
235
      xmit_spi(*buff++);
236
    } while (--wc);
237
    xmit_spi(0xFF);    /* CRC (Dummy) */
238
    xmit_spi(0xFF);
239
    resp = rcvr_spi();      /* Reveive data response */
240
      if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */
241
         return FALSE;
242
  }
243
244
  return TRUE;
245
}
246
247
248
/*-----------------------------------------------------------------------*/
249
/* Send a command packet to MMC                                          */
250
/*-----------------------------------------------------------------------*/
251
252
static
253
BYTE send_cmd (
254
  BYTE cmd,    /* Command byte */
255
  DWORD arg    /* Argument */
256
)
257
{
258
  BYTE n, res;
259
260
  if (cmd & 0x80) {
261
             /* ACMD<n> is the command sequense of CMD55-CMD<n> */
262
    cmd &= 0x7F;
263
    res = send_cmd(CMD55, 0);
264
    if (res > 1) return res;
265
  }
266
267
  /* Select the card and wait for ready */
268
  deselect();
269
  if (!select()) return 0xFF;
270
271
  /* Send command packet */
272
  xmit_spi(cmd);    /* Start + Command index */
273
  xmit_spi((BYTE)(arg >> 24));    /* Argument[31..24] */
274
  xmit_spi((BYTE)(arg >> 16));    /* Argument[23..16] */
275
  xmit_spi((BYTE)(arg >> 8));    /* Argument[15..8] */
276
  xmit_spi((BYTE)arg);      /* Argument[7..0] */
277
  n = 0x01;        /* Dummy CRC + Stop */
278
  if (cmd == CMD0) n = 0x95;  /* Valid CRC for CMD0(0) */
279
  if (cmd == CMD8) n = 0x87;  /* Valid CRC for CMD8(0x1AA) */
280
  xmit_spi(n);
281
282
  /* Receive command response */
283
  if (cmd == CMD12) rcvr_spi();
284
           /* Skip a stuff byte when stop reading */
285
  n = 10;  /* Wait for a valid response in timeout of 10 attempts */
286
  do
287
    res = rcvr_spi();
288
  while ((res & 0x80) && --n);
289
290
  return res;  /* Return with the response value */
291
}
292
293
294
295
/*--------------------------------------------------------------------------
296
297
   Public Functions
298
299
---------------------------------------------------------------------------*/
300
301
302
/*-----------------------------------------------------------------------*/
303
/* Initialize Disk Drive                                                 */
304
/*-----------------------------------------------------------------------*/
305
306
BYTE disk_initialize (                                   // #define DSTATUS BYTE
307
  BYTE drv    /* Physical drive nmuber (0) */
308
)
309
{
310
  BYTE n, cmd, ty, ocr[4];
311
312
//  CS_SETDIR();
313
//  GPIO_SetDir(2, 1<<11, 0); /* Card Detect */
314
315
  if (drv) return STA_NOINIT;  /* Supports only single drive */
316
  if (Stat & STA_NODISK) return Stat;  /* No card in the socket */
317
318
  power_on();      /* Force socket power on */
319
   FCLK_SLOW();
320
  for (n = 10; n; n--) rcvr_spi();  /* 80 dummy clocks */
321
322
  ty = 0;
323
  if (send_cmd(CMD0, 0) == 1) {  /* Enter Idle state */
324
     Timer1 = 100;  /* Initialization timeout of 1000 msec */
325
     if (send_cmd(CMD8, 0x1AA) == 1) {  /* SDHC */
326
        for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
327
                       /* Get trailing return value of R7 resp */
328
        if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
329
                      /* The card can work at vdd range of 2.7-3.6V */
330
           while (Timer1 && send_cmd(ACMD41, 1UL << 30));
331
                    /* Wait for leaving idle state (ACMD41 with HCS bit) */
332
     if (Timer1 && send_cmd(CMD58, 0) == 0) {
333
                         /* Check CCS bit in the OCR */
334
              for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
335
                    ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;
336
                            /* SDv2 */
337
     }
338
              }
339
           } else {    /* SDSC or MMC */
340
        if (send_cmd(ACMD41, 0) <= 1)   {
341
           ty = CT_SD1; cmd = ACMD41;  /* SDv1 */
342
        } else {
343
           ty = CT_MMC; cmd = CMD1;  /* MMCv3 */
344
        }
345
        while (Timer1 && send_cmd(cmd, 0));
346
                     /* Wait for leaving idle state */
347
        if (!Timer1 || send_cmd(CMD16, 512) != 0)
348
                  /* Set R/W block length to 512 */
349
           ty = 0;
350
     }
351
  }
352
  CardType = ty;
353
  deselect();
354
355
  if (ty) {      /* Initialization succeded */
356
    Stat &= ~STA_NOINIT;    /* Clear STA_NOINIT */
357
    FCLK_FAST();
358
  } else {      /* Initialization failed */
359
    power_off();
360
  }
361
362
  return Stat;
363
}
364
365
366
367
/*-----------------------------------------------------------------------*/
368
/* Get Disk Status                                                       */
369
/*-----------------------------------------------------------------------*/
370
371
DSTATUS disk_status (
372
  BYTE drv    /* Physical drive nmuber (0) */
373
)
374
{
375
  if (drv) return STA_NOINIT;  /* Supports only single drive */
376
  return Stat;
377
}
378
379
380
381
/*-----------------------------------------------------------------------*/
382
/* Read Sector(s)                                                        */
383
/*-----------------------------------------------------------------------*/
384
385
DRESULT disk_read (
386
  BYTE drv,      /* Physical drive nmuber (0) */
387
  BYTE *buff,  /* Pointer to the data buffer to store read data */
388
  DWORD sector,    /* Start sector number (LBA) */
389
  BYTE count      /* Sector count (1..255) */
390
)
391
{
392
  if (drv || !count) return RES_PARERR;
393
  if (Stat & STA_NOINIT) return RES_NOTRDY;
394
395
  if (!(CardType & CT_BLOCK)) sector *= 512;
396
             /* Convert to byte address if needed */
397
398
  if (count == 1) {  /* Single block read */
399
    if ((send_cmd(CMD17, sector) == 0)
400
                     /* READ_SINGLE_BLOCK */
401
      && rcvr_datablock(buff, 512))
402
      count = 0;
403
  }
404
  else {        /* Multiple block read */
405
    if (send_cmd(CMD18, sector) == 0) {
406
                     /* READ_MULTIPLE_BLOCK */
407
      do {
408
        if (!rcvr_datablock(buff, 512)) break;
409
        buff += 512;
410
      } while (--count);
411
      send_cmd(CMD12, 0);   /* STOP_TRANSMISSION */
412
    }
413
  }
414
  deselect();
415
416
  return count ? RES_ERROR : RES_OK;
417
}
418
419
420
421
/*-----------------------------------------------------------------------*/
422
/* Write Sector(s)                                                       */
423
/*-----------------------------------------------------------------------*/
424
425
#if _READONLY == 0
426
DRESULT disk_write (
427
  BYTE drv,      /* Physical drive nmuber (0) */
428
  const BYTE *buff,  /* Pointer to the data to be written */
429
  DWORD sector,    /* Start sector number (LBA) */
430
  BYTE count      /* Sector count (1..255) */
431
)
432
{
433
  if (drv || !count) return RES_PARERR;
434
  if (Stat & STA_NOINIT) return RES_NOTRDY;
435
  if (Stat & STA_PROTECT) return RES_WRPRT;
436
437
  if (!(CardType & CT_BLOCK)) sector *= 512;
438
                /* Convert to byte address if needed */
439
440
  if (count == 1) {  /* Single block write */
441
    if ((send_cmd(CMD24, sector) == 0)  /* WRITE_BLOCK */
442
      && xmit_datablock(buff, 0xFE))
443
      count = 0;
444
  }
445
  else {        /* Multiple block write */
446
    if (CardType & CT_SDC) send_cmd(ACMD23, count);
447
    if (send_cmd(CMD25, sector) == 0) {
448
                   /* WRITE_MULTIPLE_BLOCK */
449
      do {
450
        if (!xmit_datablock(buff, 0xFC)) break;
451
        buff += 512;
452
      } while (--count);
453
      if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
454
        count = 1;
455
    }
456
  }
457
  deselect();
458
459
  return count ? RES_ERROR : RES_OK;
460
}
461
#endif /* _READONLY == 0 */
462
463
464
465
/*-----------------------------------------------------------------------*/
466
/* Miscellaneous Functions                                               */
467
/*-----------------------------------------------------------------------*/
468
469
#if _USE_IOCTL != 0
470
DRESULT disk_ioctl (
471
  BYTE drv,    /* Physical drive nmuber (0) */
472
  BYTE ctrl,    /* Control code */
473
  void *buff    /* Buffer to send/receive control data */
474
)
475
{
476
  DRESULT res;
477
  BYTE n, csd[16], *ptr = buff;
478
  WORD csize;
479
480
481
  if (drv) return RES_PARERR;
482
483
  res = RES_ERROR;
484
485
  if (ctrl == CTRL_POWER) {
486
    switch (*ptr) {
487
    case 0:    /* Sub control code == 0 (POWER_OFF) */
488
      if (chk_power())
489
        power_off();    /* Power off */
490
      res = RES_OK;
491
      break;
492
    case 1:    /* Sub control code == 1 (POWER_ON) */
493
      power_on();    /* Power on */
494
      res = RES_OK;
495
      break;
496
    case 2:    /* Sub control code == 2 (POWER_GET) */
497
      *(ptr+1) = (BYTE)chk_power();
498
      res = RES_OK;
499
      break;
500
    default :
501
      res = RES_PARERR;
502
    }
503
  }
504
  else {
505
    if (Stat & STA_NOINIT) return RES_NOTRDY;
506
507
    switch (ctrl) {
508
    case CTRL_SYNC :
509
/* Make sure that no pending write process. Do not remove this or written sector might not left updated. */
510
      if (select()) {
511
               res = RES_OK;
512
                           deselect();
513
                  }
514
       break;
515
516
    case GET_SECTOR_COUNT :
517
                   /* Get number of sectors on the disk (DWORD) */
518
    if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
519
       if ((csd[0] >> 6) == 1) {/* SDC ver 2.00 */
520
          csize = csd[9] + ((WORD)csd[8] << 8) + 1;
521
          *(DWORD*)buff = (DWORD)csize << 10;
522
       } else {    /* SDC ver 1.XX or MMC*/
523
          n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
524
          csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
525
          *(DWORD*)buff = (DWORD)csize << (n - 9);
526
       }
527
       res = RES_OK;
528
    }
529
    break;
530
531
    case GET_SECTOR_SIZE :  /* Get R/W sector size (WORD) */
532
      *(WORD*)buff = 512;
533
      res = RES_OK;
534
      break;
535
536
    case GET_BLOCK_SIZE :
537
                  /* Get erase block size in unit of sector (DWORD) */
538
       if (CardType & CT_SD2) {    /* SDC ver 2.00 */
539
          if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */
540
             rcvr_spi();
541
       if (rcvr_datablock(csd, 16)) {
542
                                            /* Read partial block */
543
          for (n = 64 - 16; n; n--) rcvr_spi();
544
                           /* Purge trailing data */
545
          *(DWORD*)buff = 16UL << (csd[10] >> 4);
546
          res = RES_OK;
547
       }
548
          }
549
       } else {    /* SDC ver 1.XX or MMC */
550
         if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
551
                          /* Read CSD */
552
          if (CardType & CT_SD1) {  /* SDC ver 1.XX */
553
       *(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
554
          } else {    /* MMC */
555
       *(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
556
          }
557
          res = RES_OK;
558
                   }
559
          }
560
          break;
561
562
    case MMC_GET_TYPE :  /* Get card type flags (1 byte) */
563
      *ptr = CardType;
564
      res = RES_OK;
565
      break;
566
567
    case MMC_GET_CSD :
568
                   /* Receive CSD as a data block (16 bytes) */
569
      if (send_cmd(CMD9, 0) == 0   /* READ_CSD */
570
        && rcvr_datablock(ptr, 16))
571
        res = RES_OK;
572
      break;
573
574
    case MMC_GET_CID :
575
                   /* Receive CID as a data block (16 bytes) */
576
      if (send_cmd(CMD10, 0) == 0  /* READ_CID */
577
        && rcvr_datablock(ptr, 16))
578
        res = RES_OK;
579
      break;
580
581
    case MMC_GET_OCR :/* Receive OCR as an R3 resp (4 bytes) */
582
      if (send_cmd(CMD58, 0) == 0) {  /* READ_OCR */
583
        for (n = 4; n; n--) *ptr++ = rcvr_spi();
584
        res = RES_OK;
585
      }
586
      break;
587
588
    case MMC_GET_SDSTAT :
589
                   /* Receive SD statsu as a data block (64 bytes) */
590
      if (send_cmd(ACMD13, 0) == 0) {  /* SD_STATUS */
591
        rcvr_spi();
592
        if (rcvr_datablock(ptr, 64))
593
          res = RES_OK;
594
      }
595
      break;
596
597
    default:
598
      res = RES_PARERR;
599
    }
600
601
    deselect();
602
  }
603
604
  return res;
605
}
606
#endif /* _USE_IOCTL != 0 */

Die Funktion xmit_datablock läuft auch durch, aber zum Schluß liefert 
das Lesen resp = rcvr_spi(); den Wert 0xff, erwartet wird eine Antwort 
0x05, nachdem sie mit 0x1f verundet wurde, d.h die obersten 3 Bits sind 
egal.

Aus irgendeinem Grund wird der Datenblock nicht geschrieben, warum, das 
kann ich nicht nachvollziehen. Die SPI-Zugriffe funktionieren jedenfalls 
einwandfrei, es gibt ja nur Daten lesen und Daten schreiben, die jeweils 
andere Funktion wird ignoriert, denn SPI-Zugriffe machen ja eigentlich 
immer lesen und schreiben gleichzeitig. Beide Funktionen werden schon 
beim Lesen von Dateien genutzt und da funktioniert es ja. Einziger 
Hinweiß, der zu diesem Thema auf google zu finden war, hatte als Tip 
eine Neuformatierung der SD-Karte ergeben, was bei mir jedoch keine 
Änderung ergab. Der Schreiber dort hatte es schließlich gelöst, nachdem 
er alles auf 3.3V Pegel geändert hatte, bei mir ist das aber beriets der 
Fall.

Hat jemand schon Code für SD-Kartenzugriffe angewendet, vor allem mit 
ff.c und mmc.c? Den Code gibt es offensichtlich für diverse Prozessoren, 
man muß eben nur die SPI-Funktionen selber anpassen. Der CS-Anschluß 
wird übrigens nicht vom der SPI-Hardware des Prozessors bedient, sondern 
von einem separaten Port gesteuert, das ist bei mir auch entsprechend 
gemacht worden, sonst würde ja auch das Lesen von Dateien nicht 
funktionieren.

Gruß
Andy

von Forist (Gast)


Lesenswert?

Andreas W. schrieb:
> mmc.c (langer Code)

Das brauchst du nicht extra zu schreiben - merkt doch jeder selbst, wenn 
er nach dem Runter-Scrollen seine wunden Finger sieht :-(

Man fragt sich unwillkürlich, wofür es hier im Forum die Möglichkeit 
eines Dateianhanges gibt?

von Stefan F. (Gast)


Lesenswert?

Der Punkt ist, dass du hier hunderte Zeilen Code hast + Hardware ohne 
einen blassen Schimmer, wo der Fehler liegt. Du musst das Problem in 
kleinere Stücke zerlegen.

Also

1) Elektrische Signale auf Qualität prüfen (Oszilloskop, Augendiagramm)
2) Elektrische Signale auf Korrektheit prüfen (Logikanalyzer)
3) Eine Software verwenden, bei der du ganz sicher bist, dass sie mit 
deiner konkreten SD Karte Funktioniert

Also notfalls mit irgendeiner fertigen Open-Source Hard+Software 
beginnen, die fix und fertig geliefert wird. Dann ersetzt du sie 
schrittweise durch deine eigene Hardware, dann erst baust du deine 
Eigene Software ein.

Wenn du das nicht kannst, dann versuche eine andere Library. Zum 
Beispiel die https://www.mikrocontroller.net/articles/AVR_FAT32. Damit 
bin ich glücklicherweise auf Anhieb zurecht gekommen.

von Andreas W. (andy_w)


Lesenswert?

Hallo,
die andere Library AVR_FAT32 hat das Problem gelöst! Bei mir ist es zwar 
der SAM3X8E, aber das war kein großes Problem. Die hardwarespezifischen 
Funktionen mußte ich ja sowieso umschreiben, ich benutze ja sogar eigene 
SPI-Funktionen. Und das spielt alles schön zusammen. Daß man nur eine 
Datei zur gleiche Zeit offen haben kann (es gibt keinen Filepointer wie 
z.B. in C) macht nichts aus, ich lese Dateien nacheinander und schließe 
eine, bevor die nächste dran ist. Auch das Fehlen einer Meldung, wenn 
beim ffread() das Ende der Datei erreicht ist, ist kein großes Problem, 
man muß eben die Größes des Files lesen und herunterzählen.

Dafür ist der Code erheblich sauberer und übersichtlicher als der Code 
vom LPCXpresso (ff.c und mmc.c) geschrieben. Auch die Quittung von der 
alten Funktion xmit_datablock (die heißt hier jetzt anders) wird hier 
mehrfach abgefragt, bis sie kommt (bis zu 20-mal), die Änderung hatte 
ich im alten Code auch schon versucht, dort ohne Erfolg. Wahrscheinlich 
hat beim alten Code die Funktion dann tatsächlich geschrieben, ich 
vermute den Fehler inzwischen irgendwo in ff.c auf höherer Ebene, wo 
z.B. die FAT durchwühlt wird.

Auf jeden Fall ist die neue Library sehr brauchbar und funktioniert 
inzwischen in meiner originalen Umgebung mit allem Drum und Dran.

Gruß
Andy

von Andreas W. (andy_w)


Lesenswert?

Hallo,
leider zu früh gefreut... Eine Datei auf eine leere Karte schreiben 
funktioniert. Sind bereits andere Dateien drauf, werden die teilweise 
gelöscht oder teilweise zerstört oder seltsam umbenannt. Da geht also 
auch in der FAT bzw. in den Namenstabellen was schief.

Wenn ich nur Dateien lese und keine schreibe, funktioniert das beim 
ersten mal nach dem Einschalten und Aufruf von mmc_init() und 
fat_loadFatData() sehr gut. Aber nach Ausschalten und erneut einschalten 
sind die Dateien ebenfalls korrumpiert. Im Beispiel in der Funktion 
main() ist aber keine Art unmount oder so vorhanden. Gibt es da etwas 
entsprechendes, das dafür sorgt, daß alle Änderungen auf der Karte 
landen, bevor ausgeschaltet wird?

Evtl. hängt das auch mit den zerstörten Dateien beim Nur-Lesen zusammen. 
Wenn das nicht geht, werde ich wieder zur alten LPCXpresso Library 
zurückkehren, da funktioniert es wenigstens, solange man nur liest und 
nicht schreibt. Da das Ganze eine Fernbedienung für einen 
Surroundverstärker wird und die über Funk in beiden Richtungen 
kommunizieren wird, werden die zu schreibenden Daten dann wohl zum 
Verstärker gefunkt und dort gespeichert. Dort werkelt ein Raspberry Pi 
und da funktioniert das mit der SD-Karte... Ist aber irgendwie blöd, 
wenn es keine Library für Atmel gibt, die vernünftig mit SD-Karten 
klarkommt.

Gruß
Andy

von Stefan F. (Gast)


Lesenswert?

Die Library ist in Ordnung, das weiss ich aus eigener Erfahrung. Kommen 
wir zurück zur Qualität der elektrischen Signale. Wie sieht es dort aus?

von Andreas W. (andy_w)


Lesenswert?

Hallo,
die elektrische Qualität ist gut, es sind auch keine Pegelwandler 
erforderlich, da der Arduino Due mit 3.3V läuft. Was noch sein kann, ist 
der SPI-Modus, d.h. welche Polarität und welchen Ruhepegel der Takt hat 
und bei welchen Flanken Daten gelesen und geschrieben werden. Es gibt da 
insgesamt 4 Möglichkeiten. Da werde ich noch genauer weiterforschen.

Momentan hängt die SD-Karte direkt an den Portpins, der Buffer ist 
momentan nicht benutzt, um den als Fehlerquelle auszuschließen. Der wird 
erst nötig, wenn auch noch andere SPI-Hardware dazukommt, denn der 
Arduino Due hat nur einen SPI-Port. Mit dem Buffer trenne ich die 
SD-Karte komplett vom Bus, wenn ich den anderweitig benutze, den 
SD-Karten mögen es nicht unbedingt, wenn nebenher ohne CS Aktivität auf 
den Daten und Taktleitungen vorhanden ist.

Die Library vom LPCXpresso scheint wohl auch korrekt zu sein, denn 
einmal klappte das Schreiben ja. Da gibt es wohl vor allem Probleme mit 
dem Schreiben eines Sektors.

Gruß
Andy

von Stefan F. (Gast)


Lesenswert?

Stromversorgung? SD Karten brauchen kurzzeitig mitunter mehr Strom als 
du erwartest. Ich hatte mal 300mA gemessen.

von Andreas W. (andy_w)


Lesenswert?

Hallo,
Stromversorgung dürfte ausreichend sein. Der SPI-Modus war wohl richtig, 
aber die Clockfrequenz des SPI-Busses war mit ca. 2.5MHz wohl zu hoch, 
bei der Initialisierung sollen es ja 100-400kHz sein. Nachdem ich das 
geändert habe, lief das Lesen mit der Library vom LPCXpresso 
einwandfrei. Schreiben aber nicht, da habe ich die Schreibroutine für 
einen Block im Verdacht, die ist sehr "dreckig" programmiert...

Dann habe ich eine Kopie vom Projekt angelegt und das auf die oben 
angegebene AVR-FAT32 Library umgestellt. Das läuft mit Lesen auch, 
allerdings nur, solange nicht versucht wird, nicht vorhandene Dateien zu 
lesen. Leider ist keine genauere Doku zu finden, was man machen soll, 
wenn z.B. ein Öffnen der Datei nicht klappt. Muß dann z.B. trotzdem nach 
dem ffopen() ein ffclose() folgen? Momentan mache ich das nicht 
(schließlich wurde die Datei ja nicht geöffnet) und offensichtlich wird 
dann die Filestruktur zerstört. Muß also doch auch bei vergeblichem 
ffopen() ein ffclose() folgen? Und was macht man in anderen Fällen, wenn 
es eine Fehlermeldung gibt? Schadet ein zusätzliches ffclose(), obwohl 
keine Datei offen ist oder passiert dann nichts? Schreiben funktionierte 
noch nicht, bis dahin bin ich noch nicht gekommen. Momentan läuft der 
Zugriff durchgehend mit 400kHz, das Hochsetzen der Taktfrequenz nach dem 
Init mache ich erst, wenn alle Dateizugriffe funktionieren.

Nachtrag: ffclose genügt nicht. Nach dem ersten Einschalten kann ich 
alle Dateien problemlos lesen. Nach Power off und erneutem Power on sind 
Dateien verschwunden und die Filestruktur wohl auch angeknackst. Muß man 
vor dem Power off noch etwas machen, um eine Zerstörung des Filesystems 
zu verhindern? Im Beispielcode findet in der Funktion main() nach dem 
letzten Zugriff nichts mehr statt.


Gruß
Andy

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Hast du mal die Kommentare im Quelltext gelesen? Die sind hilfreich.

Ich zittiere:

// schliesst die datei operation ab. eigentlich nur noetig wenn 
geschrieben/ueberschrieben wurde. es gibt 2 moeglichkeiten :
// 1. die datei wird geschlossen und es wurde ueber die alte datei länge 
hinaus geschrieben.
// 2. die datei wird geschlossen und man war innerhalb der datei 
groesse, dann muss nur der aktuelle sektor geschrieben werden.
// der erste fall ist komplizierter, weil ermittelt werden muss wie 
viele sektoren neu beschrieben wurden um diese zu verketten
// und die neue datei laenge muss ermitt weden. abschließend wird 
entweder (fall 2) nur der aktuelle sektor geschrieben, oder
// der aktuallisierte datei eintrag und die cluster (diese werden 
verkettet, siehe fileUpdate() ).

Und wenn du Dir den Quelltext anschaust, siehst du, was die Funktion 
genau macht:

1) Beenden einer Multi-Block Operation (falls MMC_MULTI_BLOCK==TRUE && 
MMC_OVER_WRITE==FALSE)
2) Flush data (also den Schreib-Puffer)
3) Setze file.cntOfByte=0 und file.seek=0;

Langer Rede kurzer Sinn: Diese Methode musst du nur nach 
Schreiboperationen aufrufen. Also nach Fehlern beim lesen oder Öffnen 
nicht.

von Stefan F. (Gast)


Lesenswert?

> Muß man vor dem Power off noch etwas machen, um eine Zerstörung
> des Filesystems zu verhindern?

Normalerweise nicht, hab ich noch nie gemacht.

Wenn die Karte nicht getaktet (also nicht angesprochen) wird, kann sie 
auch keine Daten veändern. Es sei denn, die Spannungsversorgung macht 
ganz üble Sachen.

Hast du mal eine andere SD karte versucht? Es ist bekannt, dass viele SD 
Karten im SPI Modus nicht korrekt funktionieren. Aus dem Bauch heraus 
würde ich schätzen, dass jede dritte nicht geht.

von Falk B. (falk)


Lesenswert?

@Stefan Us (stefanus)

>Hast du mal eine andere SD karte versucht? Es ist bekannt, dass viele SD
>Karten im SPI Modus nicht korrekt funktionieren. Aus dem Bauch heraus
>würde ich schätzen, dass jede dritte nicht geht.

Das halte ich für ein Gerücht, in die Welt gesetzt von "Experten" die 
mit den frikeligsten Adaptern und den schlechtesten Schaltungen 
(Pegelwandler mit Widerständen etc.) arbeiten.

von Andreas W. (andy_w)


Lesenswert?

Hallo,
inzwischen habe ich das auch gesehen und bin auch die Source von mmc.c 
fast komplett durchgegangen, nur die Multiblockoperationen habe ich 
nicht beachtet, da die bei mir gar nicht aktiviert sind. Dabei bin ich 
auch über die Funktion spi_init() gestolpert. Die pfuscht in 
irgendwelchen SPI-Registern des Prozessors herum obwohl ich den SPI-Bus 
selber mit eigenen Funktionen initialisiere. Das wird also als nächstes 
komplett auskommentiert, auch die Funktionen, die die Taktfrequenz 
verstellen, da kommt dann was eigenes dahin, wenn es erst einmal mit 
400kHz klappt. Vielleicht hilft das schon. Ansonsten habe ich endlich 
eine brauchbare SPI-Spec gefunden (war was mit sdcard.org, Files heißen 
z.B. Part1_410.pdf). Der vorhandene Code widerspricht dieser Spec auf 
den ersten Blick nicht, ich muß mal die entsprechenden Funktionen (cmd 
senden, Block lesen und schreiben, Initialisierung) mit der Library vom 
LPCXpresso vergleichen, was da für Unterschiede vorhanden sind.

Ich würde auf jeden Fall die AVR-FAT32 Library bevorzugen, wenn sie 
funktioniert, da die erheblich besser geschrieben wurde und auch mehr 
Kommentare enthält. Die andere ist fast gar nicht kommentiert und 
dokumentiert, da gab es nur noch ein Beispielprogramm...

Gruß
Andreas

von Falk B. (falk)


Lesenswert?

@Andreas W. (andy_w)

>inzwischen habe ich das auch gesehen und bin auch die Source von mmc.c
>fast komplett durchgegangen, nur die Multiblockoperationen habe ich
>nicht beachtet, da die bei mir gar nicht aktiviert sind.

Hä? Die macht er automatisch, wenn du größere Datenblöcke schreibst. 
Diese Funktionen MÜSSEN korrekt sein. Da kann man nichts abschalten!

Das FATfs von Elm Chan funktioniert Prima, man muss nur die hardwarenahe 
Anpassung an den jeweiligen Prozessor machen.

Beitrag "Re: Problem mit Micro-SD-Karte"

>selber mit eigenen Funktionen initialisiere. Das wird also als nächstes
>komplett auskommentiert, auch die Funktionen, die die Taktfrequenz
>verstellen, da kommt dann was eigenes dahin, wenn es erst einmal mit
>400kHz klappt.

Ob das so sinnvoll ist? Der Meister aus dem fernen osten hat sich dabei 
schon was gedacht. Man muss die jeweiligen Funktion an die Hardware 
anpassen, aber NICHTS auskommentieren!

>z.B. Part1_410.pdf). Der vorhandene Code widerspricht dieser Spec auf
>den ersten Blick nicht, ich muß mal die entsprechenden Funktionen (cmd
>senden, Block lesen und schreiben, Initialisierung) mit der Library vom
>LPCXpresso vergleichen, was da für Unterschiede vorhanden sind.

Vergiss es. Die höheren Funktion sind OK. Dein Problem liegt in der 
Hardwareanpassuung der untersten Funktionen!

>Ich würde auf jeden Fall die AVR-FAT32 Library bevorzugen, wenn sie
>funktioniert, da die erheblich besser geschrieben wurde und auch mehr
>Kommentare enthält.

Ansichtsache.

> Die andere ist fast gar nicht kommentiert und
>dokumentiert, da gab es nur noch ein Beispielprogramm...

Quark.

http://elm-chan.org/fsw/ff/00index_e.html

von Stefan F. (Gast)


Lesenswert?

> Hä? Die macht er automatisch, wenn du größere Datenblöcke schreibst.
> Diese Funktionen MÜSSEN korrekt sein. Da kann man nichts abschalten!

Doch, kann man. Es gibt einen ganzen Block von #defines, mit dem man 
einzelne Feature deaktivieren kann.

von Andreas W. (andy_w)


Lesenswert?

Hallo,
genau so ist es, ich habe in der Konfiguration MMC_MULTI_BLOCK auf FALSE 
stehen, das war auch defaultmäßig schon so. Und alle 
Multiblockfunktionen stehen zwischen #ifdef MMC_MULTI_BLOCK und #endif, 
werden also gar nicht compiliert. Multiblock wird durch mehrfache 
Aufrufe der Funktionen für einen einzelnen Block realisiert.

Mit der "anderen" Library meine ich die vom LPCXpresso, nicht die von 
Elm Chan.

Ich muß die SPI-Grundfunktionen erheblich anpassen, da die wohl eher für 
einen anderen Prozessor gedacht sind, ich weiß nicht, ob das überhaupt 
für einen SAM3X8E so direkt paßt. Es sind ja vor allem die Funktionen 
zum Lesen und Schreiben eines einzelnen Bytes über den SPI-Bus sowie 
dessen Initialisierung. Ebenso das Ansprechen des Portpins, der das 
CS-Signal erzeugt, der CS von der SPI-Hardware wird nicht benutzt, so 
ist es ja auch im Original schon vorgesehen.

Gruß
Andy

von Stefan F. (Gast)


Lesenswert?

Ich hatte das mal auf einen Xmega portiert, das war auch relativ 
einfach. Es sind ja nur wenige Code-Stellen, die Hardwarespezifisch 
sind.

von Falk B. (falk)


Lesenswert?

@ Andreas W. (andy_w)

>Ich muß die SPI-Grundfunktionen erheblich anpassen, da die wohl eher für

OMG! Was für eine LAST! Geht dem Mann eine Heldenmedaille!

Menschenskinder, diese Aufgabe haben vor dir schon Tausende von Leuten 
gemacht und nicht so ein Gewese gemacht. Das bischen Kram dauert 
vielleicht 1-2 Stunden, dann sollte das laufen. (jaja, ich habe selber 
den Beweis gelifert, dass es auch mal länger dauern kann, aber da war 
ich selber Schuld. Man sollte halt ab und an etwas langsamer und 
gründlicher arbeiten).

>dessen Initialisierung. Ebenso das Ansprechen des Portpins, der das
>CS-Signal erzeugt, der CS von der SPI-Hardware wird nicht benutzt, so
>ist es ja auch im Original schon vorgesehen.

Ja und? Das ist EINE einfachste Zeile in einem #define! Sowas allein zu 
hier zu erwähnen ist Energieverschwendung.

von Falk B. (falk)


Lesenswert?

@ Stefan Us (stefanus)

>Ich hatte das mal auf einen Xmega portiert, das war auch relativ
>einfach. Es sind ja nur wenige Code-Stellen, die Hardwarespezifisch
>sind.

EBEN!

Beitrag "Re: Elm Chan FatFs SD Karte auf Keil MCB2300 LCP2378"

von Andreas W. (andy_w)


Lesenswert?

Hallo,
schalte ich die Write-Funktionalität ab (MMC_WRITE FALSE), dann 
funktioniert das Lesen einwandfrei. Mit MMC_WRITE = TRUE schreibt 
ffclose Sektoren auch beim Lesen einer Datei, das passiert in 
fflushFileData(), das in ffclose aufgerufen wird - aber eben nur, wenn 
MMC_WRITE = TRUE ist.

Warum werden eigentlich beim reinen Lesen Sektoren geschrieben? Ich habe 
das mit dem Debugger und einem gesetzten Breakpoint in der Funktion zum 
Sektorschreiben festgestellt. Kein Wunder, daß dann irgendwann beim 
Lesen die Sektoren evtl. verändert werden und das Filesystem nicht mehr 
konsistent ist, zumindest bei Dateien, die man lesen will und die noch 
nicht existieren. Mit MMC_WRITE = FALSE werden nie Sektoren geschrieben 
und das Lesen funktioniert trotzdem (warum auch nicht).

Gruß
Andy

von Falk B. (falk)


Lesenswert?

@ Andreas W. (andy_w)

>Warum werden eigentlich beim reinen Lesen Sektoren geschrieben?

Das werden sie nicht. Das ist ein Irrtum.

>Ich habe
>das mit dem Debugger und einem gesetzten Breakpoint in der Funktion zum
>Sektorschreiben festgestellt.

Dann hast du noch mehr Bugs.

von Andreas W. (andy_w)


Lesenswert?

Hallo,
wieso landet dann der Debugger schon beim Lesen der ersten Datei, die 
auch vorhanden ist, in der Funktion zum Schreiben eines Sektors? Das 
sogar mehrere Male, indirekt aufgerufen über ffclose und fflushFileData. 
Vor dem ersten Aufruf war fat.bufferDirty = TRUE.

Die SD-Karte habe ich auf dem PC jedesmal neu formatiert, allerdings nur 
schnell, und dann die Dateien wieder draufgeschrieben, dann sollten doch 
die relevanten Sektoren (FAT, Directories, vorhandene Dateien) korrekt 
sein.

Ich werde mit dem Debugger nun mal das ffclose komplett durchsteppen, 
wer weiß, was da noch für Überraschungen lauern...

Gruß
Andy

von Stefan F. (Gast)


Lesenswert?

>  Mit MMC_WRITE = TRUE schreibt
> ffclose Sektoren auch beim Lesen einer Datei.

Dann hast du mit hoher Warscheinlichkeit einen Stack-Überlauf. Der führt 
dazu, dass Variablen überschrieben werden.

Benutze mal die Suchfunktion, um Beiträge zu finden, wie man das prüft.

von Andreas W. (andy_w)


Lesenswert?

Hallo,
inzwischen habe ich das Problem gefunden: ffopen merkt sich nicht, ob 
eine Datei zum Lesen oder zum Schreiben geöffnet wurde. Beim ffclose 
wird unterschiedslos dann fflushFileData() aufgerufen. Und die Funktion 
schreibt immer mindestens einen Sektor, zumindest den zuletzt gelesenen, 
denn es könnte ja sich was beim Schreiben geändert haben. Noch 
schlimmer: ffread() gibt keine Rückmeldung darüber, ob man schon das 
Ende der Datei erreicht hat. Bei mir kam es vor, daß evtl. für ein 
Zeichen mehr ffread() aufgerufen wurde. Damit war die neue Dateilänge im 
ein Byte länger als die ursprüngliche und entsprechend wurde dann auch 
ein Sektor geschrieben. Inzwischen habe ich meine Leseroutinen so 
geändert, daß nicht mehr Daten gelesen werden als der Dateilänge 
entspricht.

Ich habe daher in der Struktur file ein weiteres Element hinzugefügt (in 
der Datei mmc-config.h, Kommentare hier im Listing zum Teil wegen zu 
langer Zeilen entfernt und etwas formatiert, da Tabs hier nicht richtig 
passen):
1
extern struct File_t
2
{
3
   uint16_t     cntOfBytes;
4
   uint32_t     seek;
5
   uint32_t     currentSectorNr
6
   uint32_t     length;
7
   uint8_t      *name;
8
   uint8_t      row;
9
   uint32_t     firstCluster;
10
   uint32_t     entrySector;
11
   uint8_t      rw_flag;        // das ist neu von mir
12
}file;

in file.rw_flag wird das 'r' bzw. 'c' vom ffopen gespeichert. Hier der 
Anfang von ffopen (Datei file.c):
1
uint8_t ffopen( uint8_t name[], uint8_t rw_flag)
2
{
3
   uint8_t file_flag = fat_loadFileDataFromDir(name);
4
   if (file_flag==TRUE && rw_flag == 'r')
5
   {
6
      fat_getFatChainClustersInRow( file.firstCluster );
7
      file.name = name;
8
      file.rw_flag = rw_flag;  // das ist neu von mir
9
......

in ffclose wird nun file.rw_flag abgefragt und nur, wenn geschrieben 
wurde ('c'), wird fflushFileData() aufgerufen. Beim Lesen ist das völlig 
unnötig und stellt nur eine mögliche, zusätzliche Fehlerquelle dar.
1
uint8_t ffclose()
2
{
3
   #if (MMC_MULTI_BLOCK==TRUE && MMC_OVER_WRITE==FALSE)
4
      if(multiblock_flag == TRUE)
5
      {
6
         mmc_multi_block_stop_read();
7
      }
8
      else
9
      {
10
         mmc_multi_block_stop_write();
11
      }
12
   #endif
13
14
   #if (MMC_WRITE==TRUE)
15
      if (file.rw_flag == 'c')  // die if-Abfrage ist neu von mir
16
      {              // nur machen, wenn ffopen mit 'c' aufgerufen wurde
17
         fflushFileData();
18
      }
19
   #endif
20
21
   file.cntOfBytes = 0;
22
   file.seek = 0;
23
24
   return TRUE;
25
}

Nun funktioniert zumindest schon mal das Lesen einwandfrei. Beim 
Schreiben eines Sektors gibt es wohl noch Probleme, die noch zu klären 
sind, aber so findet das Lesen endlich ohne Sektorschreiben statt.

Gruß
Andy

von Falk B. (falk)


Lesenswert?

@ Andreas W. (andy_w)

>inzwischen habe ich das Problem gefunden: ffopen merkt sich nicht, ob
>eine Datei zum Lesen oder zum Schreiben geöffnet wurde. Beim ffclose
>wird unterschiedslos dann fflushFileData() aufgerufen. Und die Funktion
>schreibt immer mindestens einen Sektor, zumindest den zuletzt gelesenen,
>denn es könnte ja sich was beim Schreiben geändert haben. Noch
>schlimmer: ffread() gibt keine Rückmeldung darüber, ob man schon das
>Ende der Datei erreicht hat.

Das kann ich kaum glauben. Und wenn, dann wäre die lib extrem buggy und 
pre Alpha Stadium.

Nimm Elm Chans FATfs und sein glücklich, das Ding ist solide.

>Nun funktioniert zumindest schon mal das Lesen einwandfrei. Beim
>Schreiben eines Sektors gibt es wohl noch Probleme, die noch zu klären
>sind, aber so findet das Lesen endlich ohne Sektorschreiben statt.

Warum nimmst du eine Lib, die nicht sonderlich verbreitet und getestet 
ist?

von Andreas W. (andy_w)


Lesenswert?

Hallo,
ich dachte, wenn die Lib hier in den Artikeln enthalten ist, wird die 
wohl auch brauchbar sein. Ich habe jetzt noch die Lib von Elm Chan 
downgeloaded, das wird aber die letzte Lib sein, die ich noch 
ausprobieren werde. Die Anpassungen sind ja zum Glück nahezu dieselben, 
also zum großen Teil Copy&Paste und relativ wenig Zeilen. Nur die 
Aufrufe muß ich wieder anpassen, da Funktionen wie f_open usw. andere 
Parameter haben, wahrscheinlich auch wieder ein Filepointer. Das wird 
aber erst morgen was, hoffentlich ist die besser und hoffentlich 
schreibt die keine Sektoren, wenn man nur liest...

Gruß
Andreas

von Andreas W. (andy_w)


Lesenswert?

Hallo,
endlich scheint es zu funktionieren. Zumindest kann ich jetzt Files 
lesen und schreiben, das muß sich jetzt aber noch für längere Zeit 
bewähren.

Die Library von Elm Chan funktionierte beim Nurlesen von Dateien auf 
Anhieb, nachdem ich alle Hardwareanpassungen gemacht habe, da werden 
dann auch keine Sektoren beschrieben. Schreiben funktionierte aber immer 
noch nicht. Zunächst stellte ich fest, daß meine SPI-Funktion schon 
einen Return machte, bevor die letzten SPI-Takte zudene waren, Bei der 
Funktion, die SPI gleichzeitig liest und schreibt (das ist eigentlich 
der Normalfall) war das nicht, nur bei den Spezialfunktionen für die 
SD-Karte, die nur schreiben (dann werden die gelesenen Daten ignoriert) 
und nur lesen (dann wird immer 0xff geschrieben) fehlte beim Copy&Paste 
am Schluß das Warten auf die vollendete SPI-Übertragung...

Trotzdem funktionierte das Schreiben nicht. Ich stellte fest, daß beim 
Schreiben eines Sektors das erwartete Statusbyte am Ende nicht sofort, 
sondern erst beim erneuten Lesen eines Bytes kommt, xmit_datablock() 
verlangt das Stausbyte aber sofort. Ich habe daher den Code der Funktion 
entsprechend angepaßt, sodaß die auch zufrieden ist und ohne 
Fehlermeldung zurückkehrt, wenn das Statusbyte erst beim 2. oder 3. Mal 
kommt (in Datei sdmm.c):
1
/*-----------------------------------------------------------------------*/
2
/* Send a data packet to the card                                        */
3
/*-----------------------------------------------------------------------*/
4
static int xmit_datablock                       // 1:OK, 0:Failed
5
(
6
   const BYTE *buff,                            // 512 byte data block to be transmitted
7
   BYTE token                                   // Data/Stop token
8
)
9
{
10
   BYTE d[2];
11
12
   if (!wait_ready()) return 0;
13
14
   d[0] = token;
15
   xmit_mmc(d, 1);                 // Xmit a token
16
   if (token != 0xFD)              // Is it data token?
17
   {
18
      xmit_mmc(buff, 512);         // Xmit the 512 byte data block to MMC
19
      rcvr_mmc(d, 2);              // Xmit dummy CRC (0xFF,0xFF)
20
      rcvr_mmc(d, 1);              // Receive data response
21
      if ((d[0] & 0x1F) == 0x05)   // If accepted, return with OK
22
         return 1;
23
      rcvr_mmc(d, 1);              // Receive data response
24
      if ((d[0] & 0x1F) == 0x05)   // If accepted, return with OK
25
         return 1;
26
      rcvr_mmc(d, 1);              // Receive data response
27
      if ((d[0] & 0x1F) == 0x05)   // If accepted, return with OK
28
         return 1;
29
//      if ((d[0] & 0x1F) != 0x05) // If not accepted, return with error
30
//         return 0;
31
   }
32
33
   return 0;
34
}

die auskommentierten Zeilen waren ursprünglich im Code, jetzt sind es 
stattdessen die 9 Zeilen davor.

Und nach diese Änderung funktionierte auch das Schreiben, Ähnlichen Code 
für xmit_datablock() habe ich auch schon in der Library, die hier im 
Artikel AVM FAT32 verlinkt ist, vorgefunden, dort wurde auch mehrmals 
das Statusbyte gelesen, bis die richtige Antwort kam und erst nach n 
vergeblichen Versuchen gab es eine Fehlermeldung.

Vielleicht hilft das auch anderen, evtl. ist meine SPI-Funktion zu 
schnell gewesen.

Ob das auch mit schnellerem SPI-Takt funktioniert, werde ich später noch 
ausprobieren, eine Umschaltung nach der SD-Initialisierung ist 
jedenfalls schon vorbereitet.

Gruß
Andy

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Andreas W. (andy_w)

>noch nicht. Zunächst stellte ich fest, daß meine SPI-Funktion schon
>einen Return machte, bevor die letzten SPI-Takte zudene waren,

Das ist dein Fehler.

>Trotzdem funktionierte das Schreiben nicht. Ich stellte fest, daß beim
>Schreiben eines Sektors das erwartete Statusbyte am Ende nicht sofort,
>sondern erst beim erneuten Lesen eines Bytes kommt, xmit_datablock()
>verlangt das Stausbyte aber sofort. Ich habe daher den Code der Funktion
>entsprechend angepaßt,

FALSCH! Finger weg von diesen Funktionen. DIe funtkionieren! Deine 
Aufgabe besteht NUR in der Anpassung der aller untersten SPI-Zugriffe!

>Vielleicht hilft das auch anderen, evtl. ist meine SPI-Funktion zu
>schnell gewesen.

Nö, du hast garantiert einen Puffer oder was ähnliches bei deinem 
SPI-Modul übersehen oder du fragst das falsche Statusbit ab. Oder du 
hast vergessen, VOR der Übertragung das Statusbit zu löschen. Bring das 
in Ordnung!

von Andreas W. (andy_w)


Lesenswert?

Hallo,
das erste gelesene Statusbyte hat immer den Wert 0xff. Das ist aber gar 
kein Stausbyte, das hat das Format x,x,x,0,b,b,b,1 (binär). b,b,b kann 
010 (no error), 101 (CRC error) oder 110 (data rejected due to write 
error) sein. 0xff deutet darauf hin, daß das Statusbyte zu dem Zeitpunkt 
noch nicht kommt, sondern erst ein Byte später. Ein Timingproblem ist 
das nicht, denn im Debugger, wo vor dem Lesen des Statusbytes beim 
händischen Durchsteppen mehrere Sekunden vergehen passiert genau das 
gleiche. Da ich an der Funktion sonst nichts geändert habe, ist die Zahl 
der Bytes, die davor geschrieben werden, gleich, es kann also nicht ein 
gelesenes Byte fehlen.

Hat jemand das auch zusammen mit der SPI-Hardware des Prozessors benutzt 
oder nur die Softwarevariante, in der alle Portpins per Software bedient 
werden? Wenn ja, würde mich interessieren, welche SPI-Funktionen mit 
welchen Parametern aufgerufen werden (müßte irgendwie spi_transmit() 
oder so heißen. Meine Funktion wartet, bis genau soviel Transmit 
Register empty und Receive Register full abgearbeitet wurden, wie der 
Blocklänge der SPI-Übertragung entspricht. Anschließend warte ich noch, 
bis das Transmit Register auch gesendet wurde. Was mich wundert: erst 
dann kann eigentlich das letzte Byte ins Receive Register geschrieben 
werden, warum sind aber schon soviele Receive Register full Medungen 
vorher gekommen, da die spi_transmit Funktion schon während der letzten 
Clocks sich beendet hatte?

Das Datenblatt des SAM3X8E verrät zu dem Thema keine genaueren Details, 
es gibt auch kein Fußdiagramm, wie man so einen Transfer durchziehen 
sollte. Die Examples vom Atmelstudio schreiben einfach blind das 
Transmit register und warten dann nur auf Receive Register full und 
lesen dann das Receive Register, dann wird gleich das nächste Byte 
geschrieben usw. Leider ist kaum herauszukriegen, wie SPI vorher 
konfiguriert wird, ich brauche ein SPI, das bei mehreren Bytes 
lesen/schreiben den CS ununterbrochen auf Low läßt und erst nach dem 
letzten Byte auf High geht, sonst funktioniert normale SPI-Hardware 
nicht.

Wenn tatsächlich momentan alle Bytes um eine Stelle verspätet gelesen 
werden, wundere ich mich aber, warum es trotzdem einwandfrei 
funktioniert, zumindest mit der Änderung in xmit_datablock().

Gruß
Andy

von Andreas W. (andy_w)


Lesenswert?

Hallo,
die SPI-Beschreibung im Datenblatt vom SAM3X8E ist eine Katastrophe! Es 
ist zwar viel dokumentiert, aber die Feinheiten mit den Statusbits, 
Kombinationen mit einigen Konfigurationsbits usw. sind nicht erklärt. Es 
gibt vor allem kein Flußdiagramm oder einen Beispielcode, wie man z.B. 
einen Masterzugriff auf die Beine stellt. So etwas ist auch nicht in den 
Application Notes von Atmel zu finden, es gibt überhaupt nur eine, die 
den SPI betrifft und da drückt man sich ebenfalls um genau diese 
Informationen. Wenn man den SPI-Bus ausreizen möchte, also z.B. für 
verschiedene SPI-Hardwareslaves unterschiedliche Settings nutzen muß 
(z.B. NCPHA, CPOL und Taktfrequenz individuell), helfen einem die 
Atmeltreiber nicht wirklich weiter, da kaum Doku vorhanden ist...

Aber inzwischen habe ich es geschafft, jetzt kommt das Statusbyte in 
xmit_datablock() gleich beim ersten Mal. Das war ziemlich nervig zu 
finden, da man die SPI-Funktion nicht richtig debuggen kann, denn die 
Statusbits werden gepollt und beim Debuggen vergeht einfach zuviel Zeit 
zwischen den Befehlen. Schließlich fand ich heraus, daß das Receive 
Register full Flag beim Aufruf machmal auf 1 stand, also mußte dann ein 
Dummyread erfolgen, ebenso war die Abfrage, wann das nächste Byte 
geschrieben und auch gelesen werden kann, anzupassen. So, wie im Example 
vom Atmel funktioniert es jedenfalls nicht, das hatte ich auch probiert.

Außerdem funktioniert auch das Hochsetzen des SPI-Taktes nach dem Init 
der SD-Karte, bei mir auf 10.5MHz. Dazu mußte ich aber etwas in die 
Funktion disk_initialize() eingreifen, am Anfang setze ich eine Variable 
spi_fast auf 0 und am Ende auf 1. Die Aufrufe der SPI-Funktionen selber 
berücksichtigen dann diese Variable und stellen entsprechend die 
Taktfrequenz ein. Das hat seltsamerweise auf Anhieb funktioniert...

Gruß
Andy

von Stefan F. (Gast)


Lesenswert?

> ffopen merkt sich nicht, ob eine Datei zum Lesen oder
> zum Schreiben geöffnet wurde. Beim ffclose
> wird unterschiedslos dann fflushFileData() aufgerufen.

Ich finde auch, dass das nach einem Bug aussieht.

Allerdings sollte der unnötige Schreibzugriff harmlos sein, denn es wird 
der zuletzt gelesene Sektor zurück geschrieben.

Kann es sein, dass du den Inhalt des Buffers unabsichtlich veränderst?
Ich habe hier immer noch den Verdacht, dass du einen Stack-Überlauf 
hast.

von Falk B. (falk)


Lesenswert?

@ Andreas W. (andy_w)

>die SPI-Beschreibung im Datenblatt vom SAM3X8E ist eine Katastrophe! Es
>ist zwar viel dokumentiert, aber die Feinheiten mit den Statusbits,
>Kombinationen mit einigen Konfigurationsbits usw. sind nicht erklärt.

Zeig mal das Datenblatt.

Da du augenscheinlich ziemliche Problem mit dem Verständnis und 
Inbetriebnahme des SPI hast, solltest du erstmal in einem minimalen 
Testprojekt die SPI zum Laufen bringen, vor allem das Warten auf das 
echte Ende einer Übertragung. Dazu braucht man ein Oszi oder 
Logicanalyzer, mit dem Debugger geht das nicht. Mach es ganz klassisch.

CS low
N Bytes senden
CS high

Wenn alles passt und dir kein FIFO oder falsches Statusbit ins Handwerk 
pfuscht, sollte das Signal mit CS und Takt exakt so auf dem 
Oszi/Logicanalyzer zu sehen sein.

von Andreas W. (andy_w)


Lesenswert?

Hallo,
es läuft doch jetzt, auch mit der ursprünglichen Funktion 
xmit_datablock()... Auf dem Oszi sieht es ja jetzt auch einwandfrei aus, 
so einen Minimaltest nur mit SPI-Zugriffen ohne kontaktierter SD-Karte 
hatte ich auch zum Finden des Fehlers laufen.

Es war tatsächlich noch ein Fehler in der SPI-Funktion selber, der 
inzwischen behoben ist. Die Funktion hat sich beim Pollen der Flags 
verzählt und ein Byte mehr (zuerst eins weniger) gesendet als gelesen. 
Es war eben nur ziemlich mühselig, den Fehler zu finden, da es weder 
brauchbare Beispiele (das eine, das ich gefunden hatte, ist definitiv 
falsch und lief beim Probieren auch nicht) noch genaue Erklärungen im 
Datenblatt gibt. Außerdem war das auch nicht zu debuggen, da es in 
Echtzeit ablaufen muß. Die Register sind in der Doku zwar vollständig 
beschrieben, die Funktion der einzelnen Bit und Bitfelder aber nur 
ziemlich knapp, so daß Fragen offen bleiben. Seltsamerweise hat der 
I2C-Bus auf Anhieb richtig funktioniert (da hängen mehrere Slaves dran 
und alle lassen sich problemlos ansprechen), obwohl der eigentlich etwas 
komplizierter als der SPI-Bus ist.

Der Bug bei der anderen Lib kann durchaus harmlos sein, d.h. es werden 
wahrscheinlich identische Daten zurückgeschrieben, wenn man Dateien nur 
liest. Aber unnötige Schreibzugriffe kommen besonders bei Flashspeichern 
nicht gut an, da die die Lebensdauer verkürzen. Außerdem steigt die 
Wahrscheinlichkeit, das Fehler passieren.

Inzwischen läuft die Software jetzt schon seit mehreren Tagen und hat 
bzgl. SD-Karte und SPI keine Fehler mehr gezeigt, sogar das Umschalten 
auf einen höheren Takt nach dem Init funktioniert einwandfrei.

Gruß
Andy

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Kaum macht man es richtig, funktioniert es!

Na dann mal Herzlichen Glückwunsch zur gelungenen Softwareanpassung.

von Stefan S. (mexakin)


Lesenswert?

Hallo,

ich nehm grad auch die elmchan bib in Betrieb, mich würde interessieren 
wie schnell du deinen SPI laufen lässt.

Initialisiert werden meine SD KArten mit unter 400kHz, danach ist je 
vollgas möglich, begrenzt von den SPI Pin Spezifikationen oder 
Bufferschaltungen.

Meine Betriebsgeschwindigkeit ist gerade zum Testen bei 1 MHz, ich habe 
aaber auch Kabel dazwischen, also keine direkte PCB-Anbindung, weswegen 
eben höhere Kapazitäten auftreten, die die Signale verlangsamen.

Merci.

von Falk B. (falk)


Lesenswert?

@ stefan schmitt (mexakin)

>Initialisiert werden meine SD KArten mit unter 400kHz, danach ist je
>vollgas möglich, begrenzt von den SPI Pin Spezifikationen oder
>Bufferschaltungen.

Normale SD(HC)-Karten verkraften max. 25 MHz.

>Meine Betriebsgeschwindigkeit ist gerade zum Testen bei 1 MHz, ich habe
>aaber auch Kabel dazwischen,

Wieviel Meter?

> also keine direkte PCB-Anbindung, weswegen
>eben höhere Kapazitäten auftreten, die die Signale verlangsamen.

Ja und? Selbst mit 1m Kabel kommt man da locker auf 10MHz oder mehr. 
Allerdings wird dann das Thema Wellenwiderstand relevant.

von Stefan S. (mexakin)


Lesenswert?

Mein MSP kann maximal 24 MHz, das habe ich auf Vollgas bezogen ohne es 
euch zu schreiben.

Kabel sind 20cm aktuell, sieht bei 12 MHz SPI schon gut nach Dreieck aus 
mein Clock, erstaunlicherweise geht es noch anständig, daher wollte ich 
mal wissen wie schnell andere ihre SPIs so betreiben, bzw wie andere 
Prozessortechniken wie Atmel, PIC das machen.

von Falk B. (falk)


Lesenswert?

@ stefan schmitt (mexakin)

>Mein MSP kann maximal 24 MHz,

CPU Takt oder SPI Takt?

>Kabel sind 20cm aktuell,

Genug, um sich ins Knie zu schießen ;-)

> sieht bei 12 MHz SPI schon gut nach Dreieck aus

Wie hast du das gemessen? Mit einem 10:1 Tastkopf und einem halbwegs 
schnellen Oszi? (100 MHz++)

>mal wissen wie schnell andere ihre SPIs so betreiben, bzw wie andere
>Prozessortechniken wie Atmel, PIC das machen.

Exakt genau so. SPI ist nix weiter als ein Schieberegister.

von Stefan S. (mexakin)


Lesenswert?

Prozessortakt sind 24 MHz, SPI akt kann ich also je nach Teiler auch 24 
MHz machen.

gemessen an einen 10:1 mit schnellem Oszi.

Und die Schieberegister, sind ja sicherlich von Silizium zu Silizium 
leicht anders aufgebaut, daher hätte es mich interessiert, ob da manche 
einfach schneller so nen Takt raushauen können, oder ob alle da begrenzt 
sind.

von Falk B. (falk)


Lesenswert?

@ stefan schmitt (mexakin)

>Prozessortakt sind 24 MHz, SPI akt kann ich also je nach Teiler auch 24
>MHz machen.

Sicher?

>gemessen an einen 10:1 mit schnellem Oszi.

Wie schnell?
Poste mal ein Bild.

>Und die Schieberegister, sind ja sicherlich von Silizium zu Silizium
>leicht anders aufgebaut,

Unwesentlich.

> daher hätte es mich interessiert, ob da manche
>einfach schneller so nen Takt raushauen können, oder ob alle da begrenzt
>sind.

Alles auf dieser Welt ist begrenzt. Oder wie es mal ein schlauer Kopf 
sehr treffend formulierte.

"Es gibt zwei Dinge, die unendlich sind. Das Universum und die Dummheit 
der Menschen. Bei ersten bin ich mir allerdings nicht ganz sicher".

Albert Einstein

von Andreas W. (andy_w)


Lesenswert?

Hallo,
bei mir läuft der SPI dann mit 10.5MHz (84MHz/8), höher habe ich noch 
nicht probiert. Die SD-Fassung ist mit ca. 7cm langen Leitungen per 
Stift-/Buchsenreihe mit der Leiterplatte verbunden, von dort bis zum 
Arduino sind es noch einmal ca. 5cm. Momentan hängt die SD-Karte direkt 
dran, später kommt ein Buffer (74LVC245) dazwischen, damit ich die 
SD-Karte bei anderen SPI-Zugriffen elekrisch abtrennen kann, Konkurrenz 
mit anderer SPI-Hardware mögen die nicht. Dann werde ich mal sehen, wie 
hoch ich mit der Frequenz gehen kann, 25MHz dürfte dann schon 
grenzwertig werden. Das bringt dann aber auch nicht mehr soviel 
zusätzliche Geschwindigkeit, da der Overhead zwischen den Zugriffen auf 
512Byte Sektoren schon sehr ins Gewicht fällt. Da meine Dateien nicht 
sooo groß sind, macht das nicht soviel aus.

Gruß
Andy

von Stefan F. (Gast)


Lesenswert?

Ich habe mal 32Mhz (mit 3cm Leitungslänge) probiert, klappte prima. Aber 
für die Produktion bin ich dann doch lieber auf 20Mhz runter gegangen.

von Falk B. (falk)


Lesenswert?

@Andreas W. (andy_w)

>dran, später kommt ein Buffer (74LVC245) dazwischen, damit ich die
>SD-Karte bei anderen SPI-Zugriffen elekrisch abtrennen kann, Konkurrenz
>mit anderer SPI-Hardware mögen die nicht.

Das ist Unsinn. Man muss einfach nur CS auf High setzen, schon ist Ruhe. 
Ich habe mehrere Projekte gemacht, wo die SD-Karte als ganz normaler 
SPI-Slave am SPI-Bus mit anderen Teilnehmern hing, alles kein Problem.

> Dann werde ich mal sehen, wie
>hoch ich mit der Frequenz gehen kann, 25MHz dürfte dann schon
>grenzwertig werden.

Warum? Wenn 10 MHz sauber laufen und die Signale gut aussehen spricht 
rein gar nichts gegen 25 MHz.

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.