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