Github repository containing informations about the driver for STM32 MCUs interfacing SD cards using FATFs.
The following code is intended for power optimization of the micro SD module. The SD can be power consuming, therefore it is continually switched on and off using an external PNP-BJT driven using a GPIO pin. SPI pins are configured using push-pull configuration + internal pull-ups to avoid parasitic current when the MCU enters a low power state.
This code has been modified to make this tutorial work.
-
Include FATFs in "Middleware and Software Packs"
-
Setup SPI peripheral with prescaler 256. This is needed to initialize the SD card in SPI mode (clock must be within 100 kHz - 400 kHz during the initialization procedure). SPI speed can then be increased up to 10 MHz (refer to subsequent guidelines). Other configs: 8 bit, full-duplex mode.
-
Set internal pull-up resistors on MOSI, MISO and SCK lines to avoid parasitic currents when the MCU enters a low power state.
-
Set CS pin in push-pull mode, with GPIO output level HIGH. This ensures that the Micro SD card is immediately under master control.
-
Following the above tutorial link, download this .c file and add it to your FATFS/Target directory. Do the same for the .h file.
-
Follow the tutorial instructions summirized here:
- include "user_diskio_spi.h" in "user_diskio.c".
- Add "USER_SPI_functioname" (defined in the "user_diskio_spi" files) to each "USER_functioname" defined in the "user_diskio.c" file.
As an example:
DSTATUS USER_initialize ( BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { /* USER CODE BEGIN INIT */ return USER_SPI_initialize(pdrv); //ADD THIS LINE /* USER CODE END INIT */ } - Adjust the SCLK prescaler in the "user_spi_diskio.c" file.
//(Note that the _256 is used as a mask to clear the prescalar bits as it provides binary 111 in the correct position) #define FCLK_SLOW() { MODIFY_REG(SD_SPI_HANDLE.Instance->CR1, SPI_BAUDRATEPRESCALER_256, SPI_BAUDRATEPRESCALER_256); } /* Set SCLK = slow, approx 280 KBits/s*/ #define FCLK_FAST() { MODIFY_REG(SD_SPI_HANDLE.Instance->CR1, SPI_BAUDRATEPRESCALER_256, SPI_BAUDRATEPRESCALER_16); } /* Set SCLK = fast, approx 4.5 MBits/s */ - In your "main.h" file create the SPI handle that will be used to drive the SD card.
/* USER CODE BEGIN Private defines */ #define SD_SPI_HANDLE hspi2 // Add your actual SPI handle here! /* USER CODE END Private defines */
-
These modifications must be applied to the original code to make it work:
- modify "fatfs.c /.h" files by adding the unlink driver capability, necessary to reinitialize the SD after power down.
Add this code to the "fatfs.c" file:
And this one to the "fatfs.h" file:
/* USER CODE BEGIN Application */ void MX_FATFS_close_drv(void) { retUSER = FATFS_UnLinkDriver(USERPath); } /* USER CODE END Application */This allows you to unlink the driver after powering off the SD card by using these code lines:/* USER CODE BEGIN Prototypes */ void MX_FATFS_close_drv(void); /* USER CODE END Prototypes */// Close the file f_close(&fil); //We're done, so de-mount the drive f_mount(NULL, "", 0); // Detach existing driver: MX_FATFS_close_drv(); //re-attach new driver for next initialization MX_FATFS_Init(); - Modify "user_diskio_spi.c" file as follows.
The "send_cmd" function has to be updated in this way:
This is necessary to skip the "wait_ready" statement while sening CMD0 for module initialization. Indeed in native mode the MISO line is not driven by the slave, therefore no response is received until the SD card is setup to accept SPI commands! In the original code the wait ready statement inside the "spiselect()" function was always terminating with a timeout, resulting in a fmount error while mounting the SD card driver.
/* Select the card and wait for ready except to stop multiple block read */ if (cmd != CMD12) { despiselect(); // if initializing SPI, then skip wait_ready statement if (cmd == CMD0) { CS_LOW(); /* Set CS# low */ xchg_spi(0xFF); /* Dummy clock (force DO enabled) */ } else { if (!spiselect()) return 0xFF; } } - Modify "USER_SPI_initialize" function in this way:
This allows to repeat CMD0 sequence until the SD card is actually in idle state! Indeed i observed that something in between 2-3 cycles are needed before the correct response is actually received. (Took inspiration from arduino libraries in doing this).
// command to go idle in SPI mode uint8_t CMD0_stat; SPI_Timer_On(1000); while ((CMD0_stat = send_cmd(CMD0, 0)) != 1 && SPI_Timer_Status() == 1){ }
- modify "fatfs.c /.h" files by adding the unlink driver capability, necessary to reinitialize the SD after power down.
Add this code to the "fatfs.c" file:
- Original author library.
- SD Card reader schematic.
- SPI problem discussion with SPI hardware.
- FatFs details.