1. 准备工作硬件准备- 开发板3 U. s$ f* O/ u5 ?) J
首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):' P% b2 B- ~$ R. \) E4 a
" L/ f' _( V! H @" O4 \

- Micro SD卡4 x$ ]" b- h1 p8 x: F" C8 U$ t. p
小熊派开发板板载 Micro SD 卡槽,最大支持 32 GB,需要提前自行准备一张 Micro SD卡,如图:
& b6 @ M3 \3 K4 O- B9 D
2 I% R) y% p0 r! e+ F

7 n2 M9 t% q* \1 v1 {! b软件准备- 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;
- 准备一个串口调试助手,这里我使用的是Serial Port Utility;
2 X- B3 v: i, }+ o8 m
7 d$ m3 I z6 G
+ _9 s2 l$ b& _0 k: b) k$ F- y 2.生成MDK工程" O' e, M7 J# X! `( p$ s# F
选择芯片型号打开STM32CubeMX,打开MCU选择器:# ?" w L" Y3 v$ P" k
 搜索并选中芯片STM32L431RCT6:
0 o& H- h" L) ?9 [! X ; w6 d1 _; |9 @* Q$ k, ~
配置时钟源- 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
- 如果使用默认内部时钟(HSI),这一步可以略过;8 O+ l1 T. L. C8 w7 J; B7 K
& |0 }- @ C0 u- g7 v8 K+ n ^
这里我都使用外部时钟:  & y2 p; X. D6 C3 c. |
配置串口小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下: 
这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。 接下来开始配置USART1: 
8 q n$ M5 g+ w! u. M4 o8 S# I7 U. ^配置 SDMMC 接口知识小卡片 —— SDMMC接口
SDMMC接口的全称叫SD/SDIO MMC card host interface,SD/SDIO MMC 卡 主机接口,通俗的来说,就是这个接口支持SD卡,支持SDIO设备,支持MMC卡。 知识小卡片结束啦~
首先查看小熊派开发板的原理图: 
然后根据原理图配置 SDMMC 接口: 
! [8 a5 y5 b0 @配置时钟树STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可:  ) m3 |' Z3 h, V `* h- d1 n8 @5 y9 I
生成工程设置
3 F! w0 @& X X/ |. [代码生成设置7 \) t8 j$ A, r& L6 D4 ?! E5 p
最后设置生成独立的初始化文件: 
( n8 q6 h3 x9 d$ w& C8 F 生成代码点击GENERATE CODE即可生成MDK-V5工程: 
' x2 m1 z- C% l. p 3. 在MDK中编写、编译、下载用户代码
$ b! x/ G& i) \- B3 V7 h5 G重定向printf( )函数
1 J2 w4 @: f1 u读取SD卡信息并打印SD 卡系统(包括主机和 SD 卡)定义了两种操作模式: - 卡识别模式
- 数据传输模式
# E" }5 P: H1 @# U, l' q
$ o F, T% u' j
在系统复位后,主机处于卡识别模式,寻找总线上可用的 SD卡设备;同时,SD 卡也处于卡
3 ~$ g! W- k6 S识别模式,直到被主机识别到。 使用STM32CubeMX初始化的工程中会自动生成 SDMMC 初始化函数,向 SD 卡发送命令,当 SD 卡接收到命令后, SD 卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。 所以在操作之前,需要先检查 SD 卡是否处于数据传输模式并且处于数据传输状态: 在main函数中首先定义一个变量用于存储 SD 卡状态: - <font color="#000000">int sdcard_status = 0;+ l2 Z$ m1 j# N. E
- HAL_SD_CardCIDTypeDef sdcard_cid;</font>
复制代码然后在while(1)之前编写如下读取信息代码: - <font color="#000000">/* USER CODE BEGIN 2 */1 z* j0 {0 }0 z! q
- printf("Micro SD Card Test...\r\n");1 Y+ t, M; n: R9 L) R2 V8 G& g
- </font>
复制代码
1 T6 k/ W) H; [: I
/* 检测SD卡是否正常(处于数据传输模式的传输状态) */; B7 n/ n# t1 @3 B3 m: u8 g
sdcard_status = HAL_SD_GetCardState(&hsd1);' S, t5 u* X5 H6 Q0 b1 p2 V
if(sdcard_status == HAL_SD_CARD_TRANSFER)3 ]! r( G6 R+ J! E% Y- v/ x3 v& x# d
{3 R# l4 O- ^% {
printf(“SD card init ok!\r\n\r\n”); - <font color="#000000"><span class="token comment">//打印SD卡基本信息</span>
& h: `9 ?; Y4 Q9 N* E1 S - <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>1 v' v& A! a% M9 }& @
- <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>
# m0 f8 ?3 S! ^0 |; f; @6 W. X% f - <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>
9 ?4 L* K; G) f4 o; I - <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>
/ u5 ^( z$ T8 Z# C) Q* h - <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>
6 @: o4 s+ T9 Z
6 I* d3 k4 \3 Q$ q: \' \; |- <span class="token comment">//读取并打印SD卡的CID信息</span>
. z9 \# m( I# y/ D) H9 Y9 v6 m - <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>
9 @- B* p$ O7 E e# ^ - <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>
复制代码
+ P \8 z1 @% m! L. ~: M# G
}
0 `! O, p' R, W5 m! O+ Zelse* @% V& `* k: c6 W1 r8 m! i) f
{' A( Q5 N D' P
printf(“SD card init fail!\r\n” );
( {4 {7 J- R/ b% I6 u2 N7 [return 0;4 \* H( {) k; }* q+ H; g
}/ _% F5 j% ^ B
/* USER CODE END 2 */ - J) M3 h2 W4 W ?" `5 x
编译下载后串口助手输出结果如下: 
, i) n) {7 a6 f1 q
擦除SD卡块数据为了验证实验的正确性或,先擦除数据: - /* 擦除SD卡块 */
R6 L4 |. I% Q, c3 [1 P; Z3 l( T - printf("------------------- Block Erase -------------------------------\r\n");
7 t$ d9 u `7 ?) H, ?% b" c - sdcard_status = HAL_SD_Erase(&hsd1, 0, 512);0 I! E4 {: t2 [, g9 p3 U, P/ f$ X+ S% d
- if (sdcard_status == 0)
7 t0 R) ~) _3 g+ _, ~ - {
1 w# ~# B' G# b/ g d" j( d - printf("Erase block ok\r\n");6 |+ m5 O' ~# F! S% M. {, @
- }
w- P) _7 k; h9 F' n2 h! h - else' r: m# v8 `/ L. \
- {
9 ]0 V. G2 d. c( n- O - printf("Erase block fail\r\n");
4 P6 ?, L5 U$ R4 q/ x# I - }
& L$ W8 l1 V; H5 z4 }
复制代码 ' U' f( c3 Y7 {' M3 o2 _9 n
读取SD卡块数据- /* Private user code ---------------------------------------------------------*/
$ F% y! m e4 K" n6 W' o3 y5 k - /* USER CODE BEGIN 0 */2 m) ]9 n' j5 d$ J7 b" ~
- uint8_t read_buf[512];
* g. x9 R+ N6 m# ]' | - /* USER CODE END 0 */8 G+ l3 ~5 x: q+ }* n2 b
复制代码 7 M$ ~) {- P& w3 q! `
然后在之前读取信息的代码之后添加读取数据的代码: - /* 读取未操作之前的数据 */! N! P* G2 L+ ~! K8 @: \7 C
- printf("------------------- Read SD card block data Test ------------------\r\n");
0 _% I& n2 I7 ~& }" C9 R) o - sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);3 @, I) I: e! w, f) R S
- if(sdcard_status == 0)! b, `) c: Y% M6 B3 k- x7 {* B
- { ; D; B# y% M. d" h
- printf("Read block data ok \r\n" );
/ c9 Y% \4 Q! ?- z9 P# P3 D( s$ W - for(i = 0; i < 512; i++)5 t1 X# {+ [: L3 W+ D
- {3 {) S+ i4 [7 x' A
- printf("0x%02x ", read_buf[i]);" p' Z$ r( `* V6 u3 F! @8 j' ^
- if((i+1)%16 == 0) ~" |) I: O$ m3 p3 }% ^
- {
8 z5 J6 ?5 G1 C$ M; `: B2 _ - printf("\r\n");
! s, K7 S4 z G. B2 M, q2 Q1 V - }. H4 D2 ~+ B8 U: {
- }* I. b7 ]! F! h% w' d
- }
0 b ?7 u& F, e y - else
) ?: s* P5 u, G: {0 ~" v! ]/ C# ` - {
# C$ ~4 w% p' j: E1 Z - printf("Read block data fail!\r\n " ); I% _/ |) d0 p, x' N1 F3 f3 \
- }# g% v, g/ K6 l O
复制代码
' `/ p; B4 Y/ Q g d向SD卡块写入数据同样的,开辟一个全局缓冲区,用于存放即将要写入SD卡的数据: - uint8_t write_buf[512];4 { b5 @( W* ~4 p2 l: Q
复制代码 , ?8 G( I' y# w }, c/ | K
然后在之前读取数据的代码之后添加的代码,将缓冲区的数据赋初值: - /* 填充缓冲区数据 */8 R$ t6 P6 A2 h+ {* Q
- for(i = 0; i < 512; i++)
. i" b9 W! r N* T - {- N' x. E2 N8 D2 r
- write_buf[i] = i % 256;
. x2 |; J( X( c @' V+ q - }# X& h! |; a. U3 e! G6 L
复制代码
. L8 Y0 N# L. P然后继续添加代码,将该缓冲区数据写入SD卡: - /* 向SD卡块写入数据 */
: V# R: w$ u/ H3 l8 ^ - printf("------------------- Write SD card block data Test ------------------\r\n");9 i. d! B) _' _, P. U( k( r' L
- sdcard_status = HAL_SD_WriteBlocks(&hsd1,(uint8_t *)write_buf,0,1,0xffff);
+ U3 H& Z. Q2 I6 Q. B( m - if(sdcard_status == 0)& C8 D9 Z) i( U
- { # x6 {6 d3 H% m% F
- printf("Write block data ok \r\n" );& j% Q+ \" L4 p4 L# w$ d
- }
$ b* L& O* h; l) r" p- T - else
, a' o- G- `& H! f - {
8 R) l s8 [* Z: J( k/ z$ Q - printf("Write block data fail!\r\n " );
' C4 o; n3 V9 @8 C! ?8 C: |( o - }
# Q: t1 V" u' ~7 f b# L. X
复制代码 5 N! f$ R6 u2 z# p& j
添加完之后,为了检查数据是否正常写入,再将数据读出: - /* 读取操作之后的数据 */3 g. R& q2 R8 [# X8 b# A" O+ `* U: w
- printf("------------------- Read SD card block data after Write ------------------\r\n");
+ a" e6 Y7 a! e: ^4 Z: _0 [8 {- ?# x0 ^ - sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);1 _ Z7 ~/ P) H2 ]" Y6 ~* {/ \
- if(sdcard_status == 0)! A( g+ X# V* L4 \
- { * d* d6 n+ z0 p6 n' a6 O) L6 P9 q) i3 _
- printf("Read block data ok \r\n" );
; @' q, x; S6 K- N' C# K: A - for(i = 0; i < 512; i++)+ l/ S% _$ `2 i8 ?7 f C# O
- {
8 u. a9 ? a7 W. x8 s/ c8 @+ r2 Y - printf("0x%02x ", read_buf[i]);9 d4 _( [% [; U3 a, q
- if((i+1)%16 == 0)) z& D' I: h& Y0 [2 p
- {
1 ^, l0 f4 m; k1 q6 Z3 V3 x - printf("\r\n");
1 Z2 H) p Z" W3 [" Y - }
, `8 G3 y3 p2 A; { ^2 W' _& Q7 V - }
! G0 O% L3 u5 p: K - }* ]0 l l. `0 L
复制代码
$ M/ r$ f6 H3 Q; y% h, O: H5 v/ L7 O将程序编译下载,最终的实验结果如下: 
" B6 p( j3 Y& A0 k4 M

3 W) P4 h. c- {, t5 x* l' t

, N8 v" J# d6 i# H( Q6 p' ?

2 {% y* J) K) L6 Q2 b+ z& O3 ?; o
|