The more we know the harder it is to defeat us!

Using FatFS with HAL

Recently I have written a short post about the HAL library created by STM32. The HAL is gaining popularity among hobbyists and is more and more frequently used. However, when you would like to use it for something else than just basic stuff like generating PWM on digital output you have to write it by yourself. Not so long ago I had to use SD card in one of my projects. It turned out that there is no driver for FatFS based on HAL – at least there was not. I decided to write the driver and here you can read about it…

FatFS delivers you with a template which you have to fill up so you can communicate with the SD card. In my case I needed something simple and straightforward. I decided to implement hardware layer for SPI based on HAL. There is no DMA transfer, it is just a simple implementation that works pretty well with not very frequent transfers. It is well-suited for log purposes.

You need to write definition for a few functions:

void SELECT(void),

void DESELECT(void),

void xmit_spi(BYTE Data),

BYTE rcvr_spi(void).

Below are definitions of those four functions:

1

2

3

staticvoidSELECT(void){

HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);

}

1

2

3

staticvoidDESELECT(void){

HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);

}

1

2

3

4

staticvoidxmit_spi(BYTEData){

while(HAL_SPI_GetState(&hspi2)!=HAL_SPI_STATE_READY);

HAL_SPI_Transmit(&hspi2,&Data,1,5000);

}

1

2

3

4

5

6

7

8

staticBYTErcvr_spi(void){

unsignedcharDummy,Data;

Dummy=0xFF;

Data=0;

while((HAL_SPI_GetState(&hspi2)!=HAL_SPI_STATE_READY));

HAL_SPI_TransmitReceive(&hspi2,&Dummy,&Data,1,5000);

returnData;

}

As you can see I am using SPI2 but you can use whatever SPI you want. For details about the HAL functions look up the documentation.

Final remark.

If you do not use chip select pin you can simply pull-down CS input port of SD card and leave SELECT and DESELECT functions empty or create empty macros which would be a bit more efficient.

Update

Below you can find my full implementation of diskio.c based on ChaN original file with some tweaks.

Update 2

I have forgotten to mention that this is non standart implementation in terms of timing. The function disk_timerproc() is required to be called every 10 ms (100 Hz). Since this implementation was using in a project where I was using FreeRTOS with finer grain (0.1 ms) I had to modify the sdcard_systick_timerproc() function.

C

1

2

3

4

5

6

7

voidinlinesdcard_systick_timerproc(void){

++sdcard_timer;

if(sdcard_timer>=100){

sdcard_timer=0;

disk_timerproc();

}

}

If you want it to be standard implementation you have to modify the code above. Simply instead of 100 place 10 and call this function every 1 ms i.e. in SysTick handler. Thanks lauba for pointing this out!

Update 3

Here I present a very simple example how to write data to the SD card:

Yes, you are right. This files are from project where I was using FreeRTOS and I modified the timings. So this is not standard implementation.
My SystemTick is called every 0.1 ms so the function disk_timerproc() is called every 10 ms as required.

Thank you for pointing this out! I will add this information to the article.

But what is strange is working also with 100 ms;) and next question do you think about using SD card operations no blocking? for example using DMA for this? because when we want to save a lot of data to SD card than we have to wait when one block will be ready. Next operate with the second again save and wait and in loop the same;)

Well, I was using 100 ms because of the original driver, it had 100 ms. Now, I see the newest example of FatFS is calling this every 1 ms. As said before, it can be easily alternated.
As for the DMA. The example presented here is not very optimal. Each read/write or ioctl is working in a blocking mode. DMA with some FIFO could be used for this. However, for my purposes it is more than enough and for most of other things like data logging it should be also enough as long as you do not write tons of data.

No problem. I will add it to my to do list. However, if you are really concerned about time maybe you should have a look at FatFS website. The Author has published many examples and also some sample code for different platforms.

I appreciate your prompt response. I’ve been trying for a few weeks to store time stamps with FatFS + SPI on STM32L4 and I feel like I’m missing something basic to put it all together. Your diskio.c example helps greatly.

Yes, you should use the f_*() functions. The driver is driver implementation only — the hardware layer. It is transparent for the user.
I have also added a very short example how to write some data to a file. I hope it helps.

First you have to decide which SPI are you going to use and you have to configure it accordingly. When you are decided then you have to change the hardware layer of FatFS and you are good to go. By changing the hardware layer you tell FatFS which interface it should use to communicate with the SD card. Those functions were enumerated in my blog post.
The usage is very simple and straightforward. First you initialize the SPI and you can use the FatFS functions. There is an example in one of the updates to this post.

Thank you so much for responding. Forgive me, but I am a beginner. Could you tell me exactly what i would have to initialize in the main code, or could you direct me to the blogspot where this has already been explained?

Just to clarify one thing. If you are using my code you should not configure FatFS in CubeMX. The CubeMX version is using SDIO, the hardware layer presented here is using SPI. If that is cleared out then you let’s go to the SPI initialization. You should initialize SPI as Master with FullDuplex, 8 bits, MSB first, low clock polarity and data transfer on rising edge and that’s all. As for the baudrate you can set it for around 10Mbit/s for starters.
After above is configured then you can adjust the hardware layer for FatFS files with your specific SPI (sd_stm32.c/h files). Finally, you add your logic to the program (FatFS functions).

Hi,
Could you tell me what I should exactly do… I skip FatFs configuration in CubeMX, I attached your *.c and *.h file to my software. I add FatFs Library directly from they website but I have lot of problem with compiling of the code. At the end I need mention that I configure SPI interface according your advice. Maybe I don’t catch everything. Thank you in advance for your support.

From what I can see you are using FatFS from Cube with the CMSIS RTOS also from Cube. Giving one line of code is not enough. It says that a semaphore named osSemaphoreId is undefined. I guess that you are missing headers in ff.h header file.
The hardware layer I’ve prepared is for SPI and it works with Cube but requires some minor alterations.

I do not have an example which only is using SD card. However, in one of the comments I have pointed out how the SPI should be configured. I think this is what you are after, isn’t it. Also I will add this description to the body of the post so it would be easy to find.

Hi,
I’m using the STM32f103c8t6 to save the value of an analog port into the SD. i finally succeed on doing this, but the max frequency that i could save was about 20hz. how can i make this number bigger?
i’m using the tim interrupt for saving the data, every 50ms i mount, open, write and close the file. but when i try to save faster the program just doesnt work anymore.

Probably it hangs on remounting the SD card. However, debugging it would be the best option to see what is happening.
Even though, I would not recommend mounting, opening and closing the file in a cycle. Mount the SD card once, and open the file. Write data to the file periodically. This should solve your problem. Although, there may be one issue with actually storing the data to the SD card. You should occasionally call f_sync() for syncing the cashed data to the SD card. This way if the system crashes you will not lose all data but only a part of it. You can read about it heref_sync

If I don’t configure FatFS on CubeMX, how can I use de functions f_*()? I used the CubeMX to configure the spi2 and the GPIO_Output as CS. After this, I added your drivers sd_stm32 .c in scr and .h in inc. Now what should I do? I need diskio.h and ff.h files, right? I’m having some trouble with this. I’m using stm32f103c8t6. Thank you a lot!!!

Oh great, thank you a lot! One last question: if I want to write a .txt file in my uSD card, in my main function should I call f_*() functions, like f_write(), or some function from your sd_stm32 driver? I just don’t get it how to use your driver in my main function.
Congratulations about your blog by the way, it’s really amazing!

Yes, you only use FatFS specific functions, those starting with f_ prefix. The driver is invisible, it is only a hardware abstraction layer which in fact is used by the FatFS itself.
Thank you! I’m glad you like it.

The definition is actually using BYTE type to create a new way of referencing to it, there it is bool. The error is showing up because you do not have the definition of BYTE type itself. I believe it is in FatFS header files. Give it a try and you will find it in no time.

I just had to include ff.h before diskio.h and ir solved. Now I only have one error left: “Symbol disk_read multiply defined (by diskio.o and sd_stm32.o)”. I have never faced and error related to .o files before, so I don’t know how to fix it. Any suggestions?

You should not “include” the diskio.c. Now, you have two strong definitions of the same functions, e.g. disk_read(), and this is the reason why linker is showing you this error. You should decide which implementation you would like to use. This from diskio.c or from sd_stm32.c. Basically, you should exclude one of these files from compilation or at least from linking stage.

Now the code compiled okay, thank you! Unfortunately it seems that your simple code of Update 3 isn’t working. The function f_mount() “is expected to have 3 arguments, not 2”. Could you help me with this final issue? I just need a code to write a (.txt). I’m sorry for asking you so many questions.
Thank you a lot!

First thanks for sharing this code it helps me a lot to understand how sd cards are working and how to use them with the stm32f1.

I tried to use your code with a 16Gb sdhc card and found that the disk_initialize function failed. I checked with an osciloscope that I got data through spi.
Apparently my sd card is returning 1 to this command send_cmd(CMD58, 0) which caused the function to fail.

At the bottom of this page https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ the state diagram shows that CSS can be 1 for SD version 2 block address.
Unfortunately I didn’t find anything relevant on CSS and if it changes the way the code was written. I will continue to investigate but if you have any clues I would be glad that you can help me.

Thank you for the information. However, removing the Timer1 variable from the code is not recommended. It ensures the proper work of timeouts. Just to clarify one thing. Do you call disk_timerproc() periodically (every 10 [ms])? This actually decrements the Timer1 and Timer2 values so the timeouts work properly. You can put a call to disk_timerproc() in SysTick callback (by default is is called every 1 [ms]) and ensure it is processed every 10 [ms].

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.