1. 准备工作硬件准备- 开发板" S& ~3 H0 t q: L
首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):
5 m+ p5 \( m, Y' T3 x3 {, L) ^1 Q# O9 m$ i

- Micro SD卡6 W. C' k( q* x" h$ f
小熊派开发板板载 Micro SD 卡槽,最大支持 32 GB,需要提前自行准备一张 Micro SD卡,如图:6 T( u% ]1 \ E o y% E% b+ K0 {
3 t% ^ |6 h5 G8 `8 P
 : E* y- _- z! @! t! w
软件准备- 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;
- 准备一个串口调试助手,这里我使用的是Serial Port Utility;
# j) y. R2 E' S9 h1 I# L8 X# `; D f& ^- B$ s3 X2 H
( W" K/ D: u0 ^$ c! H6 L! N 2.生成MDK工程: y/ u4 t# f k5 i# P
选择芯片型号打开STM32CubeMX,打开MCU选择器:; g5 ]. d1 g5 K1 ]% M
 搜索并选中芯片STM32L431RCT6:
9 g3 _9 y% m+ {0 |# ~/ j$ ~+ z ' p* Y4 q7 V3 T5 v
配置时钟源- 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
- 如果使用默认内部时钟(HSI),这一步可以略过;
; `6 m$ v; N7 Y' ]% U2 \3 |8 L* i2 p; l3 V! ]. }, |0 E
这里我都使用外部时钟: 
# q& e- Q0 r; ~4 y. S: i配置串口小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下: 
这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。 接下来开始配置USART1: 
4 ]( V' u. P% S; ^% h0 E配置 SDMMC 接口知识小卡片 —— SDMMC接口
SDMMC接口的全称叫SD/SDIO MMC card host interface,SD/SDIO MMC 卡 主机接口,通俗的来说,就是这个接口支持SD卡,支持SDIO设备,支持MMC卡。 知识小卡片结束啦~
首先查看小熊派开发板的原理图: 
然后根据原理图配置 SDMMC 接口: 
+ y# J3 I4 m" p8 ?6 u3 R% w# l1 [配置时钟树STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可:  9 A& y" `: l3 u; x7 z. ]4 }* t4 b
生成工程设置 7 |* t( f) t0 _
代码生成设置
9 x5 x" t, Y( |. g最后设置生成独立的初始化文件: 
4 s5 Q2 M" |1 N, g b U& B1 F 生成代码点击GENERATE CODE即可生成MDK-V5工程: 
7 A# Y( X4 t& Q1 K 3. 在MDK中编写、编译、下载用户代码' v0 T5 o& g9 X
重定向printf( )函数7 T( J) g& d9 ~& [: B' M
读取SD卡信息并打印SD 卡系统(包括主机和 SD 卡)定义了两种操作模式: - 卡识别模式
- 数据传输模式
) A* \4 Z; J9 R+ B0 V, g, \' ^( C6 I1 H
在系统复位后,主机处于卡识别模式,寻找总线上可用的 SD卡设备;同时,SD 卡也处于卡; `+ R% L" k. v6 o3 c5 y9 U; M
识别模式,直到被主机识别到。 使用STM32CubeMX初始化的工程中会自动生成 SDMMC 初始化函数,向 SD 卡发送命令,当 SD 卡接收到命令后, SD 卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。 所以在操作之前,需要先检查 SD 卡是否处于数据传输模式并且处于数据传输状态: 在main函数中首先定义一个变量用于存储 SD 卡状态: - <font color="#000000">int sdcard_status = 0;! I' z: O) u0 h* @7 k. j& u g5 j
- HAL_SD_CardCIDTypeDef sdcard_cid;</font>
复制代码然后在while(1)之前编写如下读取信息代码: - <font color="#000000">/* USER CODE BEGIN 2 */
( S3 g7 }% W. N3 [* t, }. ^4 M - printf("Micro SD Card Test...\r\n");
# ~ x4 a5 |" @, G% c - </font>
复制代码
# @2 n7 `0 l6 w5 Y0 \2 s2 |
/* 检测SD卡是否正常(处于数据传输模式的传输状态) */7 X* q, Z, t: \0 }
sdcard_status = HAL_SD_GetCardState(&hsd1);
3 e3 e( _/ I c7 w+ fif(sdcard_status == HAL_SD_CARD_TRANSFER): [" R5 ~2 h3 w
{/ Y$ e$ C# z x9 A8 a! k
printf(“SD card init ok!\r\n\r\n”); - <font color="#000000"><span class="token comment">//打印SD卡基本信息</span>" }- G0 K( [& }* i
- <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>
$ T4 U4 ~+ Z2 A - <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>
0 u$ D6 S7 {! D, i6 K3 _ - <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>7 G* |( C! U. h! f
- <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>* Q) ?% s) ~6 W/ O: ~3 I, w
- <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>
5 P* d9 t' V7 j
! o8 V" O. J! Z0 G4 b/ i- <span class="token comment">//读取并打印SD卡的CID信息</span>
& x+ x' Z7 D0 D T8 p* ^9 q3 b - <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>
* N7 @# r) b" {' s; R1 K* B! o - <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>
复制代码( V7 o0 O7 c: i9 |) O9 W
}
" k$ f+ Y' E9 R5 qelse
* o: K [& H8 z4 G9 O0 c; c9 T! J{! E" O% `4 U# g/ v
printf(“SD card init fail!\r\n” );
L# \/ b1 t4 k' m1 k, Lreturn 0;
5 @& j) t6 k/ c$ E) Y% J}
% h* u, Q0 y: u/* USER CODE END 2 */
" S( \# R F' G) t# P: ^
编译下载后串口助手输出结果如下: 
6 S# u8 k# M+ O1 C
擦除SD卡块数据为了验证实验的正确性或,先擦除数据: - /* 擦除SD卡块 */0 w5 r7 y8 O3 J% A
- printf("------------------- Block Erase -------------------------------\r\n");1 S! x0 M# H* Z4 @ Z9 M! ~
- sdcard_status = HAL_SD_Erase(&hsd1, 0, 512);
1 W6 _, [" t! Y2 e9 N0 m - if (sdcard_status == 0)7 e# p! s* u" N
- {0 w/ p3 A2 ?* ~2 e% ^! Q% t6 ~; C
- printf("Erase block ok\r\n");! }7 q& M5 W" b3 a' m) x
- }/ w0 \; E1 h) M% ^- G
- else# j& J2 W% i# X1 K/ x
- {
/ J- u2 \$ j% z; r - printf("Erase block fail\r\n");) A4 N! W! E9 k
- }- c3 V0 i% ]1 ~$ m. {* E. q0 {, D7 N
复制代码
# J u" V2 @6 t' U1 R读取SD卡块数据- /* Private user code ---------------------------------------------------------*/1 i0 L6 } N! J8 j" p# ^9 H
- /* USER CODE BEGIN 0 */5 x; T& ~+ |! v% P# ?: l, e
- uint8_t read_buf[512];
6 e' j2 m# U8 _, S+ n' W! j - /* USER CODE END 0 */4 x+ M0 W4 U- f* ]5 Z2 L6 d
复制代码
1 Q: M! h. t1 ]然后在之前读取信息的代码之后添加读取数据的代码: - /* 读取未操作之前的数据 */
7 y: e. W/ f$ M" f$ h - printf("------------------- Read SD card block data Test ------------------\r\n");
% b. K5 x8 l5 O" x - sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);
7 D0 }' P* V4 j0 m/ A9 k9 ] - if(sdcard_status == 0): y# O1 U! n4 I! \- e/ r
- {
. k+ i. R5 U, w. y) o/ c# Z7 q - printf("Read block data ok \r\n" );
! y( p" y" \6 N) n - for(i = 0; i < 512; i++)# B. i: K! u6 C& M, V: B9 C
- {: W3 i6 K" `* d/ B, y* d
- printf("0x%02x ", read_buf[i]);% |9 m- b- l, ]0 R1 [
- if((i+1)%16 == 0)
* X/ ]6 H) `" c4 J F8 [1 | - {5 K; w) W, o Q4 M S6 i
- printf("\r\n");1 `+ s( v, {4 d, a: t! h/ X
- }
$ S5 j+ F3 P" J$ q1 Y - }
$ J* Y4 \' F; y, r3 i4 T - }
7 g- X* V4 P: `! L8 N - else* N) Z* q+ {* e5 E9 y4 Q6 g$ x2 v/ }
- {' H! x6 k% e; V/ P9 G1 f& x( F9 S
- printf("Read block data fail!\r\n " );
% L2 J* ^/ ^. v0 \0 `" T - }9 N! ~0 _6 X6 Y; Q9 r* j9 Y% b
复制代码 ! f" Z' A' R$ x. c6 c* r2 N2 H
向SD卡块写入数据同样的,开辟一个全局缓冲区,用于存放即将要写入SD卡的数据: - uint8_t write_buf[512];. I0 W; } {3 L1 W! M" ~3 L
复制代码 ' |7 U) t& ~6 h1 W" x
然后在之前读取数据的代码之后添加的代码,将缓冲区的数据赋初值: - /* 填充缓冲区数据 */4 L. G; z: z. O# P+ [7 {
- for(i = 0; i < 512; i++)
( g7 y! D$ Z9 x1 U1 |/ x+ K( P - {3 c1 |; E& T! k1 y' ]
- write_buf[i] = i % 256; L0 H6 w/ T3 q; C5 |% `1 e; \5 j
- }* z, d5 l; m% }$ `+ ?
复制代码 5 O6 _2 A9 T8 T. k& N
然后继续添加代码,将该缓冲区数据写入SD卡: - /* 向SD卡块写入数据 */# o( l! s8 G7 Y ^! J- {
- printf("------------------- Write SD card block data Test ------------------\r\n");
4 S- o0 k3 v8 D, ~" |8 X - sdcard_status = HAL_SD_WriteBlocks(&hsd1,(uint8_t *)write_buf,0,1,0xffff);2 H { F9 ?. `% d1 F
- if(sdcard_status == 0)
+ S0 z5 \7 p) i - {
/ C. F' v% d0 u& j2 A Q - printf("Write block data ok \r\n" );8 M/ ~# e3 x" K2 C3 H
- }) ~) D/ n1 _3 n5 h% }( {
- else" o$ h& Y& v( \, {% z
- {
* w, \- |4 d2 i& ?$ m - printf("Write block data fail!\r\n " );, x& g' e1 g7 J6 [' x! x
- } Z# w/ Z" P* S) |" N/ M' h
复制代码 5 O4 q+ T* q1 p; k- o6 E
添加完之后,为了检查数据是否正常写入,再将数据读出: - /* 读取操作之后的数据 */$ P" j) m5 b) X. X- k, g d$ F
- printf("------------------- Read SD card block data after Write ------------------\r\n");
" \9 i: M( {5 ]0 k - sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);! ], @" A' G' e' g6 L7 o
- if(sdcard_status == 0)2 }) j2 f# H$ e
- { ! c. l, Y2 g8 A: g5 ?$ x
- printf("Read block data ok \r\n" );
8 b; Y* {8 F) B. Z D9 U7 ]5 a- x - for(i = 0; i < 512; i++)
: N4 e7 g4 K r$ w0 m2 ` - {
$ {3 s- K; ~! T6 @& M - printf("0x%02x ", read_buf[i]);' j) D8 H3 h' s" f2 w+ i, ^! q! f
- if((i+1)%16 == 0)
8 K# _- S! }, C% ^" h1 J - {
* L7 ]6 \2 c" q. k# d5 c0 l3 W - printf("\r\n");
, Y M& U1 a, _7 n- p, k - }
% e) Z. Q+ L; _7 B - }. z0 Y9 u3 ^" n% N, N
- }" H1 v* ]' H: _0 g
复制代码 8 J3 `7 R& @6 I+ H ~1 M
将程序编译下载,最终的实验结果如下: 
% p) D0 H# A) X. A

- J' {4 H* w/ y8 i, D$ j4 m: g

6 E1 g2 n# r/ w

/ y+ }8 p* M( ~" D4 y+ i
- G2 l5 L0 B6 r |