0:09
hello and welcome to controllers tech a
0:12
while ago i created a video that
0:14
demonstrated how to interface an sd card
0:17
with an stm32 microcontroller using the
0:20
spi protocol while the example in that
0:23
video worked perfectly fine it involved
0:26
a somewhat complex configuration process
0:28
for the libraries which made it
0:30
difficult for many viewers to follow
0:33
since then st has significantly changed
0:36
the structure of their codebase which
0:38
makes that older method even more
0:40
confusing especially for beginners who
0:42
are just starting with stm32 development
0:46
today i'm introducing an updated library
0:48
that simplifies the entire setup this
0:51
new library is not only easier to
0:54
configure but it also comes with more
0:56
features and better performance one of
0:59
the major additions is the support for
1:01
dma in spi communication which allows
1:05
for much more efficient data transfers
1:07
it also enables multiblock read and
1:10
write operations which greatly improves
1:13
the speed at which data is accessed or
1:16
written to the card this updated library
1:18
supports a wide range of sd card types
1:22
whether you're using a standard capacity
1:24
sdsc high-capacity sdhc or extended
1:28
capacity sdxc card this library should
1:32
be able to handle it without any issues
1:34
as we proceed through this video i'll
1:36
walk you through how to use this new
1:38
library step by step i'll also show you
1:41
some of the key functions available and
1:43
towards the end we'll run some
1:45
benchmarks to evaluate the read and
1:47
write speeds using both dma and nondma
1:50
modes to follow along you can download
1:54
the library files using the link
1:56
provided in the description of this
1:57
video once downloaded you'll find all
2:00
the required files neatly placed inside
2:03
the src and in folders i'll demonstrate
2:06
exactly how to organize them within a
2:09
project structure as we go forward let's
2:12
begin by launching stm32
2:15
cube ide and creating a new project for
2:18
this tutorial i'll be using the
2:24
microcontroller which is the chip found
2:27
on the popular blue pillboard start by
2:29
giving your project a name of your
2:31
choice and then click on finish to
2:33
create the project once the project is
2:35
set up the first step is to configure
2:38
the system clock enable the high-speed
2:41
external hsse crystal since the blue
2:45
pill board comes with an 8 mhz crystal
2:47
oscillator we'll configure the system to
2:50
run at its maximum frequency of 72 mhz
2:54
also be sure to enable serial wire debug
2:58
swd which is required for debugging stm
3:01
32f1 controllers next go to the
3:04
connectivity tab and enable spi1 in full
3:08
duplex master mode while we will
3:10
configure the detailed spi parameters
3:13
shortly let's first review the default
3:15
pin assignments provided by cubmx by
3:18
default spi1 uses pa5 for sc pa6 for
3:24
miso and pa7 for mosi however in my
3:28
setup i have connected the sd car module
3:30
to pb3 pb4 and pb5 for the spi signals
3:36
and pb6 for the chip select or cs pin
3:39
we'll reassign the spi pins accordingly
3:42
to do this hold down the control key and
3:45
drag the assigned spi pins to their new
3:48
locations on pb3 pb4 and pb5 these pin
3:53
selections are based purely on
3:55
convenience as they make it easier for
3:57
me to connect the module to the board
3:59
physically now i need to configure the
4:01
pb6 pin as a gpio output which will be
4:05
used as the cs line rename this pin to
4:08
cs for better readability later in the
4:11
code next let's configure the spi
4:14
peripheral parameters we'll start by
4:16
adjusting the pre-scaler to reduce the
4:18
spi clock frequency to around 2 megabits
4:22
per second this speed is generally safe
4:25
for most sd cards though you're welcome
4:28
to experiment with higher clock rates up
4:30
to 10 megabits per second should work
4:33
fine in many cases the rest of the spi
4:36
settings will remain at their default
4:38
values with 8bit data size msb first
4:42
clock polarity set to low and the clock
4:45
phase configured to trigger on the first
4:47
clock edge if you plan to use dma for
4:50
data transfers now is the time to enable
4:53
it make sure you enable dma for both the
4:56
transmit and receive directions set the
4:59
dma mode to normal and configure the
5:02
data width for both memory and
5:05
peripheral to 8 bits to see logs and
5:07
output data on a serial console we also
5:10
need to enable ur in this setup i've
5:13
connected the transmit pin pa9 to an
5:17
ft232 usb to serial converter which
5:20
allows communication with the computer
5:23
we'll enable use one in asynchronous
5:25
mode the default settings are fine here
5:28
with the baud rate set to 115,200
5:32
bits per second now return to the gpio
5:35
configuration and adjust the cs pin we
5:38
configured earlier set its initial
5:40
output state to high as this is the idle
5:43
state for the cs line also change its
5:47
output speed to high because the pin
5:49
will need to toggle quickly during
5:51
communication the next step is to enable
5:54
the fat fs middleware which we'll use to
5:57
work with files on the sd card inside
5:59
the fat fs configuration ensure that the
6:02
make fs function for formatting and set
6:05
label for assigning volume labels are
6:07
both enabled additionally enable support
6:10
for long file names and make sure to use
6:13
a dynamic working buffer located on the
6:15
heap this will allow us to work with
6:18
file names up to 255 characters long
6:22
instead of being limited to just eight
6:23
characters for the name and three for
6:26
the extension leave the rest of the fat
6:28
fs settings at their defaults that means
6:31
the sector size remains 512 bytes and
6:34
we'll be using only a single volume one
6:38
crucial step before generating code is
6:40
to disable the automatic initialization
6:43
of fat fs to do this go to the project
6:46
manager tab and open the advanced
6:48
settings there disable the code
6:51
generation for fat fs initialization the
6:55
reason we do this is because our custom
6:58
fat fs driver implementation will handle
7:01
all the necessary linking internally and
7:03
we don't want cubmx to interfere with it
7:06
now that everything is set up generate
7:08
the code once the project is created
7:10
open main.c see and notice that the fat
7:13
fs initialization code is not present
7:17
which is exactly what we want before we
7:19
move forward let's quickly build the
7:21
project to ensure there are no errors if
7:24
the build completes successfully we can
7:27
now move on to importing the library
7:29
files copy the c files from the
7:32
downloaded library into the src folder
7:35
of the project and copy the header files
7:37
into the ink folder if you've downloaded
7:40
the complete project from the link the
7:42
files will already be arranged this way
7:44
so just match what you see here let's
7:46
take a quick look at the contents of
7:48
these files the sdn_spic
7:52
c file handles the low-level spi
7:54
communication with the sd card at the
7:57
top of the file you can choose whether
7:59
or not to use dma by setting a flag then
8:03
you define the spi instance you're using
8:06
in this case spi1 and also define the cs
8:10
pin and its port you don't need to
8:12
change anything beyond that point this
8:14
file includes functions for sending and
8:17
receiving data initializing the sd card
8:20
and reading or writing blocks next we
8:25
c file which serves as the bridge
8:28
between our sd card functions and the
8:30
fat fs middleware it contains the
8:32
required driver functions that connect
8:35
the low-level operations with a fat fs
8:38
stack this means we don't need to
8:40
manually configure any of that in other
8:42
files the third file sd_f functions c
8:47
includes higher level functions that
8:48
make working with the sd card simpler
8:51
you'll find functions for mounting and
8:53
unmounting the card reading and writing
8:56
files listing directory contents and
8:59
more all logging in this project is done
9:01
through print f so we'll also define a
9:04
custom function that redirects print f
9:06
output to the u make sure you point it
9:09
to the correct u instance in case you're
9:12
using something different from mine
9:14
let's now take a look at how to actually
9:15
use this library in our code begin by
9:18
including the sders functions.h header
9:21
file at the top of your main.c this file
9:24
gives you access to all the core
9:26
functions required to work with the sd
9:28
card inside the main function we'll
9:31
begin by mounting the sd card using the
9:35
once mounted we'll call the sd list to
9:39
display all the files and directories
9:41
currently stored on the card finally
9:44
we'll unmount the card using the
9:45
sd_mount i've already copied a few files
9:49
and folders onto the sd card beforehand
9:51
so you should be able to see those
9:53
printed in the serial console when we
9:54
run the code let's now build and flash
9:57
this project to the board once it's
10:00
flashed successfully open up your serial
10:02
monitor and make sure it's configured to
10:07
baud which matches the ur settings we
10:09
used earlier now insert the sd card into
10:12
the module and press the reset button on
10:15
your board in some cases especially when
10:17
using dma for spi the sd card may show a
10:21
not ready error on the first attempt
10:24
this usually happens because the card
10:26
hasn't completed its powerup sequence if
10:29
you see that error just press the reset
10:31
button again and it should work on the
10:33
next try once the sd card is
10:36
successfully detected you'll see a
10:38
message in the console showing the card
10:40
type in this example the card type is
10:43
sdhc and it has a total storage size of
10:47
16 gb right after that you'll see a list
10:50
of all the files and folders located at
10:53
the root level of the sd card in my case
10:55
i got a file 1.txd and two folders named
10:59
f1 and f2 inside the f1 folder there are
11:03
additional subfolders named f1 f1 and f1
11:07
f2 and there are more files placed
11:09
inside those as well this confirms that
11:12
our file listing function is working
11:14
correctly and the fat fs integration is
11:18
successful now that we verified that the
11:20
sd card is detected and the files are
11:23
listed properly let's try reading a
11:25
specific file for this demonstration
11:28
i'll be reading a file named file 5.txt
11:32
this file is located inside the
11:37
f2 directory structure to read this file
11:40
we first define a buffer that can hold
11:42
more than 30 bytes of data since the
11:44
file size is exactly 30 bytes we also
11:48
declare a variable to store the number
11:50
of bytes actually read inside the main
11:53
we'll again mount the sd card then use
11:55
the sders read file to read the contents
11:58
of file 5.txt into our buffer and
12:01
finally print that buffer to the serial
12:04
console after that we'll unmount the
12:06
card when you run this code you'll see
12:09
that the contents of the file are
12:11
printed correctly on the console this
12:13
confirms that our file read operation is
12:16
working as expected let's now try
12:18
reading another file file 1.txt
12:21
which is placed directly in the root
12:23
folder of the sd card this file is
12:26
larger it contains 71 bytes of data so
12:30
we'll increase the size of our buffer to
12:32
80 bytes to safely store the entire file
12:35
content the procedure for reading the
12:37
file remains exactly the same mount the
12:40
card read the file print the content and
12:44
unmount the card you'll now see the new
12:46
file contents printed on the console and
12:49
the number of bytes read will match the
12:51
file size so both the reading operations
12:54
for files inside folders and for files
12:57
at the root are working perfectly fine
12:59
using this library let's now move on to
13:02
another important operation writing data
13:05
to a new file on the sd card just like
13:08
with our previous operations we'll start
13:10
by mounting the sd card using the
13:12
sd_mount once the card is mounted we'll
13:16
move forward to creating a new file
13:21
this file will be placed in the root
13:22
directory of the sd card the function
13:25
sa_right file requires two arguments the
13:29
name of the file to be created and the
13:31
actual data that you want to write to
13:33
the file in our case we're writing a
13:36
simple text string which is
13:38
approximately 36 bytes long after
13:40
writing is complete we'll call the
13:43
intercore read file to verify the result
13:45
by reading the file back we'll print the
13:48
contents of this buffer to the console
13:50
using print f once that's done we'll
13:53
also call the sd list again to display
13:56
the updated directory structure let's
13:59
now build the project and flash it to
14:01
the stm32 board after flashing we'll
14:05
open the serial monitor to check the
14:07
output from the microcontroller as you
14:10
can see in the console output the driver
14:12
reports that it successfully wrote 36
14:14
bytes to file 6.txt txt immediately
14:18
after that we read the file back and the
14:21
data printed on the console matches
14:23
exactly what we originally wrote this
14:26
confirms that both the write and the
14:28
read operations are functioning
14:29
correctly we also printed the directory
14:32
listing and right there in the root
14:34
folder you can now see the presence of
14:38
along with the earlier entries file
14:40
1.txd and the folders f1 and f2 so we
14:44
can confidently say that file creation
14:46
and writing operations are working
14:49
perfectly with this updated library next
14:52
let's take a look at a slightly more
14:54
advanced operation reading data from a
14:57
csv file this is very useful in cases
15:00
where data logging has been performed in
15:02
tabular format or when external systems
15:05
generate csv logs that need to be parsed
15:07
by the microcontroller i already have a
15:10
file named file 4.csv a csv stored
15:12
inside a folder called f1 and within
15:16
that another subfolder named f1 f2 this
15:19
file contains 16 rows of data with two
15:23
columns in each row which is a standard
15:26
format for csv files to read this file
15:29
we will use a special function from the
15:31
library called sdre csv before we call
15:34
the function we need to prepare a data
15:37
structure that will store the contents
15:39
of each row this structure already
15:41
defined in the library typically
15:44
contains two fields field one and field
15:47
two both stores as character arrays
15:50
we'll then define an array of this
15:52
structure large enough to store multiple
15:55
records in this case we'll allow for up
15:58
to 20 records even though the file only
16:01
contains 16 alongside this we'll declare
16:04
an integer variable named record_c count
16:08
which will store the number of rows that
16:10
were successfully read by the function
16:12
once everything is set up we'll call the
16:14
function est read csv we'll pass in the
16:22
f2 /file fork.csv csv along with the
16:26
array to store the data the maximum
16:28
number of records to read and the record
16:31
underscore count variable internally the
16:33
function will open the file read each
16:36
line split the line using the comma as a
16:39
separator and store the resulting two
16:42
values into field one and field two as
16:45
part of the process the function will
16:47
also automatically print each row to the
16:50
console so you can see the data as it's
16:53
being read to take this a step further
16:55
we'll also debug the project to inspect
16:57
how the data is stored in memory let's
17:00
place a break point at the while loop
17:02
when the program hits the break point we
17:05
can open the variables tab in the
17:06
debugger and examine each entry in the
17:09
array you'll see that the record
17:11
underscore count shows 16 which matches
17:14
the number of rows in the file let's
17:16
check the console now the function has
17:18
successfully read the data from the csv
17:21
file and you can see the output right
17:23
here each row begins with a serial
17:25
number for example the first row starts
17:28
with 01 followed by two values that
17:31
represent the actual data in that row
17:34
you'll notice that there are exactly 16
17:36
rows numbered from 0 to 15 and each row
17:40
shows two columns of data as expected
17:44
this confirms that the csv file was read
17:46
correctly and the data was parsed and
17:49
printed in the proper format inside the
17:51
structure field one contains the data
17:54
from the first column of each row and
17:56
field two contains a second all of this
17:59
is stored as character arrays making it
18:02
very easy to process or display later
18:05
this confirms that the csv reading
18:07
function works flawlessly and a
18:09
structure-based approach allows you to
18:11
manipulate the data in a very clean and
18:15
organized way now that we've seen how to
18:17
create read and parse files let's try
18:20
some additional file operations like
18:23
appending new data to an existing file
18:26
this is useful when you want to continue
18:28
logging data without overwriting what's
18:31
already there in this example we'll
18:33
append 22 more bytes to file 6.txt txt
18:38
which we created earlier we'll use the
18:42
passing the file name and the new data
18:45
we want to add after performing the
18:47
append operation we'll again use s read
18:50
file to read the entire content of file
18:55
and then print it to the console this
18:57
allows us to confirm that the new data
18:59
was added correctly let's also test the
19:02
file deletion feature in this case we'll
19:05
remove file 1.tx txt from the root
19:07
folder we'll do this using the function
19:10
sd delete which only requires the file
19:13
name as a parameter once the file is
19:16
deleted we'll again call sdc list to
19:19
refresh the list of files on the sd card
19:22
when you look at the output you'll see
19:24
that the total file size has now
19:26
increased from 36 bytes to 58 bytes and
19:30
both the original content and the newly
19:32
appended text appear one after another
19:35
just as expected also note that we no
19:37
longer see file 1.txd in the list this
19:41
confirms that the delete operation
19:43
worked as intended with that we've now
19:46
successfully tested all major file
19:48
operations creating reading writing
19:52
appending deleting and even reading
19:55
structured csv data let's now test the
19:58
read and write speed of this library
20:00
using the built-in benchmarking feature
20:02
there's a file named sd_benchmark
20:06
c that helps in performing this test
20:09
inside the file you can choose whether
20:11
to enable or disable the usage of dma by
20:15
modifying a simple flag you can also
20:17
define the test file size if needed by
20:20
default the benchmark creates and works
20:22
with a 500 kilobytes file to test how
20:25
fast the sd card can read and write data
20:28
we'll begin by testing the speed without
20:30
using dma first make sure to set the dma
20:34
flag to zero in both the spi c
20:40
c files which ensures that the polling
20:43
method will be used for all data
20:44
transfers then include the benchmark
20:47
header file in your main c and call the
20:50
benchmark function inside the main
20:53
function it's important not to call the
20:55
mount and unmount functions manually
20:58
here because the benchmark function
21:00
already handles that internally let's
21:02
now build and flash the project to the
21:05
board once flashed wait for the test to
21:08
run completely you'll see the results
21:10
printed on the console in my case the
21:13
right speed is reported to be around 194
21:16
kilobytes pers while the read speed is
21:19
approximately 67 kilobytes pers to
21:23
confirm the consistency let's reset the
21:25
microcontroller and run the test once
21:28
again this time again we get the same
21:30
speeds i repeated this a third time and
21:34
the results were identical so we can say
21:37
with confidence that in polling mode the
21:40
sd card delivers around 194 kilobytes
21:44
per second for writing and about 67
21:46
kilobytes per second for reading now
21:49
let's perform the same benchmark test
21:51
but this time with dma enabled to do
21:54
this go back to the se_spi.c_benchmark
22:00
c files and set the dma flag to one this
22:03
allows the library to use dma for
22:06
transferring data over spi build the
22:09
project again and flash it to the board
22:12
on my setup i encountered a disk
22:14
initialization error on the first boot
22:17
this can sometimes happen when using dma
22:20
especially if the sd card was not ready
22:22
or fully inserted simply reset the
22:25
microcontroller and a test should start
22:28
running now that the benchmark has
22:29
started we'll again wait for it to
22:32
complete once finished check the console
22:35
for the new speed results interestingly
22:37
the right speed remains nearly the same
22:40
around 194 kilobytes pers but the read
22:44
speed increases drastically going up to
22:47
nearly 200 kilobytes per second this is
22:50
expected as dma makes data transfers
22:53
much more efficient especially for
22:56
continuous read operations to further
22:58
validate the libraries compatibility i
23:01
tested it with a different sd card a 1
23:04
gbte sdsc type micro sd card after
23:07
inserting the card and running the
23:09
benchmark the console correctly
23:11
identifies the card type as sdsc with a
23:15
total size of 1 gb however the right
23:19
speed on this card turned out to be much
23:21
lower compared to the sdhc card the read
23:24
speed was still acceptable but sdsc
23:26
cards generally don't perform as well in
23:29
terms of writing especially with larger
23:31
files or higher transfer rates still the
23:35
important point here is that the library
23:37
successfully detects and supports sdsc
23:40
cards in addition to sdhc and sdxc cards
23:44
we've already seen now that we've
23:46
verified the core functionality you can
23:48
explore many more features that this
23:51
library offers for example you could
23:54
delete files rename them create new
23:57
directories and append data to existing
24:00
files each of these operations is
24:03
implemented through simple function
24:05
calls in the code i encourage you to try
24:07
them out yourself after downloading and
24:10
setting up the project this concludes
24:12
the demonstration for the spi based sd
24:15
card interface using the updated library
24:18
but this is not the end of sd card
24:20
tutorials in the upcoming videos i'll
24:23
cover how to use the sdio interface
24:26
which provides even faster data
24:28
transfers and more efficient
24:30
communication compared to spi after that
24:33
we'll also explore the sdmmc interface
24:36
which is commonly used in stm32 devices
24:39
that offer high-speed memory card
24:41
support you can download the complete
24:44
source code for this project from the
24:45
link provided in the video description
24:47
it contains all the configuration files
24:50
source and header files and a fully
24:53
working stm32 cube ide project so you
24:57
can try it out on your own hardware if
24:58
you run into any issues or have
25:01
questions feel free to leave a comment
25:03
below i try my best to reply to as many
25:05
as i can thank you for watching this
25:08
video keep learning keep experimenting
25:11
and as always have a great day ahead