Arduino FatFS
SDArduinoSPIIO.h
1 
18 // This code was ported by kiwih from a copywrited (C) library written by ChaN
19 // available at http://elm-chan.org/fsw/ff/ffsample.zip
20 //(text at http://elm-chan.org/fsw/ff/00index_e.html)
21 
22 // This file provides the FatFs driver functions and SPI code required to manage
23 // an SPI-connected MMC or compatible SD card with FAT
24 
25 // It is designed to be wrapped by a cubemx generated user_diskio.c file.
26 
27 #pragma once
28 
29 #include "BaseIO.h"
30 #include "SPI.h"
31 #include "sdcommon.h"
32 
33 #ifndef _USE_WRITE
34 #define _USE_WRITE 1 /* 1: Enable disk_write function */
35 #endif
36 
37 #ifndef _USE_IOCTL
38 #define _USE_IOCTL 1 /* 1: Enable disk_ioctl function */
39 #endif
40 
41 namespace fatfs {
42 
48 class SDArduinoSPIIO : public BaseIO {
49  public:
50  SDArduinoSPIIO(int cs = -1, SPIClass &spi = SPI) { setSPI(cs, spi); }
51  SDArduinoSPIIO(SPIClass &spi) { setSPI(spi); }
52 
53  void setSPI(SPIClass &spi = SPI) {
54  this->p_spi = &spi;
55  this->cs = -1;
56  }
57 
58  void setSPI(int cs = -1, SPIClass &spi = SPI) {
59  this->p_spi = &spi;
60  this->cs = cs;
61  if (cs != -1) {
62  pinMode(cs, OUTPUT);
63  }
64  }
65 
66  DSTATUS disk_initialize(BYTE drv /* Physical drive number (0) */
67  ) override {
68  BYTE n, cmd, ty, ocr[4];
69 
70  if (drv != 0) return STA_NOINIT; /* Supports only drive 0 */
71  // assume SPI already init init_spi(); /* Initialize SPI */
72 
73  if (stat & STA_NODISK) return stat; /* Is card existing in the soket? */
74 
75  set_spi_fast(false);
76  for (n = 10; n; n--) xchg_spi(0xFF); /* Send 80 dummy clocks */
77 
78  ty = 0;
79  if (send_cmd(CMD0, 0) == 1) { /* Put the card SPI/Idle state */
80  spi_timer_on(1000); /* Initialization timeout = 1 sec */
81  if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */
82  for (n = 0; n < 4; n++)
83  ocr[n] = xchg_spi(0xFF); /* Get 32 bit return value of R7 resp */
84  if (ocr[2] == 0x01 &&
85  ocr[3] == 0xAA) { /* Is the card supports vcc of 2.7-3.6V? */
86  while (spi_timer_status() &&
87  send_cmd(ACMD41, 1UL << 30)); /* Wait for end of initialization
88  with ACMD41(HCS) */
89  if (spi_timer_status() &&
90  send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
91  for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF);
92  ty =
93  (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* Card id SDv2 */
94  }
95  }
96  } else { /* Not SDv2 card */
97  if (send_cmd(ACMD41, 0) <= 1) { /* SDv1 or MMC? */
98  ty = CT_SD1;
99  cmd = ACMD41; /* SDv1 (ACMD41(0)) */
100  } else {
101  ty = CT_MMC;
102  cmd = CMD1; /* MMCv3 (CMD1(0)) */
103  }
104  while (spi_timer_status() &&
105  send_cmd(cmd, 0)); /* Wait for end of initialization */
106  if (!spi_timer_status() ||
107  send_cmd(CMD16, 512) != 0) /* Set block length: 512 */
108  ty = 0;
109  }
110  }
111  CardType = ty; /* Card type */
112  despiselect();
113 
114  if (ty) { /* OK */
115  set_spi_fast(true); /* Set fast clock */
116  stat = STA_CLEAR; /* Clear STA_NOINIT flag */
117  } else { /* Failed */
118  stat = STA_NOINIT;
119  }
120 
121  return stat;
122  }
123 
124  /*-----------------------------------------------------------------------*/
125  /* Get disk status */
126  /*-----------------------------------------------------------------------*/
127 
128  DSTATUS disk_status(BYTE drv /* Physical drive number (0) */
129  ) override {
130  if (drv) return STA_NOINIT; /* Supports only drive 0 */
131  return stat; /* Return disk status */
132  }
133 
134  /*-----------------------------------------------------------------------*/
135  /* Read sector(s) */
136  /*-----------------------------------------------------------------------*/
137 
138  DRESULT disk_read(
139  BYTE drv, /* Physical drive number (0) */
140  BYTE *buff, /* Pointer to the data buffer to store read data */
141  DWORD sector, /* Start sector number (LBA) */
142  UINT count /* Number of sectors to read (1..128) */
143  ) override {
144  if (drv || !count) return RES_PARERR; /* Check parameter */
145  if (stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
146 
147  if (!(CardType & CT_BLOCK))
148  sector *= 512; /* LBA ot BA conversion (byte addressing cards) */
149 
150  if (count == 1) { /* Single sector read */
151  if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */
152  && rcvr_datablock(buff, 512)) {
153  count = 0;
154  }
155  } else { /* Multiple sector read */
156  if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
157  do {
158  if (!rcvr_datablock(buff, 512)) break;
159  buff += 512;
160  } while (--count);
161  send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
162  }
163  }
164  despiselect();
165 
166  return count ? RES_ERROR : RES_OK; /* Return result */
167  }
168 
169  /*-----------------------------------------------------------------------*/
170  /* Write sector(s) */
171  /*-----------------------------------------------------------------------*/
172 
173 #if _USE_WRITE
174  DRESULT disk_write(BYTE drv, /* Physical drive number (0) */
175  BYTE *buff, /* Ponter to the data to write */
176  DWORD sector, /* Start sector number (LBA) */
177  UINT count /* Number of sectors to write (1..128) */
178  ) override {
179  if (drv || !count) return RES_PARERR; /* Check parameter */
180  if (stat & STA_NOINIT) return RES_NOTRDY; /* Check drive status */
181  if (stat & STA_PROTECT) return RES_WRPRT; /* Check write protect */
182 
183  if (!(CardType & CT_BLOCK))
184  sector *= 512; /* LBA ==> BA conversion (byte addressing cards) */
185 
186  if (count == 1) { /* Single sector write */
187  if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */
188  && xmit_datablock(buff, 0xFE)) {
189  count = 0;
190  }
191  } else { /* Multiple sector write */
192  if (CardType & CT_SDC)
193  send_cmd(ACMD23, count); /* Predefine number of sectors */
194  if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
195  do {
196  if (!xmit_datablock(buff, 0xFC)) break;
197  buff += 512;
198  } while (--count);
199  if (!xmit_datablock(0, 0xFD)) count = 1; /* STOP_TRAN token */
200  }
201  }
202  despiselect();
203 
204  return count ? RES_ERROR : RES_OK; /* Return result */
205  }
206 #endif
207 
208  /*-----------------------------------------------------------------------*/
209  /* Miscellaneous drive controls other than data read/write */
210  /*-----------------------------------------------------------------------*/
211 
212 #if _USE_IOCTL
213  DRESULT disk_ioctl(BYTE drv, /* Physical drive number (0) */
214  BYTE cmd, /* Control command code */
215  void *buff /* Pointer to the conrtol data */
216  ) override {
217  DRESULT res;
218  BYTE n, csd[16];
219  DWORD *dp, st, ed, csize;
220 
221  if (drv) return RES_PARERR; /* Check parameter */
222  if (stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
223 
224  res = RES_ERROR;
225 
226  switch (cmd) {
227  case CTRL_SYNC: /* Wait for end of internal write process of the drive */
228  if (spiselect()) res = RES_OK;
229  break;
230 
231  case GET_SECTOR_COUNT: /* Get drive capacity in unit of sector (DWORD) */
232  if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
233  if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
234  csize =
235  csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1;
236  *(DWORD *)buff = csize << 10;
237  } else { /* SDC ver 1.XX or MMC ver 3 */
238  n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) +
239  2;
240  csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) +
241  ((WORD)(csd[6] & 3) << 10) + 1;
242  *(DWORD *)buff = csize << (n - 9);
243  }
244  res = RES_OK;
245  }
246  break;
247 
248  case GET_BLOCK_SIZE: /* Get erase block size in unit of sector (DWORD) */
249  if (CardType & CT_SD2) { /* SDC ver 2.00 */
250  if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */
251  xchg_spi(0xFF);
252  if (rcvr_datablock(csd, 16)) { /* Read partial block */
253  for (n = 64 - 16; n; n--)
254  xchg_spi(0xFF); /* Purge trailing data */
255  *(DWORD *)buff = 16UL << (csd[10] >> 4);
256  res = RES_OK;
257  }
258  }
259  } else { /* SDC ver 1.XX or MMC */
260  if ((send_cmd(CMD9, 0) == 0) &&
261  rcvr_datablock(csd, 16)) { /* Read CSD */
262  if (CardType & CT_SD1) { /* SDC ver 1.XX */
263  *(DWORD *)buff =
264  (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1)
265  << ((csd[13] >> 6) - 1);
266  } else { /* MMC */
267  *(DWORD *)buff =
268  ((WORD)((csd[10] & 124) >> 2) + 1) *
269  (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
270  }
271  res = RES_OK;
272  }
273  }
274  break;
275 
276  case CTRL_TRIM: /* Erase a block of sectors (used when _USE_ERASE ==
277  1) */
278  if (!(CardType & CT_SDC)) break; /* Check if the card is SDC */
279  if (disk_ioctl(drv, MMC_GET_CSD, csd)) break; /* Get CSD */
280  if (!(csd[0] >> 6) && !(csd[10] & 0x40))
281  break; /* Check if sector erase can be applied to the card */
282  dp = (DWORD *)buff;
283  st = dp[0];
284  ed = dp[1]; /* Load sector block */
285  if (!(CardType & CT_BLOCK)) {
286  st *= 512;
287  ed *= 512;
288  }
289  if (send_cmd(CMD32, st) == 0 && send_cmd(CMD33, ed) == 0 &&
290  send_cmd(CMD38, 0) == 0 &&
291  wait_ready(30000)) { /* Erase sector block */
292  res = RES_OK; /* FatFs does not check result of this command */
293  }
294  break;
295 
296  default:
297  res = RES_PARERR;
298  }
299 
300  despiselect();
301 
302  return res;
303  }
304 
305 #endif
306 
307  protected:
308  volatile DSTATUS stat = STA_NOINIT; /* Physical drive status */
309  BYTE CardType; /* Card type flags */
310  SPIClass *p_spi = &SPI;
311  SPISettings spi_slow{280000, MSBFIRST, SPI_MODE0};
312  SPISettings spi_fast{4500000, MSBFIRST, SPI_MODE0};
313  SPISettings spi_settings;
314  uint32_t spi_timeout;
315  int cs = -1;
316 
317  void spi_timer_on(uint32_t waitTicks) { spi_timeout = millis() + waitTicks; }
318 
319  bool spi_timer_status() { return (millis() < spi_timeout); }
320 
322  void set_spi_fast(bool fast) { spi_settings = fast ? spi_fast : spi_slow; }
323 
325  inline void set_cs(bool high) {
326  if (cs != -1) digitalWrite(cs, high);
327  }
328 
329  /* Exchange a byte */
330  inline BYTE xchg_spi(BYTE dat) { return p_spi->transfer(dat); }
331 
332  /* Receive multiple byte */
333  void xchg_spi_multi(BYTE *buff, UINT btr) { p_spi->transfer(buff, btr); }
334 
335  /* Wait for card ready */
336  int wait_ready( /* 1:Ready, 0:Timeout */
337  UINT wt /* Timeout [ms] */
338  ) {
339  BYTE d;
340  // wait_ready needs its own timer, unfortunately, so it can't use the
341  // spi_timer functions
342  uint32_t timeout = millis() + wt;
343  do {
344  d = xchg_spi(0xFF);
345  /* This loop takes a time. Insert rot_rdq() here for multitask
346  * envilonment. */
347  /* Wait for card goes ready or timeout */
348  } while (d != 0xFF && ((millis() < timeout)));
349 
350  return (d == 0xFF) ? 1 : 0;
351  }
352 
353  /* Despiselect card and release SPI */
354 
355  void despiselect(void) {
356  p_spi->endTransaction();
357  set_cs(true); /* Set CS# high */
358  xchg_spi(0xFF); /* Dummy clock (force DO hi-z for multiple slave SPI) */
359  }
360 
361  /* Select card and wait for ready */
362 
363  int spiselect(void) /* 1:OK, 0:Timeout */
364  {
365  p_spi->beginTransaction(spi_settings);
366  set_cs(false); /* Set CS# low */
367  xchg_spi(0xFF); /* Dummy clock (force DO enabled) */
368  if (wait_ready(500)) return 1; /* Wait for card ready */
369 
370  despiselect();
371  return 0; /* Timeout */
372  }
373 
374  /* Receive a data packet from the MMC */
375 
376  int rcvr_datablock( /* 1:OK, 0:Error */
377  BYTE *buff, /* Data buffer */
378  UINT btr /* Data block length (byte) */
379  ) {
380  BYTE token;
381 
382  uint64_t end = millis() + 200;
383  do { /* Wait for DataStart token in timeout of 200ms */
384  token = xchg_spi(0xFF);
385  /* This loop will take a time. Insert rot_rdq() here for multitask
386  * envilonment. */
387  } while ((token == 0xFF) && millis() < end);
388  if (token != 0xFE)
389  return 0; /* Function fails if invalid DataStart token or timeout */
390 
391  xchg_spi_multi(buff, btr); /* Store trailing data to the buffer */
392  xchg_spi(0xFF);
393  xchg_spi(0xFF); /* Discard CRC */
394 
395  return 1; /* Function succeeded */
396  }
397 
398  /* Send a data packet to the MMC */
399 
400 #if _USE_WRITE
401  int xmit_datablock( /* 1:OK, 0:Failed */
402  BYTE *buff, /* Ponter to 512 byte data to be sent */
403  BYTE token /* Token */
404  ) {
405  BYTE resp;
406 
407  if (!wait_ready(500)) return 0; /* Wait for card ready */
408 
409  xchg_spi(token); /* Send token */
410  if (token != 0xFD) { /* Send data if token is other than StopTran */
411  xchg_spi_multi(buff, 512); /* Data */
412  xchg_spi(0xFF);
413  xchg_spi(0xFF); /* Dummy CRC */
414 
415  resp = xchg_spi(0xFF); /* Receive data resp */
416  if ((resp & 0x1F) != 0x05)
417  return 0; /* Function fails if the data packet was not accepted */
418  }
419  return 1;
420  }
421 #endif
422 
423  /* Send a command packet to the MMC */
424  BYTE send_cmd( /* Return value: R1 resp (bit7==1:Failed to send) */
425  BYTE cmd, /* Command index */
426  DWORD arg /* Argument */
427  ) {
428  BYTE n, res;
429 
430  if (cmd & 0x80) { /* Send a CMD55 prior to ACMD<n> */
431  cmd &= 0x7F;
432  res = send_cmd(CMD55, 0);
433  if (res > 1) return res;
434  }
435 
436  /* Select the card and wait for ready except to stop multiple block read */
437  if (cmd != CMD12) {
438  despiselect();
439  if (!spiselect()) return 0xFF;
440  }
441 
442  /* Send command packet */
443  xchg_spi(0x40 | cmd); /* Start + command index */
444  xchg_spi((BYTE)(arg >> 24)); /* Argument[31..24] */
445  xchg_spi((BYTE)(arg >> 16)); /* Argument[23..16] */
446  xchg_spi((BYTE)(arg >> 8)); /* Argument[15..8] */
447  xchg_spi((BYTE)arg); /* Argument[7..0] */
448  n = 0x01; /* Dummy CRC + Stop */
449  if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) */
450  if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) */
451  xchg_spi(n);
452 
453  /* Receive command resp */
454  if (cmd == CMD12)
455  xchg_spi(0xFF); /* Diacard following one byte when CMD12 */
456  n = 10; /* Wait for response (10 bytes max) */
457  do {
458  res = xchg_spi(0xFF);
459  } while ((res & 0x80) && --n);
460 
461  return res; /* Return received response */
462  }
463 };
464 
465 } // namespace fatfs
Empty IO implementation that we can use to test the compilation.
Definition: BaseIO.h:10
Accessing a SD card via the Arduino SPI API.
Definition: SDArduinoSPIIO.h:48
void set_spi_fast(bool fast)
set fast/slow SPI speed
Definition: SDArduinoSPIIO.h:322
void set_cs(bool high)
update the CS pin
Definition: SDArduinoSPIIO.h:325