Blog 2: SD Card Interfacing
Before we dive into camera interfacing, we must first ready the SD card where we will save the images. The aim of this second blog is to have a working SD Card driver in pure Verilog HDL.
How to Interface with an SD Card
I will not start with what is an SD Card since you already know it (I will leave some references below just in case), we will now dive into how can you interface with an SD Card.
SD cards have two main operating modes: SD mode and SPI mode. SD mode is used in computers, phones, and any commercialized products that needs an SD card since it maximizes the throughput of writing and reading from it. However, this will require the designer of the SD card driver to sign an NDA with the SD association and pay some royalties. Thus, we hobbyists are restricted to SPI mode. This is not a bad thing since SPI is a very common communication protocol but the throughput of SD mode is just much higher than SPI mode. SD mode default speed is 100Mbit/s whlle SPI is 25Mbit/sec.
The SPI used in SD card is just the common four-wire type and below is the pin-outs of an SD card. Note the pins needed by the SPI (CS, DI(MOSI) , DO(MISO), and SCLK).

So first, we must ready the SPI driver.
SPI Driver
SPI is a very simple protocol. The image below best shows how it works. A byte(8 bits) are transferred in series in reference to the clock. The clock is provided by the Master. For the SD card driver, the slave is the SD card while the FPGA is the Master. There are actually 4 SPI modes to control clock polarity and phase but no need to sweat the details since it just differs on what edge the data is transferred. For the SD card driver, the default mode 0 is used wherein the transferred or received data must be stable at the rising edge of the clock. Thus, data can only switch on falling edges of the clock.

Now for the SPI driver code in Verilog HDL:
`timescale 1ns/1ps
//HI_FREQ_DIV=clk division for the high freq,LO_FREQ_DIV=clk division for low freq [can be any even number >=2]
module spi #(parameter HI_FREQ_DIV=12,LO_FREQ_DIV=30,SPI_MODE=0) ( //SPI_MODE can be either 0,1,2,3
input wire clk,rst,
//SPI control
input clk_div, //1: high frequency , 0=low frequency
input wire rd,wr,hold, //pins to start read or write operation, hold is for holding the clock for multibyte read/write
input wire[7:0] wr_data, //data to be sent to the slave
output wire[7:0] rd_data, //data received from the slave
output reg done_tick, //ticks if either write or read operation is finished
output ready, //can perform read/write operation only if ready is "1" (except for multibyte read/write where state will never go to IDLE)
//SPI pinouts
input wire miso,
output wire mosi,
output wire sclk,cs_n
);
/* template
spi #(.HI_FREQ_DIV(12),.LO_FREQ_DIV(30),.SPI_MODE(0)) m0 //High freq: 12MHz/12=1MHz , Low freq: 12MHz/30=400KHz
(
.clk(clk),
.rst(rst),
//SPI control
.rd(rd),
.wr(wr),
.hold(hold), //pins to start read or write operation, hold is for holding the clock for multibyte read/write
.wr_data(wr_data), //data to be sent to the slave
.rd_data(rd_data), //data received from the slave
.done_tick(done_tick), //ticks if either write or read operation is finished
.ready(ready), //can perform read/write operation only if ready is "1" (except for multibyte read/write where state will never go to IDLE)
//SPI pinouts
.miso(miso),
.mosi(mosi),
.sclk(sclk),
.cs_n(cs_n_1)
);
*/
localparam IDLE=0,
WRITE=1,
WRITE_DELAY=2,
READ=3,
HOLD=4;
localparam CPOL=SPI_MODE[1], //clock polarity
CPHA=SPI_MODE[0]; //clock phase
reg[2:0] state_q=0,state_d;
reg[3+!CPHA:0] count_q=0,count_d; //counter for shifting 8 times
reg[7:0] wr_data_q=0,wr_data_d; //contains data to be sent to the slave
reg[7:0] rd_data_q=0,rd_data_d; //contains data received from the slave
reg[$clog2(LO_FREQ_DIV+1)-1:0] clk_ctr_q=0,clk_ctr_d; //counter for clock division
reg cs_n_q=0,cs_n_d;
reg sclk_q=0,sclk_d;
reg hold_q=0,hold_d;
wire[$clog2(LO_FREQ_DIV+1)-1:0] CLK_DIV=clk_div? HI_FREQ_DIV:LO_FREQ_DIV;
//register operations
always @(posedge clk,posedge rst) begin
if(rst) begin
state_q<=0;
count_q<=0;
wr_data_q<=0;
rd_data_q<=0;
clk_ctr_q<=0;
cs_n_q<=0;
sclk_q<=0;
hold_q<=0;
end
else begin
state_q<=state_d;
count_q<=count_d;
wr_data_q<=wr_data_d;
rd_data_q<=rd_data_d;
clk_ctr_q<=clk_ctr_d;
cs_n_q<=cs_n_d;
sclk_q<=sclk_d;
hold_q<=hold_d;
end
end
//FSM logic
always @* begin
state_d=state_q;
count_d=count_q;
wr_data_d=wr_data_q;
rd_data_d=rd_data_q;
clk_ctr_d=clk_ctr_q;
cs_n_d=cs_n_q;
sclk_d=sclk_q;
hold_d=hold_q;
done_tick=0;
//logic for clk division and sclk
clk_ctr_d=(state_q==IDLE || state_q==HOLD || clk_ctr_q==CLK_DIV-1)? 0:clk_ctr_q+1'b1;
sclk_d=(clk_ctr_q==CLK_DIV-1) || (clk_ctr_q==((CLK_DIV>>1)-1))? !sclk_q:sclk_q;
case(state_q)
IDLE: begin
sclk_d=CPOL; //CPOL (clock polarity) is the initial value of clock
cs_n_d=1; //cs_n is active low
count_d=0;
if(wr) begin
cs_n_d=0;
wr_data_d=wr_data;
state_d=CPHA? WRITE_DELAY:WRITE; //CPHA=1: MOSI is delayed by half-clk cycle
end
if(rd) begin
cs_n_d=0;
state_d=READ;
end
end
WRITE: if(clk_ctr_q==(CLK_DIV>>CPHA)-1) begin //CPHA=1: MOSI is updated at half-clk cycle , CPHA=0: MOSI is updated at end-of-clk cycle
wr_data_d=wr_data_q<<1;//shift data 8 times (MSB first)
count_d=(count_q==7)? 0:count_q+1'b1;
if(count_q==7) begin
done_tick=1;
if(wr) begin //write again (multibyte write)
count_d=0;
wr_data_d=wr_data;
state_d=WRITE;
end
else if(rd) begin //read after writing
count_d=0;
state_d=READ;
end
else if(hold) begin
sclk_d=CPOL;
state_d=HOLD;
end
else begin //go back to IDLE
state_d=IDLE;
cs_n_d=1;
sclk_d=CPOL;
end
end
end
WRITE_DELAY: if(clk_ctr_q==(CLK_DIV>>1)-1) state_d=WRITE; //for CPHA=1 when MOSI is delayed by half-clk cycle
READ:if(clk_ctr_q==(CLK_DIV>>!CPHA)-1)begin //CPHA=1: MISO is read at end-of-clk cycle , CPHA=0: MISO is read at half-clk cycle
rd_data_d={rd_data_q[6:0],miso}; //shift read data (MSB first)
count_d=(count_q==7+!CPHA)? 0:count_q+1'b1; //CPHA=0 needs one more clk cycle(thus 0-to-8) BEFORE GOING IDLE (not needed if not going to idle after reading)
if(count_q==7) begin
done_tick=1;
hold_d=hold;
if(wr) begin //write after reading
count_d=0;
wr_data_d=wr_data;
state_d=WRITE;
end
else if(rd) begin
count_d=0;
state_d=READ; //multibyte read
end
else if(CPHA) begin //for CPHA=1 only,goes IDLE
state_d=IDLE;
sclk_d=CPOL;
cs_n_d=1;
if(hold) begin
state_d=HOLD;
cs_n_d=0;
end
end
end
if(count_q==8) begin //for CPHA=0 only, goes IDLE
rd_data_d=rd_data_q; //we added one more clk cycle for CPHA=0 before going IDLE but rd_data_q must not be updated again for that last clk cycle
state_d=IDLE;
sclk_d=CPOL;
cs_n_d=1;
if(hold_q) begin
state_d=HOLD;
cs_n_d=0;
end
end
end
HOLD: begin
sclk_d=CPOL; //CPOL (clock polarity) is the initial value of clock
count_d=0;
hold_d=0;
if(wr) begin
wr_data_d=wr_data;
state_d=CPHA? WRITE_DELAY:WRITE;
end
if(rd) state_d=READ;
end
default: state_d=IDLE;
endcase
end
assign mosi=(state_q==WRITE)? wr_data_q[7]:1'b1; //asserted to "1" when not sending/receiving data
assign rd_data=rd_data_d;
assign sclk=sclk_q;
assign cs_n=cs_n_q;
assign ready=state_q==IDLE || state_q==HOLD;
endmodule
Just like what I had stated on my first blog, I love code re-use so I also always parameterize my codes as much as possible. In this case, we can choose the SPI_MODE (0-to-3) and we have two frequency options to run the SPI: HI_FREQ_DIV and LO_FREQ_DIV. This would allow the SPI to switch between low and high frequencies which will be useful for the SD card driver since initializing the SD card is limited to 400KHz max but since we want to maximize the throughput, we must switch to high speed(25MHz) when starting to write data to it. All other pins are intuitive, we can start reading using rd pin and write using wr pin. Below is the overall schematic of the SPI driver.

Now that the SPI driver is ready, we can start making the SD card driver.
SD Card Driver
To interface with an SD card, we will need a MicroSD Card adapter just like the one below. This module is very common to be used by Arduino hobbyists since it has level shifters to switch the 5V inputs to 3.3V which is what an SD card can only handle.

On the perspective of the FPGA, the SD card is just a simple addressable sector on which it can read and write. It will use the SPI to send the commands for writing and also the data to be saved to the SD card. The command frame from FPGA to SD card is a fixed length packet (6 bytes). First byte is the index of the command, second-to-fourth bytes are the arguments, and the last byte is the 8-bit CRC. After sending this data packet, clock must continue for another 8 clock cycles before the SD card will start sending the response. Response can either be R1,R2,R3, and R7. List of all SD card commands are available from this pdf (page 219 to page 222).

There are a total of 58 usable commands but for my SD card driver, I only used these seven basic commands:
| CMD Index | Name | Description |
| CMD0 | GO_IDLE_STATE | Reset then start SPI mode |
| CMD8 | SEND_IF_COND | Check voltage range |
| CMD59 | CRC_ON_OFF | Turn on/off the CRC validation |
| ACMD41 | SD_SEND_OP_COND | Start initialization |
| CMD58 | READ_OCR | Check CCS bit |
| CMD24 | WRITE_BLOCK | Write in a block(512 bytes fixed) |
| CMD13 | SEND_STATUS | Check if write operation succeeded |
Now for the tricky part, before doing a read or write operation, the SD card must be first initialized properly. The sequence of commands for initialization are shown below:

For writing to SD card, CMD14 must be used. SD card has a fixed block length of 512 bytes. This means that a write command will not stop until it receive 512 bytes from the host. After writing,the SD card will be busy for some definite time and will leave the DO(or MISO) pin low. When this pin becomes high again, we can then send the next command.
And that is all the things we must know to make a basic SD card driver, here is my driver implementation in Verilog HDL:
`timescale 1ns / 1ps
module sdcard_interface(
input wire clk,rst,
output wire led0_r,led0_g,led0_b, //{red,green,blue} red if SDCARD initialization is stuck at CMD0, blue if stuck somewhere else, green if initialization complete
output idle, //sdcard not busy
//HOST interface
input write, //start writing to SD card
output reg rd_fifo, //read next data to be written
input[7:0] data, //data to be written to SD catd
//SPI pinouts
input wire SD_MISO,
output wire SD_MOSI,
output wire SD_DCLK,SD_nCS,
//UART for debugging
output wire uart_rx,uart_tx
);
//FSM states
localparam POWER_ON=0,
COMMANDS=1,
SEND_COMMAND=2,
RECEIVE_RESPONSE=3,
END_CMD=4,
IDLE=5,
DELAY=6,
WRITE_1=7,
WRITE_2=8,
BUSY=9;
reg[3:0] state_q=0,state_d;
reg[9:0] counter_q=0,counter_d; //counter for the 74 clk cycles needed for power-on
reg[3:0] cmd_counter_q=0,cmd_counter_d; //index for cmd_list
reg[3:0] response_counter_q=0,response_counter_d; //number of bytes needed for a response (R1=1 byte , R7=R3=5 bytes)
reg[55:0] wr_data_q=0,wr_data_d;
reg[39:0] rd_data_q=0,rd_data_d;
reg[2:0] led_q=0,led_d;
reg stuck_q=0,stuck_d;
reg[9:0] stuck_counter_q=0,stuck_counter_d;
reg[15:0] addr_counter_q,addr_counter_d;
//SPI pinouts
reg rd,wr,hold;
reg[7:0] wr_data;
reg clk_div_q=0,clk_div_d;
wire[7:0] rd_data;
wire done_tick,ready;
wire clk_div,cs_n_1;
//uart PINOUTS
reg wr_uart,rd_uart;
reg[7:0] wr_data_uart;
wire[7:0] rd_data_uart;
wire rx_empty;
//list of commands for SDCARD initialization
localparam INIT_LAST_INDEX=6;
reg[55:0] cmd_list[10:0];
initial begin
cmd_list[0]=48'h40_00_00_00_00_95; //CMD0: GO_IDLE_STATE (R1) //resets SDCARD for SPI mode
cmd_list[1]=48'h48_00_00_01_AA_87; //CMD8: SEND_IF_COND (R7) //chceck host voltage supply and if ver2.00 above
cmd_list[2]=48'h7B_00_00_00_00_83; //CMD59 CRC_ON_OFF (R1) //turn off CRC checking
cmd_list[3]=48'h77_00_00_00_00_00; //CMD55: prefix for every ACMD (application commad) (R1)
cmd_list[4]=48'h69_40_00_00_00_00; //ACMD41:SD_SEND_OP_COND ((R1) //iniitialize SDCARD
cmd_list[5]=48'h7A_00_00_00_00_00; //CMD58:READ_OCR (R3) //check if SDSC(standard) or SDHC/SDXC
cmd_list[6]=48'h50_00_00_02_00_00; //CMD16: set block length to 512
cmd_list[7]=48'h58_00_00_00_00_00; //CMD24: Single WRITE TO ADDRESS 0
cmd_list[8]=48'h4D_00_00_00_00_00; //Status Reg
end
//register operations
always @(posedge clk,posedge rst) begin
if(rst) begin
state_q<=0;
counter_q<=0;
wr_data_q<=0;
rd_data_q<=0;
led_q<=0;
cmd_counter_q<=0;
response_counter_q<=0;
stuck_q<=0;
stuck_counter_q<=0;
clk_div_q<=0;
addr_counter_q<=0;
end
else begin
state_q<=state_d;
counter_q<=counter_d;
wr_data_q<=wr_data_d;
rd_data_q<=rd_data_d;
led_q<=led_d;
cmd_counter_q<=cmd_counter_d;
response_counter_q<=response_counter_d;
stuck_q<=stuck_d;
stuck_counter_q<=stuck_counter_d;
clk_div_q<=clk_div_d;
addr_counter_q<=addr_counter_d;
end
end
//FSM logic
always @* begin
state_d=state_q;
counter_d=counter_q;
wr_data_d=wr_data_q;
rd_data_d=rd_data_q;
led_d=led_q;
cmd_counter_d=cmd_counter_q;
response_counter_d=response_counter_q;
stuck_d=stuck_q;
stuck_counter_d=stuck_counter_q;
clk_div_d=clk_div_q;
addr_counter_d=addr_counter_q;
rd=0;
wr=0;
hold=0;
wr_data=8'hff;
rd_fifo=0;
case(state_q)
///////////////////////////////////START SDCARD INITIALIZATION////////////////////////////////////////////
POWER_ON: begin //send at least 74 clk cycles with cs_n and d_out line high
rd=1;
led_d=3'b100;
cmd_counter_d=0;
clk_div_d=0;
addr_counter_d=0;
if(done_tick) begin
counter_d=counter_q+1'b1;
if(counter_q==15) begin//8*10=80 clk cycles had passed
rd=0;
state_d=COMMANDS;
end
end
end
COMMANDS: if(ready) begin //commands to be sent to SDCARD
wr_data_d=cmd_list[cmd_counter_q];
if(cmd_counter_q==7) wr_data_d[39:8]=2049+addr_counter_q; //start address(2050): Real start: 2049-2048+1=2
state_d=SEND_COMMAND;
response_counter_d=0;
counter_d=0;
stuck_counter_d=0;
stuck_d=0;
end
SEND_COMMAND: if(ready || done_tick) begin
wr_data_d={wr_data_q[47:0],8'hff}; //shift by 1 byte
wr_data=wr_data_d[55:48];
wr=1;
counter_d=counter_q+1'b1;
if(counter_q==7) begin //6 bytes had been sent to SPI, another 1 byte for the 8 clk cycles needed by sdcard before responding
wr=0;
rd=1; //response always starts at logic 0 so hold the clock until then
counter_d=0;
state_d=RECEIVE_RESPONSE;
end
end
RECEIVE_RESPONSE: if(done_tick) begin
response_counter_d=response_counter_q+1; //counter for some responses that has multiple bytes (R3 and R7)
//rules for types of response for every command
case(cmd_counter_q)
0: begin
cmd_counter_d=(rd_data==8'h01)? cmd_counter_q+1:cmd_counter_q; //CMD0: resets SDCARD for SPI mode
state_d=END_CMD;
end
1: begin
rd_data_d={rd_data_q[31:0],rd_data};
rd=1;
led_d=3'b001;
if(response_counter_d==5) begin//5 bytes had been received
cmd_counter_d=(rd_data_d==40'h01_00_00_01_AA)? cmd_counter_q+1:cmd_counter_q; //CMD8: Host Voltage Supply is correct and SDCARD is ver2.00 or later
rd=0;
state_d=END_CMD;
end
end
2: begin
cmd_counter_d=(rd_data==8'h01)? cmd_counter_q+1:cmd_counter_q; //CMD59: turns off CRC checking
state_d=END_CMD;
end
3: begin
cmd_counter_d=(rd_data==8'h01)? cmd_counter_q+1:cmd_counter_q; //CMD55: Now ready for next command which is an Application Commmand(ACMD)
state_d=END_CMD;
end
4: begin
stuck_counter_d=stuck_counter_q+1;
if(stuck_counter_q==1000) stuck_d=1; //if ACMD41 stuck 1000x , go back to power-on
cmd_counter_d=(rd_data==8'h00)? cmd_counter_q+1:cmd_counter_q-1; //ACMD41: Initialization of SDCARD is complete
state_d=END_CMD;
end
5: begin
rd_data_d={rd_data_q[31:0],rd_data};
rd=1;
if(response_counter_d==5) begin//5 bytes had been received
rd=0;
cmd_counter_d=(rd_data_d==40'h00_C0_FF_80_00)? cmd_counter_q+1:cmd_counter_q; //CMD58: CCS bit is 1 and SDCARD is classified as SDHC(High Capacity) or SDXC(eXtended Capacity)
state_d=END_CMD;
end
end
6: begin
cmd_counter_d=(rd_data==8'h00)? cmd_counter_q+1:cmd_counter_q; //CMD55: Now ready for next command which is an Application Commmand(ACMD)
state_d=END_CMD;
end
7: begin //acknowledge for write
state_d=WRITE_1;
end
8: begin
rd_data_d={rd_data_q[31:0],rd_data};
rd=1;
if(response_counter_d==2) begin//5 bytes had been received
cmd_counter_d=(rd_data_d==16'h0000)? cmd_counter_q+1:cmd_counter_q; //CMD8: Host Voltage Supply is correct and SDCARD is ver2.00 or later
rd=1;
state_d=IDLE;
led_d=3'b010;
end
end
endcase
end
END_CMD:if(ready) begin //must provide 8 clks before shutting down sclk or starting new command
wr=1;
state_d=stuck_q? POWER_ON:COMMANDS;
if(cmd_counter_q==INIT_LAST_INDEX+1 || clk_div_q) state_d=DELAY;
end
DELAY: if(ready) begin //delay before switching to high frequency for writing in SDCARD
stuck_counter_d=stuck_counter_d+1;
clk_div_d=1;
if(stuck_counter_d==1000) begin
led_d=3'b010;
state_d=IDLE;
end
end
///////////////////////////////////////INITIALIZATION COMPLETE///////////////////////////////////////////////
/////////////////////////////SDCARD READ/WRITE OPERATION//////////////////////////////////////////////////
IDLE: if(write) begin
cmd_counter_d=7; //WRITE
state_d=COMMANDS;
addr_counter_d=addr_counter_q+1;
led_d=100;
end
WRITE_1: if(ready || done_tick) begin
if(counter_q==0 || counter_q==513 || counter_q==514) wr_data=8'b1111_1110; //start_token
else begin
wr_data=data;
rd_fifo=1;
end
wr=1;
counter_d=counter_q+1'b1;
if(counter_q==515) begin //515 bytes had been sent to SPI,
wr=0;
rd_fifo=0;
rd=1; //Data response immediately
counter_d=0;
state_d=WRITE_2;
end
end
WRITE_2: if(done_tick) begin
if(rd_data[4:0] == 5'b0_010_1) begin //data accepted
state_d=BUSY;
end
end
BUSY: begin
rd=1;
if(SD_MISO && done_tick) begin //sd card finishes the writing operation
cmd_counter_d=8;
state_d=COMMANDS;
end
end
default: state_d=POWER_ON;
endcase
end
assign SD_nCS=(state_q==POWER_ON || state_q==COMMANDS || (state_q==END_CMD && ready) || state_q==IDLE)? 1'b1:cs_n_1; //at power_on, toggle clk 74 times WHILE cs_n is high
assign led0_r=led_q[2]? clk:1'b1,
led0_g=led_q[1]? clk:1'b1,
led0_b=led_q[0]? clk:1'b1; //PWM used is the clk itself
assign idle=state_q==IDLE;
//module instantiations
spi #(.HI_FREQ_DIV(20), .LO_FREQ_DIV(250), .SPI_MODE(0)) m0 //High freq: 100MHz/6=16.7MHz , Low freq: 100MHz/250=400KHz
(
.clk(clk),
.rst(rst),
//SPI control
.clk_div(clk_div_q),
.rd(rd),
.wr(wr),
.hold(hold), //pins to start read or write operation, hold is for holding the clock for multibyte read/write
.wr_data(wr_data), //data to be sent to the slave
.rd_data(rd_data), //data received from the slave
.done_tick(done_tick), //ticks if either write or read operation is finished
.ready(ready), //can perform read/write operation only if ready is "1" (except for multibyte read/write where state will never go to IDLE)
//SPI pinouts
.miso(SD_MISO),
.mosi(SD_MOSI),
.sclk(SD_DCLK),
.cs_n(cs_n_1)
);
uart #(.DBIT(8),.SB_TICK(16),.DVSR(326),.DVSR_WIDTH(9),.FIFO_W(10)) m1 //9600 Baud
(
.clk(clk),
.rst_n({!rst}),
.rd_uart(rd_uart),
.wr_uart(wr_uart),
.wr_data(wr_data_uart),
.rx(uart_rx),
.tx(uart_tx),
.rd_data(rd_data_uart),
.rx_empty(rx_empty),
.tx_full()
);
//UART for debugging SDCARD responses
always @* begin
wr_uart=0;
rd_uart=0;
wr_data_uart=0;
wr_uart= wr || (state_q==RECEIVE_RESPONSE && done_tick) || (state_q==WRITE_2 && done_tick); //write all commands passing thrugh MOSI and MISO
wr_data_uart=wr? wr_data:rd_data;
end
endmodule
For easier debugging of the driver, I added a UART and some RGB leds. At power-up, the SD card will automatically start the initialization process.After initialization,, idle pin will go high, this notifies the host that it can start the write operation by triggering the write pin and send the data to be sent to the data pin. Since 512 subsequent data must be sent in one go, the driver will make the rd_fifo high to notify the host that it needs the next data to write.The first address will depend on parameter FIRST_ADDRESS which on this case is set to address 2050. Below is the overall schematic of the SD card driver.

Preview for the Next Blog:
Now that I have implemented the driver for the SD card, will it really work? Next stop: testing the SD card driver.
6-Part Blog Series
- Security Camera #1: Project Proposal
- Security Camera #2: SD Card Interfacing
- Security Camera #3: Testing the SD Card Driver
- Security Camera #4: Interfacing with OV7670 Camera
- Security Camera #5: Adding Peripheral Sensors
- Security Camera #6: Project Demonstration and Final Words
To see more of my FPGA projects, visit my GitHub account: https://github.com/AngeloJacobo
References:
http://elm-chan.org/docs/mmc/mmce.html
http://nerdclub-uk.blogspot.com/2012/11/how-spi-works-with-sd-card.html
http://nerdclub-uk.blogspot.com/2012/11/how-spi-works-with-sd-card.html
https://components101.com/modules/micro-sd-card-module-pinout-features-datasheet-alternatives
https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/