1. 准备工作硬件准备- 开发板9 D/ k3 @0 t3 q# |0 a3 p/ J( h
首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):' m6 V/ K' _8 Z5 Q
. `# b) v8 t4 F5 i) ]0 a: L
- Micro SD卡
3 e! l/ M6 g1 b* l$ Z8 I t U小熊派开发板板载 Micro SD 卡槽,最大支持 32 GB,需要提前自行准备一张 Micro SD卡,如图:$ [, [+ L* _ s/ U/ v
" R! h/ P; M0 e- r& X, [
0 N+ i7 s. {# q9 |2 ?& |软件准备- 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;
- 准备一个串口调试助手,这里我使用的是Serial Port Utility;
$ q7 F$ x! W; ?, U* l* I; q
$ J& X6 ]2 u; P1 K8 d4 i$ t
' o* n. `! {2 S8 O* F) X' s9 d
2.生成MDK工程
, o! h3 r: J0 @' T+ }# r选择芯片型号打开STM32CubeMX,打开MCU选择器:
}1 G6 m# {2 R 搜索并选中芯片STM32L431RCT6:" `: ?- h- w! Z9 I
6 a3 L! {4 r4 x' ^3 q7 X, n
配置时钟源- 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
- 如果使用默认内部时钟(HSI),这一步可以略过;
8 E* V" u# Q& v. o) H
' M$ T+ [, D+ {/ Q& | U" m
这里我都使用外部时钟: 2 A9 S1 X# M6 e& |1 [1 `1 A
配置串口小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下: 这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。 接下来开始配置USART1:
1 n1 G" L9 J% z+ x; [! ?* n! I配置 SDMMC 接口知识小卡片 —— SDMMC接口
SDMMC接口的全称叫SD/SDIO MMC card host interface,SD/SDIO MMC 卡 主机接口,通俗的来说,就是这个接口支持SD卡,支持SDIO设备,支持MMC卡。 知识小卡片结束啦~
首先查看小熊派开发板的原理图: 然后根据原理图配置 SDMMC 接口:
3 a% |3 s6 t, K3 G配置时钟树STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可:
. y% }! C; D0 d) Y2 G, C, b生成工程设置
- Q) I. J) l7 y代码生成设置9 F6 ]/ q8 I$ f; g" u% Q/ p
最后设置生成独立的初始化文件: 2 ?1 X6 e" G2 N$ O, S4 [' X, f
生成代码点击GENERATE CODE即可生成MDK-V5工程: / e/ ?# G8 O, ~0 ?! o
3. 在MDK中编写、编译、下载用户代码4 H, |! X0 {1 @( t" |( P. q+ R% Y
重定向printf( )函数
5 t; Y6 q! L& g% O, K6 a A( h6 K8 x6 K读取SD卡信息并打印SD 卡系统(包括主机和 SD 卡)定义了两种操作模式: - 卡识别模式
- 数据传输模式
6 m- [8 L1 ^; o5 O! y/ L, |( I3 S- V8 N
在系统复位后,主机处于卡识别模式,寻找总线上可用的 SD卡设备;同时,SD 卡也处于卡5 F& j7 n- \) k* s3 T6 ^ F
识别模式,直到被主机识别到。 使用STM32CubeMX初始化的工程中会自动生成 SDMMC 初始化函数,向 SD 卡发送命令,当 SD 卡接收到命令后, SD 卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。 所以在操作之前,需要先检查 SD 卡是否处于数据传输模式并且处于数据传输状态: 在main函数中首先定义一个变量用于存储 SD 卡状态: - <font color="#000000">int sdcard_status = 0;
/ |. z/ b8 p% h7 X* n' z - HAL_SD_CardCIDTypeDef sdcard_cid;</font>
复制代码然后在while(1)之前编写如下读取信息代码: - <font color="#000000">/* USER CODE BEGIN 2 */
) Q3 i* n; m5 U+ W - printf("Micro SD Card Test...\r\n");
6 S" a. U6 r( |: Q$ z' j, z - </font>
复制代码* \# E8 [4 a0 {' U3 N
/* 检测SD卡是否正常(处于数据传输模式的传输状态) */
* P$ |5 n% Z5 L7 D. H- B& isdcard_status = HAL_SD_GetCardState(&hsd1);" _8 v* l b& E! `6 t
if(sdcard_status == HAL_SD_CARD_TRANSFER)
8 B* q8 o7 E0 \6 H{
0 U" i0 S2 f, p9 b0 j' A0 qprintf(“SD card init ok!\r\n\r\n”); - <font color="#000000"><span class="token comment">//打印SD卡基本信息</span>
& D) b9 G% @0 @$ _& v4 L, ~' g - <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>7 m0 A' A, ?, c' l! D
- <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>( [, r8 g L! u7 V( j Z
- <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>8 a: V1 r% h: r, }
- <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>8 W7 j( |% q0 S
- <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>
. z! q$ x, r1 v% N t" V - * U+ ]. c' [, ^0 G
- <span class="token comment">//读取并打印SD卡的CID信息</span>
, @- U" L4 @2 k3 x% 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>
5 z$ [! n2 M g, l) C4 D" O. X - <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>
复制代码
( O4 w3 A4 |* ]9 o
}
t2 t: W, K1 n. e% Qelse M8 k3 G) u2 j' z4 T
{
& ?3 \. e! {& ^# Sprintf(“SD card init fail!\r\n” );9 ^3 @3 ?8 ?; _6 D U
return 0;6 g- l- F0 f8 @. H7 X( \$ p, \" p
}% q' Z. ]! E" D3 U, J
/* USER CODE END 2 */
6 A( B- V/ y9 E E! ]: A/ a
编译下载后串口助手输出结果如下:
T; g* d& g4 l' m4 T) ` 擦除SD卡块数据为了验证实验的正确性或,先擦除数据: - /* 擦除SD卡块 */
5 l5 f* x% u7 R2 a9 m4 B; u - printf("------------------- Block Erase -------------------------------\r\n");) u! m- ?' Y: I" A
- sdcard_status = HAL_SD_Erase(&hsd1, 0, 512);
1 ~) h2 W; |1 V" \# } - if (sdcard_status == 0)' `% D+ u- y. Z6 T* h7 I' p
- {
/ X/ A; M/ O- s( \" A - printf("Erase block ok\r\n");
" Q0 d v: w! K, h3 @/ i) x& Q - }; n2 R& U0 F E& l0 \
- else' q# m, c. d& c4 d, B5 Z
- {
1 i9 y0 C- O- X - printf("Erase block fail\r\n");
/ K' R6 ?4 i) r$ V6 x9 ^9 [' X - }
) M* Y5 { v5 Y# X
复制代码 ( N8 r% \2 v/ E
读取SD卡块数据- /* Private user code ---------------------------------------------------------*/4 l# e+ V7 C& P( ]
- /* USER CODE BEGIN 0 */) K* S! R3 U7 d) [) M3 I( N
- uint8_t read_buf[512];" D% F6 { E4 a5 ~* d
- /* USER CODE END 0 */* i- ]+ u/ U! D* g t2 m
复制代码 : P# c! b2 `, N' J1 c! L; @; b
然后在之前读取信息的代码之后添加读取数据的代码: - /* 读取未操作之前的数据 */% ]) I" @# O+ x4 T9 H' K% b T% A
- printf("------------------- Read SD card block data Test ------------------\r\n");% y7 E4 ~# ?" e& e
- sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);7 z/ r3 N; G8 V
- if(sdcard_status == 0)/ i6 o: B% U% z
- {
/ _; B v( Y+ R+ H - printf("Read block data ok \r\n" );
* W' [6 l) j! r/ y2 B! E - for(i = 0; i < 512; i++)
! j- K. @+ k& Y# ~ - {
1 {$ D7 \3 J% x; D# u* K" d2 N4 J - printf("0x%02x ", read_buf[i]);
+ v( ^7 w0 o# N( p0 p: X - if((i+1)%16 == 0)+ y; g6 ?/ {1 ^
- {) F. e8 z- @( O! S: F# W* V1 b
- printf("\r\n");
# a0 e" {' V3 M - }! O2 N( E# x. H4 n% _
- }
/ G" _+ ~% d! x9 n' z - }, s3 M: {0 {- ]0 E5 r: r: @
- else2 s& E2 G- I$ a. y+ W8 q
- {. w" b1 l/ w* ]0 ]
- printf("Read block data fail!\r\n " );8 c! Y7 H0 L5 E' _- a% T3 F
- }
$ m2 h+ E1 q6 F6 [( F l% G
复制代码
a) |8 O! N6 @向SD卡块写入数据同样的,开辟一个全局缓冲区,用于存放即将要写入SD卡的数据: - uint8_t write_buf[512];
, I: R% `1 G3 v2 H2 G
复制代码
( S$ s+ T1 O! \" | H! j# {然后在之前读取数据的代码之后添加的代码,将缓冲区的数据赋初值: - /* 填充缓冲区数据 */
/ s; J9 S/ ~) h. } - for(i = 0; i < 512; i++)
7 a$ }: ~( p) n& m& q" \$ ?& ] - {% g0 b" P( D" N; J6 v
- write_buf[i] = i % 256;
' ^3 ?7 y9 V$ l$ l0 j& j - }
0 O+ F, o1 F; Z& c/ k6 R& B4 i
复制代码 . F2 u( K, ]. j; C
然后继续添加代码,将该缓冲区数据写入SD卡: - /* 向SD卡块写入数据 */
0 ~, u! q O& p0 R$ I - printf("------------------- Write SD card block data Test ------------------\r\n");
7 W3 E5 j" v; u - sdcard_status = HAL_SD_WriteBlocks(&hsd1,(uint8_t *)write_buf,0,1,0xffff);: Q1 O: `' e! `; O& N7 i8 _+ ]1 g
- if(sdcard_status == 0). ]+ l/ u: Z! ]- Q, W) q+ Z
- {
/ U+ Z9 \$ k/ K4 `, r - printf("Write block data ok \r\n" );
% F6 o( a+ O2 K+ h9 P" B" A - }9 t( l3 {# ^+ m* w# ?# \# Z: l
- else
i6 {$ Z7 w& e, c4 j - {; X: @9 U/ ?7 ?8 s \
- printf("Write block data fail!\r\n " );
6 y, b$ }' S" |/ L6 ?- h7 z. U4 o - }
9 u( V5 M7 b$ I: o; i% D& p
复制代码
* W$ e4 L( L* V4 t- C \3 C添加完之后,为了检查数据是否正常写入,再将数据读出: - /* 读取操作之后的数据 */9 h6 W# F i% o6 J& S4 b
- printf("------------------- Read SD card block data after Write ------------------\r\n");+ z0 G& F. r4 [6 [; h
- sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);' P; d/ f3 r" y) [5 y1 T
- if(sdcard_status == 0)
. L3 N& y0 n' C e# W* f1 k - {
& k( z4 Y) G8 \) }0 P% A - printf("Read block data ok \r\n" );
. J# v- ?& |9 V1 s. O1 z; \+ F8 i - for(i = 0; i < 512; i++)
) D( ^- g k& h) P ~9 j& | - {; y% |: j5 n- f7 M8 ~( k8 V; S) q, ~
- printf("0x%02x ", read_buf[i]);
1 ~/ G3 f; p$ b0 j9 M) O, y - if((i+1)%16 == 0)
5 u, C+ Y& w+ q - {- R9 G9 g* Q1 o/ C5 ^* v( |
- printf("\r\n");+ F- c- e: k9 R% i
- }
& C% R( Q1 z& H. |% C1 N, r - }/ ?% C5 p6 C* J
- }
6 W' i" T6 Z; _$ |, f
复制代码
5 y p7 `5 Z' O: O* u4 K将程序编译下载,最终的实验结果如下: 1 x: A* J3 x, i
8 H% e+ p. u5 e& ?
4 C( r- Q/ o7 W9 H. Y& U3 r* y
" D% m; I# a6 j$ p W# a2 C" x2 D# \7 o( A2 S7 H! [
|