1. 准备工作硬件准备- 开发板
8 c( f, B V% o+ \; ~首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):/ y* n0 d8 u1 n3 k o0 q! {' \
( ?" b+ d& _, j9 C% K

- Micro SD卡0 b8 e" `9 h4 `# ?# [& ^ K
小熊派开发板板载 Micro SD 卡槽,最大支持 32 GB,需要提前自行准备一张 Micro SD卡,如图:' V& Y; I6 w- h3 `2 k! b
) h* J2 d' ~1 E3 g3 o

2 V4 ]5 L; r1 p7 ?9 b软件准备- 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;
- 准备一个串口调试助手,这里我使用的是Serial Port Utility;
4 u8 p+ i" }& B6 F& w
0 o8 `. O/ [9 | K: \- J
. p( I5 r: P5 P8 V% P4 I% i 2.生成MDK工程3 n! U* ^$ m6 H) |
选择芯片型号打开STM32CubeMX,打开MCU选择器:. s0 V+ T. d, f. S0 A
 搜索并选中芯片STM32L431RCT6:
6 A. e3 H8 r7 u1 Z5 M4 m8 I % H$ O2 f5 a, T5 B
配置时钟源- 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
- 如果使用默认内部时钟(HSI),这一步可以略过;! L+ L4 ]6 x( {! f. J7 m; |% F
0 ?# N8 P- p- d8 p
这里我都使用外部时钟:  8 I6 c6 z2 S/ Y0 S9 v6 K$ d# N
配置串口小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下: 
这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。 接下来开始配置USART1: 
d* K7 j( Q( m5 A, r4 ~配置 SDMMC 接口知识小卡片 —— SDMMC接口
SDMMC接口的全称叫SD/SDIO MMC card host interface,SD/SDIO MMC 卡 主机接口,通俗的来说,就是这个接口支持SD卡,支持SDIO设备,支持MMC卡。 知识小卡片结束啦~
首先查看小熊派开发板的原理图: 
然后根据原理图配置 SDMMC 接口:  ' d9 O- r4 T N+ x5 n
配置时钟树STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可: 
4 E$ R/ i1 y8 y6 o* O2 u6 X2 [生成工程设置 4 d: o; `' T/ s& z, w# t6 G! T
代码生成设置
, R; Y" {6 K- i% z3 p0 n# D4 ~最后设置生成独立的初始化文件: 
& X7 W+ B4 g; y/ b1 |
生成代码点击GENERATE CODE即可生成MDK-V5工程: 
. ~ b0 {4 ~2 z; n2 T! o 3. 在MDK中编写、编译、下载用户代码
+ \: g) ], {4 s, q8 _& ?重定向printf( )函数3 X+ h/ V3 o2 \; d: z6 ?
读取SD卡信息并打印SD 卡系统(包括主机和 SD 卡)定义了两种操作模式: - 卡识别模式
- 数据传输模式! ~# r3 A9 p6 @9 |9 w0 b
, d Q1 Z8 K6 [8 [6 \/ u
在系统复位后,主机处于卡识别模式,寻找总线上可用的 SD卡设备;同时,SD 卡也处于卡
. a7 J* U8 Y* O8 S% E识别模式,直到被主机识别到。 使用STM32CubeMX初始化的工程中会自动生成 SDMMC 初始化函数,向 SD 卡发送命令,当 SD 卡接收到命令后, SD 卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。 所以在操作之前,需要先检查 SD 卡是否处于数据传输模式并且处于数据传输状态: 在main函数中首先定义一个变量用于存储 SD 卡状态: - <font color="#000000">int sdcard_status = 0;
G0 Q2 u0 \/ u: _ - HAL_SD_CardCIDTypeDef sdcard_cid;</font>
复制代码然后在while(1)之前编写如下读取信息代码: - <font color="#000000">/* USER CODE BEGIN 2 */+ {* E4 v4 ]+ d! I# B) n7 `) a
- printf("Micro SD Card Test...\r\n");8 L8 `0 K( ^+ V c5 r& _; z* F6 O
- </font>
复制代码
7 i2 K, j: v' Z) j! q: ^
/* 检测SD卡是否正常(处于数据传输模式的传输状态) */; _ s) A/ |3 r8 f
sdcard_status = HAL_SD_GetCardState(&hsd1);
2 f5 o. I8 M, C6 W% |9 N. Xif(sdcard_status == HAL_SD_CARD_TRANSFER)/ s" g5 E( d" x' W8 W/ {* b
{. N( w$ y9 Z# Q' Z
printf(“SD card init ok!\r\n\r\n”); - <font color="#000000"><span class="token comment">//打印SD卡基本信息</span>0 M1 k5 K% H! E. L6 l' m% b
- <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"SD card information!\r\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span># D4 m% W2 S9 a; H7 C7 |0 G
- <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"CardCapacity: %llu\r\n"</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">long</span> <span class="token keyword">long</span><span class="token punctuation">)</span>hsd1<span class="token punctuation">.</span>SdCard<span class="token punctuation">.</span>BlockSize<span class="token operator">*</span>hsd1<span class="token punctuation">.</span>SdCard<span class="token punctuation">.</span>BlockNbr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
6 p; x! M' g+ q3 q) w% P - <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"CardBlockSize: %d \r\n"</span><span class="token punctuation">,</span>hsd1<span class="token punctuation">.</span>SdCard<span class="token punctuation">.</span>BlockSize<span class="token punctuation">)</span><span class="token punctuation">;</span>
6 s- _; M8 ]" P' N7 D - <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"RCA: %d \r\n"</span><span class="token punctuation">,</span>hsd1<span class="token punctuation">.</span>SdCard<span class="token punctuation">.</span>RelCardAdd<span class="token punctuation">)</span><span class="token punctuation">;</span>
7 K5 X( k" J7 f F - <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"CardType: %d \r\n"</span><span class="token punctuation">,</span>hsd1<span class="token punctuation">.</span>SdCard<span class="token punctuation">.</span>CardType<span class="token punctuation">)</span><span class="token punctuation">;</span>
3 z7 V- [" D! T' P" V - g6 A; I3 O: M; A0 F7 n" d
- <span class="token comment">//读取并打印SD卡的CID信息</span>& l. h6 H( I% w; Z9 {: g2 |
- <span class="token function">HAL_SD_GetCardCID</span><span class="token punctuation">(</span><span class="token operator">&</span>hsd1<span class="token punctuation">,</span><span class="token operator">&</span>sdcard_cid<span class="token punctuation">)</span><span class="token punctuation">;</span>2 b2 l. }- v" i: J+ @5 E9 D: p% G
- <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"ManufacturerID: %d \r\n"</span><span class="token punctuation">,</span>sdcard_cid<span class="token punctuation">.</span>ManufacturerID<span class="token punctuation">)</span><span class="token punctuation">;</span></font>
复制代码
9 f4 x- ~1 m' q1 `
}
: V( {- A4 T9 Q; _: P. x; n6 delse2 C) r1 L1 {7 e* Y0 y+ h
{3 W0 G* Q. l% H \
printf(“SD card init fail!\r\n” );
/ W$ A/ r, [! Z4 l/ U# V( S$ kreturn 0;+ ]6 m3 x% l" R' M4 v
}
* @( E) B- r6 y- a1 C0 n" q/* USER CODE END 2 */ . u% l/ P% t0 I& m& A7 F& h: E
编译下载后串口助手输出结果如下: 
7 G z6 r V9 e g9 m/ i$ q3 x& M
擦除SD卡块数据为了验证实验的正确性或,先擦除数据: - /* 擦除SD卡块 */
% k% u1 W) c x4 |! b z - printf("------------------- Block Erase -------------------------------\r\n");
7 C. Z k; }/ R8 F/ s - sdcard_status = HAL_SD_Erase(&hsd1, 0, 512);
1 Z) @5 v2 _6 s5 g: d - if (sdcard_status == 0)) F- U0 L2 l; F o0 ~1 f
- {( `5 `0 O& p7 `4 G# H" r0 n
- printf("Erase block ok\r\n");
# | H6 [+ k0 V* {" h - }3 V7 V- U. q/ h
- else
0 C9 z. I" b3 k# b0 X+ @( w - {* |. H+ d& m: f$ |1 S
- printf("Erase block fail\r\n"); Q% P6 `% Z+ e5 E
- }
0 i c3 g. F% O% `
复制代码 ' `7 N$ d7 |- w' p! x
读取SD卡块数据- /* Private user code ---------------------------------------------------------*/6 {/ ?. h0 _6 a# S; U- `' T, Z( j6 Q
- /* USER CODE BEGIN 0 */
2 t' y b5 y- v5 ?" ]# H1 C7 r - uint8_t read_buf[512];' B" ~1 a0 M9 {5 _5 ^: e0 E
- /* USER CODE END 0 */- x1 I* z4 f8 s+ I# N" n" S# r
复制代码
1 L, `1 V+ }3 Y然后在之前读取信息的代码之后添加读取数据的代码: - /* 读取未操作之前的数据 */* I& b; C) O9 h1 t! L
- printf("------------------- Read SD card block data Test ------------------\r\n");2 I, ~# ~2 v: [; p B O% i5 z
- sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);
7 X/ P2 ^. e( }; | - if(sdcard_status == 0)- M0 |- X+ ^. V7 V
- { 9 Z0 [$ X- r! V7 x
- printf("Read block data ok \r\n" );+ k2 c( B3 U* t3 B; ]7 {% D) k V
- for(i = 0; i < 512; i++) \2 [" x9 C, x5 ^: m/ P
- {
/ c8 P! Z( E( `' N4 r - printf("0x%02x ", read_buf[i]);
9 j- D* ~# q, n# k* U, e. m# p - if((i+1)%16 == 0)
9 K/ b1 ^& C5 M" v- I G - {4 B/ c1 l- n, v/ g( h
- printf("\r\n");1 s' Q4 M& G) F1 N3 t' G4 q
- }: D/ U7 C; a3 F
- }6 d% z( w3 I" E: i/ N
- }
; D" K9 J% j3 c/ Q - else
4 A4 s8 k& `- o, t; M, M - {0 s7 j( l0 x* p0 i+ N, Z
- printf("Read block data fail!\r\n " );6 N; G9 N9 z6 z& ^1 d( p* @
- }
& B; p" B( _# Y, A4 e( S
复制代码 2 ?$ v! J a2 \" E, R8 |4 n
向SD卡块写入数据同样的,开辟一个全局缓冲区,用于存放即将要写入SD卡的数据: - uint8_t write_buf[512];6 t5 M: e/ b+ c: E, }: m6 z
复制代码 0 A; \5 R! Q4 ~# E1 y1 [
然后在之前读取数据的代码之后添加的代码,将缓冲区的数据赋初值: - /* 填充缓冲区数据 */
% c$ C c8 ?! l - for(i = 0; i < 512; i++)
4 K: U! ?8 Z( I. t2 N - {# n' F" d2 f* b2 c" W4 ~
- write_buf[i] = i % 256;. Z4 [9 x& X4 U4 p( X ?6 e6 S
- }9 l" p( j9 Z0 @6 H- a( f* M$ h. H
复制代码 " J' v% b( D8 S L
然后继续添加代码,将该缓冲区数据写入SD卡: - /* 向SD卡块写入数据 */
) B) X5 [( H$ m V N" Y: q - printf("------------------- Write SD card block data Test ------------------\r\n");
1 C i9 ^5 R9 P4 n6 }' }: | - sdcard_status = HAL_SD_WriteBlocks(&hsd1,(uint8_t *)write_buf,0,1,0xffff);
& q4 D6 a7 U! M' G. L( E - if(sdcard_status == 0)$ \ Z, N4 a \- E
- {
( {# P; u. H$ j" N - printf("Write block data ok \r\n" );4 O3 g# Y: j6 C0 C5 T* ~( b: B
- }
& a' I# z. ] u- b" m3 R - else/ _ ?6 f: m1 y7 t/ c: i
- {! H* I6 d' }0 }& r5 Z& y4 _
- printf("Write block data fail!\r\n " );
: z6 Z0 w- J- h0 M' g - }
+ n0 _1 G' @4 Q. S
复制代码
/ B G* V7 E; K: X6 o添加完之后,为了检查数据是否正常写入,再将数据读出: - /* 读取操作之后的数据 */
5 W! ]* M. y+ B" ?. ^: c - printf("------------------- Read SD card block data after Write ------------------\r\n");3 C/ K- l8 _* x$ M8 h" |/ o: k
- sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);2 p) j/ I ~7 M, ]% D* a& C
- if(sdcard_status == 0)
+ p5 Y, u4 F. f& u: h - { 1 P; W5 c. @9 s
- printf("Read block data ok \r\n" );, _/ `8 H! x, b: v0 c4 t$ j3 w0 d
- for(i = 0; i < 512; i++)
$ O5 c! z0 H) E# J r' N - {+ e" a& N2 u. W& U
- printf("0x%02x ", read_buf[i]);, a: G" u. \' f
- if((i+1)%16 == 0)
* l3 h' [1 T" S. J0 o - {
0 [0 E; n5 m% M; T! v - printf("\r\n");
- A# b, N: n4 D d9 l4 [ - }6 t9 p9 K/ D' Z
- }! O. o; H1 _5 C# |7 T' h
- }
. x8 J7 c" z6 R) F/ N
复制代码 9 V3 D. E8 \" V$ q# h0 _
将程序编译下载,最终的实验结果如下: 
* O, D5 v3 F9 G5 Q& Q5 c

, W& B4 K( c8 h5 e5 E3 O$ H

& q7 f+ }9 u4 j1 ~7 s8 Q

' Z/ l$ b- S4 g+ c8 C- S! m5 y4 D( w% r
|