SD card using SPI in STM32
You already know what are you looking for and that’s why probably you are here. So I am not going to waste your time with the facts that what is an SD card ? How does it work and all these..
In this tutorial we will interface a SD card with stm32 micro controller using SPI mode. I am using STM32F103C8 controller and SD card size is 1 GB. Also we will do some basic file handling operations such as creating a file, writing, reading, deleting etc.
Some advanced operations, i.e. directory related operations, will be covered in another tutorial. So let’s jump right into it.
UPDATE 1
The code has been updated with new libraries thanks to eziya https://blog.naver.com/eziya76/221188701172 SPI can work upto 10 Mbps
If getting FR_NOT_READY error, use some external 5V power source. Power from board seems to be not enough (it could be current required for the module)
To Know these errors, put a Breakpoint after f_mount and see the value of fresult.
UPDATE 2
If you are getting FR_NO_FILESYSTEM (mainly happening with LOW storage cards), then use the older library from https://controllerstech.com/wp-content/uploads/2020/07/SDCARD_SPI_OLD_F103.zip
To Know these errors, put a Breakpoint after f_mount and see the value of fresult.
HOW TO
Obviously first we need to create a project in CubeMx and make sure you follow the steps below.
Make sure you select the fatfs and I have left everything to default. I am also selecting uart in my project so that the data can be sent to the computer.
Also the SPI speed is kept at 2.25 MBits/s. This is important, make sure you keep your speed around it.
After the project is generated, open it and Now we need to copy some library files in the project folder.
copy the files fatfs_sd.c and fatfs_sd.h in the respective src and inc folders. After successfully copying, we need to do some basic defines and all.
First, open the fatfs_sd.c file and edit the following
/* defines for the CS PIN */
#define SD_CS_GPIO_Port GPIOB
#define SD_CS_Pin GPIO_PIN_0
/* manage your SPI handler below */
extern SPI_HandleTypeDef hspi1;
Next, in the user_diskio.c file, we have to make some changes and you can see them, once you download the code.
We also need to setup the Timer 1 and Timer 2 functions. These values should constantly decrease every 1 ms and that’s why we define them inside the interrupt handler, in the systick handler.
So, open the STM32f1xx_it.c and define the following:
volatile uint8_t FatFsCnt = 0;
volatile uint8_t Timer1, Timer2;
void SDTimer_Handler(void)
{
if(Timer1 > 0)
Timer1--;
if(Timer2 > 0)
Timer2--;
}
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
FatFsCnt++;
if(FatFsCnt >= 10)
{
FatFsCnt = 0;
SDTimer_Handler();
}
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
Some Insight into the CODE
f_mount
will mount the SD card. “/” is the path, that we want to mount. fs is the pointer to the file system, which we need to define as a global definition, as we are going to use it a lot.
fresult = f_mount(&fs, "/", 1);
if (fresult != FR_OK) send_uart ("ERROR!!! in mounting SD CARD...\n\n");
else send_uart("SD CARD mounted successfully...\n\n");
Once the card is mounted, we will create a file and to do that, we will use the following function
/* Create second file with read write access and open it */
fresult = f_open(&fil, "file2.txt", FA_CREATE_ALWAYS | FA_WRITE);
If the file already exists, this will override it, and if it doesn’t exist, it will create a new file and than open it.
fil is the pointer to the file, which is also global.
“file2.txt” is the name of the file, that we want to create
the third parameter is the attributes given to the file. Below is the table for that
once the file is opened, we can write the data using the function below
/* Writing text */
strcpy (buffer, "This is File2.txt, written using ...f_write... and it says Hello from Controllerstech\n");
fresult = f_write(&fil, buffer, bufsize(buffer), &bw);
here bw is the variable to keep the count of the number of bytes written to the file.
buffer contains the string, that we want to write into the file.
After writing, it’s time to close the file, and to do that we can use
/* Close file */
f_close(&fil);
Now it’s time to read the data, that we just wrote into the file. So, let’s open it again with read attribute
/* Open second file to read */
fresult = f_open(&fil, "file2.txt", FA_READ);
if (fresult == FR_OK)send_uart ("file2.txt is open and the data is shown below\n");
/* Read data from the file
* Please see the function details for the arguments
*/
f_read (&fil, buffer, f_size(&fil), &br);
send_uart(buffer);
send_uart("\n\n");
/* Close file */
f_close(&fil);
The file opened, read, and data from the file got saved into the buffer. bw is the count of the number of bytes read from the file.
Than we sent the buffer to the uart, and at last close the file.
To delete the above created file use the function below
fresult = f_unlink("/file2.txt");
if (fresult == FR_OK) send_uart("file2.txt removed successfully...\n");
so, f_unlink
will delete the file.
“/file2.txt” is the path to the file that we want to delete
And finally we will unmount the CARD using the f_mount function again. But this time the file system pointer will be NULL.
/* Unmount SDCARD */
fresult = f_mount(NULL, "/", 1);
if (fresult == FR_OK) send_uart ("SD CARD UNMOUNTED successfully...\n");
Result