1. 准备工作硬件准备- 开发板 t T4 F. C" \7 m5 N& J. T6 \* H
首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):4 E+ o) R5 f6 N% T5 a
9 P5 u% H$ {" m% R0 k! v

- Micro SD卡
1 g4 X. s, g* E" `, X" z! `# ^& F小熊派开发板板载 Micro SD 卡槽,最大支持 32 GB,需要提前自行准备一张 Micro SD卡,如图:/ J" f+ ]& r: B$ q+ Z
2 V! L* Q) }8 p$ ?

) m, A6 r3 s7 R' Y$ }2 a软件准备- 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;
- 准备一个串口调试助手,这里我使用的是Serial Port Utility;
/ H. [& Y' j* G& V! m0 ~0 |9 `) W' X/ K
) J! d6 S' }8 R/ r8 G
8 u- S* M3 `' A# j) J( C
2.生成MDK工程
2 _7 ^6 ~6 ]" `# ?选择芯片型号打开STM32CubeMX,打开MCU选择器:- {" h4 S* K/ ^# R T
 搜索并选中芯片STM32L431RCT6:4 H, u$ g) e U6 v

3 b2 ?; I) g6 ]8 M配置时钟源- 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
- 如果使用默认内部时钟(HSI),这一步可以略过;+ }0 }1 \' k( P$ S6 v8 S0 S& C
3 A. S& K) i2 e3 r2 {
这里我都使用外部时钟: 
( R2 Q3 E9 }' b' M0 I配置串口小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下: 
这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。 接下来开始配置USART1:  0 e" \! J1 A l; ~8 g
配置 SDMMC 接口知识小卡片 —— SDMMC接口
SDMMC接口的全称叫SD/SDIO MMC card host interface,SD/SDIO MMC 卡 主机接口,通俗的来说,就是这个接口支持SD卡,支持SDIO设备,支持MMC卡。 知识小卡片结束啦~
首先查看小熊派开发板的原理图: 
然后根据原理图配置 SDMMC 接口: 
$ h$ W/ j% J, _: q配置时钟树STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可: 
- A% @* ]% s8 `& f0 K7 y( ^2 P6 b3 A生成工程设置
; j V3 K5 }/ I F0 s3 u0 ]- b+ H代码生成设置; o4 c9 X. X9 d- m' T* J
最后设置生成独立的初始化文件: 
; U& X1 g: y+ J
生成代码点击GENERATE CODE即可生成MDK-V5工程: 
5 q, V Y: ?% F# n 3. 在MDK中编写、编译、下载用户代码
- K( u* x. n) l2 V& Q重定向printf( )函数
2 U% \/ u0 T( }, J. ]读取SD卡信息并打印SD 卡系统(包括主机和 SD 卡)定义了两种操作模式: - 卡识别模式
- 数据传输模式
; ?7 \( E, r3 e/ f5 x9 {
+ [# P1 b( U' }/ h4 C3 |& O. S
在系统复位后,主机处于卡识别模式,寻找总线上可用的 SD卡设备;同时,SD 卡也处于卡/ L, U) p( p4 J' e# L4 f
识别模式,直到被主机识别到。 使用STM32CubeMX初始化的工程中会自动生成 SDMMC 初始化函数,向 SD 卡发送命令,当 SD 卡接收到命令后, SD 卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。 所以在操作之前,需要先检查 SD 卡是否处于数据传输模式并且处于数据传输状态: 在main函数中首先定义一个变量用于存储 SD 卡状态: - <font color="#000000">int sdcard_status = 0;- ^* Y8 X( s7 b
- HAL_SD_CardCIDTypeDef sdcard_cid;</font>
复制代码然后在while(1)之前编写如下读取信息代码: - <font color="#000000">/* USER CODE BEGIN 2 */
: P: c) R( I6 P2 f u - printf("Micro SD Card Test...\r\n");
* X; G# R' ^! c) |) p - </font>
复制代码5 J7 f) R; Y6 C5 |; S- r
/* 检测SD卡是否正常(处于数据传输模式的传输状态) */
! G) L$ ~) G F* usdcard_status = HAL_SD_GetCardState(&hsd1);) q1 D, j( X3 B$ ~) X- p& z
if(sdcard_status == HAL_SD_CARD_TRANSFER)& D" l6 L G/ {5 r4 R
{
1 v0 I) T3 q, _printf(“SD card init ok!\r\n\r\n”); - <font color="#000000"><span class="token comment">//打印SD卡基本信息</span>+ t7 E, K! ^- r# l( X- P
- <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>
% c# Y# s3 }4 A5 i. Q) Z - <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>1 W* S1 S9 Z [6 Y9 b, I; `# r- v
- <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>
% k) q. ]6 D; D# v+ m/ Y - <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>
, A$ Y) G( D: G2 \! {* i& X3 K( `1 d - <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>
( x) I t- |+ t2 v5 ]% s# z
1 N) b: k8 b6 d) _$ [. ^: H; T4 W Q& x- <span class="token comment">//读取并打印SD卡的CID信息</span>) m; }$ w3 N% K* f) {; u3 Y7 X
- <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>, W( G# w$ h) ^$ ^! n
- <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>
复制代码1 X! |; ]) ?' j4 e+ d, y! [# G/ m
}
/ N6 J9 d& ]& q* I# Q& [else
. I' Z P7 Y$ o4 E{% d( I; F/ T. Z. \6 A, O
printf(“SD card init fail!\r\n” );& T I% R# M2 Y% `* b
return 0;; V% l O. E6 ~# \
}
# D( V+ c( l) E/* USER CODE END 2 */
* a# s( e$ X& g3 U! g
编译下载后串口助手输出结果如下: 
1 l3 c4 }/ ]3 k+ p# N! Q2 h. ^7 r
擦除SD卡块数据为了验证实验的正确性或,先擦除数据: - /* 擦除SD卡块 */
' `& K0 l$ G6 e: X- _% v/ G - printf("------------------- Block Erase -------------------------------\r\n");
3 B6 M4 N) r- u0 }) `3 Y/ {# s2 M - sdcard_status = HAL_SD_Erase(&hsd1, 0, 512);
0 v, }6 j8 W1 V$ C6 {! r - if (sdcard_status == 0)% \$ N7 m+ A p; w+ U. n
- {
]7 J% c: l* c* O* N9 [ - printf("Erase block ok\r\n");
& U. Y8 ^6 q j - }
5 \ v# t7 u1 q& w: h% i2 K - else
) j1 f J: T8 \* {. c0 ^" g" J - {1 c0 w; G7 @. _- i6 @
- printf("Erase block fail\r\n");
; a: ], E, r2 s- C7 E2 {- t+ X - }+ a5 g) j: Q( _: I' z2 B+ w/ n
复制代码
/ u* G0 w0 v1 D" P读取SD卡块数据- /* Private user code ---------------------------------------------------------*/
& ^$ M! R' X/ {7 g - /* USER CODE BEGIN 0 */
! I6 Q' \: u/ V& z9 ?- s( k - uint8_t read_buf[512];
4 Y6 u; ~' \+ P) t$ N3 I! D, P - /* USER CODE END 0 */
; t+ Z" `8 y# t7 F/ l, Y& c
复制代码
5 N+ o1 U* N) Q+ G9 W. z然后在之前读取信息的代码之后添加读取数据的代码: - /* 读取未操作之前的数据 */# t$ [7 x1 u/ l) }
- printf("------------------- Read SD card block data Test ------------------\r\n");
6 A* e( E9 h5 w) c9 Y' a& z - sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);
6 y, u1 ` J2 z& ?9 L, [ - if(sdcard_status == 0)& ]2 S: T+ x+ ^ d
- { * l, w# B% l& x6 Y
- printf("Read block data ok \r\n" );/ m: }6 G: W" M
- for(i = 0; i < 512; i++)" q4 K g% V5 Q5 i; l$ q1 J0 y
- {
3 Y2 ^& N9 ?( _$ a - printf("0x%02x ", read_buf[i]);. H9 ~1 g! T, I% L$ Z
- if((i+1)%16 == 0)
1 X) _3 {+ w5 W) Z' j7 w - {
( {3 a6 U0 K, k - printf("\r\n");* ^- Q1 Q' b' H; o' W: S- p
- }9 V4 R8 j6 l4 G8 u: Q- \
- }5 K/ n+ q/ {4 v' T
- }
2 @- I# {+ @* |) O5 W0 Q9 a: A6 m - else/ o6 a2 M+ e1 A* t# W
- {
. i2 K! G1 ^# ]2 y( C2 B; i, N - printf("Read block data fail!\r\n " );+ q4 E6 j' T' O7 Z! ?* N- T# {
- }
6 s& _! b5 X- J# \* {' ~% q& a7 N
复制代码 ( L7 u( t% D$ S4 f4 A
向SD卡块写入数据同样的,开辟一个全局缓冲区,用于存放即将要写入SD卡的数据: - uint8_t write_buf[512];
. ]6 C& O' D1 u$ j, d
复制代码
1 d! F% `# E9 u) S然后在之前读取数据的代码之后添加的代码,将缓冲区的数据赋初值: - /* 填充缓冲区数据 */4 I1 J9 M/ d& n6 Z# B
- for(i = 0; i < 512; i++)( S& T5 D' f! y8 y
- {
7 R& {8 u; t% a - write_buf[i] = i % 256;( l, _2 `+ j' R( u" _
- }
5 e9 k9 f6 N7 K" s% \/ `
复制代码
' d5 |! A4 q) A" @) p然后继续添加代码,将该缓冲区数据写入SD卡: - /* 向SD卡块写入数据 */8 p0 h3 ]( C" [6 F! ^8 v* b
- printf("------------------- Write SD card block data Test ------------------\r\n");. `& a$ R1 Z( T- U; M
- sdcard_status = HAL_SD_WriteBlocks(&hsd1,(uint8_t *)write_buf,0,1,0xffff);
8 l2 u* l' T- m6 @, m c5 A - if(sdcard_status == 0)
1 c5 t$ e/ C" P {# I; e - { 7 N: z1 Y; C9 X+ J. x9 Q
- printf("Write block data ok \r\n" );1 f+ R) c/ a& b5 C
- }* e) w. x2 a$ B; s: ]; D0 c
- else
, u$ d7 W1 c; [5 s - {
5 D. Z0 t, @$ a, ` - printf("Write block data fail!\r\n " );
* Z, y4 ^! r9 Y* M; W4 z - }
- S0 a! r3 |7 A$ R
复制代码 , {. D s& H+ o( i& h
添加完之后,为了检查数据是否正常写入,再将数据读出: - /* 读取操作之后的数据 */. N6 I3 L- I4 h/ T8 @9 T2 y3 I
- printf("------------------- Read SD card block data after Write ------------------\r\n");
' `) D3 [- [, w2 ]1 V8 S - sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);5 i, ?1 L% e2 W* H" ]" z
- if(sdcard_status == 0)4 r4 H0 h2 R9 ]" E, v# @
- {
3 h1 D7 i% M: l9 K- e5 b - printf("Read block data ok \r\n" );
( Z" S! w K4 C) o - for(i = 0; i < 512; i++)! G8 w n5 F$ y
- {
5 u5 G$ K$ t- i. u. w - printf("0x%02x ", read_buf[i]);
* _- @' f- `' [' H: }( F - if((i+1)%16 == 0)
m8 M6 T- F; d8 Y. K6 u' I - {
1 L+ }! d0 Q9 c- C5 d+ ?" Q - printf("\r\n");
; } ^3 }: C+ R5 l X - }
6 R6 m3 q' ?; E8 q - }
7 X. d7 a) e# v - }
2 O8 |' u i% j( T4 K
复制代码 3 A: a" [! u6 t+ X) P/ E" q o
将程序编译下载,最终的实验结果如下: 
5 s a/ c" r+ n& e- k5 w8 t

# k$ u8 @, M6 ^6 L g& T+ p7 ?

# j! v1 V4 O0 P6 R- z$ @- O

" n2 L( L2 A# X8 {* K% e% w: C* R7 Y
4 g4 n. [& b0 ]8 O |