1. 准备工作硬件准备- 开发板; E3 X% N! [- V& |5 S
首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):5 f. V$ O& W# g a: O, y( T, g8 o. ?# L
5 X7 y& g( J5 O2 }$ c
- Micro SD卡
8 r2 }8 \! F, X+ W/ O' J小熊派开发板板载 Micro SD 卡槽,最大支持 32 GB,需要提前自行准备一张 Micro SD卡,如图:1 N) c1 C9 h3 A
% M1 k. k) I& E& q1 A1 F& A
- {! z& {9 b+ B$ ]1 v6 x9 ^6 a* p. F软件准备- 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;
- 准备一个串口调试助手,这里我使用的是Serial Port Utility;
# h: n. ?# ?8 ~
: h1 _! }5 x/ Y A
9 E3 ~ K3 I, _* c) H! S
2.生成MDK工程! H7 q* F; A) z+ r7 s
选择芯片型号打开STM32CubeMX,打开MCU选择器:# O+ s* Z) `0 E0 X* T. ~
搜索并选中芯片STM32L431RCT6:* w* t( Z- r6 m* z/ i" Y! @% x1 I
6 `! U0 q* ~: e! P8 T/ f
配置时钟源- 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
- 如果使用默认内部时钟(HSI),这一步可以略过;
- a% K0 G/ T6 ] j- J! |: ?. I5 x2 K5 i, |0 q
这里我都使用外部时钟:
% D- T/ K0 q1 ?- `配置串口小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下: 这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。 接下来开始配置USART1:
" Y9 p) U3 _1 Y4 Q* c配置 SDMMC 接口知识小卡片 —— SDMMC接口
SDMMC接口的全称叫SD/SDIO MMC card host interface,SD/SDIO MMC 卡 主机接口,通俗的来说,就是这个接口支持SD卡,支持SDIO设备,支持MMC卡。 知识小卡片结束啦~
首先查看小熊派开发板的原理图: 然后根据原理图配置 SDMMC 接口:
* ?! S" y" ^! y. N配置时钟树STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可: 0 G0 [# l% V3 s! Y4 X
生成工程设置3 L! x) a8 b3 T
代码生成设置0 ?" M8 q2 U+ [
最后设置生成独立的初始化文件: 7 }5 m+ d+ C' q y. g2 k
生成代码点击GENERATE CODE即可生成MDK-V5工程: 8 S' W* o. @$ S
3. 在MDK中编写、编译、下载用户代码! f! q5 |8 ~& t& G
重定向printf( )函数
K7 A8 W. Y4 x4 J6 `* ~4 u2 O* A1 v读取SD卡信息并打印SD 卡系统(包括主机和 SD 卡)定义了两种操作模式: - 卡识别模式
- 数据传输模式 q6 s# \3 G: A6 k4 a
4 r& j. D+ N, _
在系统复位后,主机处于卡识别模式,寻找总线上可用的 SD卡设备;同时,SD 卡也处于卡& D& J1 G# r( z& U: u& Y' s
识别模式,直到被主机识别到。 使用STM32CubeMX初始化的工程中会自动生成 SDMMC 初始化函数,向 SD 卡发送命令,当 SD 卡接收到命令后, SD 卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。 所以在操作之前,需要先检查 SD 卡是否处于数据传输模式并且处于数据传输状态: 在main函数中首先定义一个变量用于存储 SD 卡状态: - <font color="#000000">int sdcard_status = 0;
- M. X+ k& g: B9 K6 g# R- g - HAL_SD_CardCIDTypeDef sdcard_cid;</font>
复制代码然后在while(1)之前编写如下读取信息代码: - <font color="#000000">/* USER CODE BEGIN 2 */
! L* `! c; o; X" K- x: h - printf("Micro SD Card Test...\r\n");
9 w& c- u5 t: B - </font>
复制代码
r W) C# |& b9 p
/* 检测SD卡是否正常(处于数据传输模式的传输状态) */
- \& v v/ ?6 l# r) D4 l/ vsdcard_status = HAL_SD_GetCardState(&hsd1);
: K! O/ D& [* c |) Bif(sdcard_status == HAL_SD_CARD_TRANSFER)
' Z% t, u; s! V: Y6 Y1 \3 ?" {{
- Z6 b; Q! @' K/ x4 lprintf(“SD card init ok!\r\n\r\n”); - <font color="#000000"><span class="token comment">//打印SD卡基本信息</span>
S$ U& M) X+ M; V9 s8 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>
% A( g5 j# e/ |) `# c - <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>
& j f: U- }- o ?% ~, }$ 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>
6 v. T/ b: l4 W1 ~8 K3 E - <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>6 G, X" A% V% T( L2 I
- <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>
) @! I. x$ M/ J/ g' K, D( w$ Y- W - 7 I% g" {+ {2 p `
- <span class="token comment">//读取并打印SD卡的CID信息</span>& t2 t6 @/ D X9 B& L% _' a
- <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>
. G# Q$ M- J( Y - <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 {1 ~4 F- g- e
}4 j" n% M5 P2 r8 \( B* o" x
else5 |0 s7 B8 p. Z$ D( {( f8 L
{
" ], W! g' X, Vprintf(“SD card init fail!\r\n” ); W" j0 @ {) P' T! t
return 0;* i U% A1 A( h+ ~
}
i% d/ B3 ~0 |4 s$ o/* USER CODE END 2 */ ) ^9 r9 q: ^0 ~6 {( P6 o
编译下载后串口助手输出结果如下: 9 W0 l* ^7 H+ f. w8 x
擦除SD卡块数据为了验证实验的正确性或,先擦除数据: - /* 擦除SD卡块 */
& k% f* k) z8 x) }: J/ T - printf("------------------- Block Erase -------------------------------\r\n");+ C3 N! [; K7 K1 D
- sdcard_status = HAL_SD_Erase(&hsd1, 0, 512);
. P( S$ p! A; {! m - if (sdcard_status == 0)
3 j6 z7 G2 w0 U: j# D2 _7 p3 q* v - {
- V& f* I. O9 O& @ - printf("Erase block ok\r\n");
, K2 g* g) x7 g) E0 Q( D6 h - }, v# B( e* n8 b4 c% ?! x
- else
9 y/ {! [; l4 P& F - {
8 M+ M: V1 Z3 S/ D - printf("Erase block fail\r\n");8 A6 O! Y T2 |7 L% p
- }
% i1 _, Y8 \1 p
复制代码
! I- R0 a0 j% h# [读取SD卡块数据- /* Private user code ---------------------------------------------------------*/% f% ]7 a# |! ~1 A- U! q5 v/ D
- /* USER CODE BEGIN 0 */5 J! T% ~' |. z* Q& j+ b. U- v7 o' P
- uint8_t read_buf[512];
0 B& q* \; \1 Q8 X - /* USER CODE END 0 */3 ^* a7 ~( Q3 w" u+ \9 i, o7 E; n
复制代码
) `" t1 h% |" ]( {1 u然后在之前读取信息的代码之后添加读取数据的代码: - /* 读取未操作之前的数据 */; j1 W- w7 l) a
- printf("------------------- Read SD card block data Test ------------------\r\n");
8 c2 b8 X) g/ S; Y5 n: E' L y - sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);
1 \) P$ p' y" D - if(sdcard_status == 0)
( a( `* C* @0 M r1 N) C( w T - {
) I t9 G" F7 x4 x' o - printf("Read block data ok \r\n" );
$ a/ a; q9 W! n3 E; t% Q% { - for(i = 0; i < 512; i++), c: @* j& ?( C1 J' y! }! A
- {
% q$ T; v% m* y$ h; ~! k - printf("0x%02x ", read_buf[i]);/ y* F6 i/ h7 o, m+ ]: |0 R$ C5 h
- if((i+1)%16 == 0)
y& ]0 n2 }& w3 t% s - {$ s+ F7 b6 Q+ Q& J. q* D
- printf("\r\n");0 v7 X- u$ T( X: k. F
- }
+ V. l- V9 y7 K - }
0 J! ^ o+ g7 |( ~ - }" ?& f, k- R4 X Y9 {$ x0 x
- else
# a& M& |- W: S( ]2 ^9 |( z - { k, m0 L8 v* g' S2 T4 r
- printf("Read block data fail!\r\n " );
- y: L9 G8 ^$ M - }
1 W8 {% m' X& d" W, M1 s8 k! ]
复制代码 ! x+ n3 O) L- V& O" n) C
向SD卡块写入数据同样的,开辟一个全局缓冲区,用于存放即将要写入SD卡的数据: - uint8_t write_buf[512];
# i1 o/ u- r9 `- N
复制代码 4 X7 o5 G) x6 v7 I9 G! k+ Y
然后在之前读取数据的代码之后添加的代码,将缓冲区的数据赋初值: - /* 填充缓冲区数据 *// H$ G+ R2 S# X0 Q$ ^" [- J1 m
- for(i = 0; i < 512; i++)
& D/ V5 ~3 t( u/ O( n" y% s* ^ - {
+ B: S; Z( E! q - write_buf[i] = i % 256;4 ]: M( l9 g" n$ R" ^
- }0 [# s. `5 f: D6 R+ _# O! w0 ^7 R
复制代码
! z$ ?+ a( V" f y1 f2 \2 N9 q7 k然后继续添加代码,将该缓冲区数据写入SD卡: - /* 向SD卡块写入数据 */
9 i' ^9 u% U1 a7 G$ x6 ^ - printf("------------------- Write SD card block data Test ------------------\r\n");
9 K# B- S/ `/ i2 w6 i - sdcard_status = HAL_SD_WriteBlocks(&hsd1,(uint8_t *)write_buf,0,1,0xffff);, P' S9 i7 H. P" A7 L# C: |3 X N
- if(sdcard_status == 0)
% |' `6 ]# z& H& R# D - {
/ w# R( A( h1 G' W3 u& @& v - printf("Write block data ok \r\n" );
% f, F2 W1 {9 n7 M1 l' R/ f$ J2 d - }
, _! @8 h! `0 K" u* V5 L9 P - else
! y2 ^+ |. t( j: A* I# U% Q& I) N5 W - {2 `, V6 V) I& o1 o' o
- printf("Write block data fail!\r\n " );* r1 D+ c8 Q$ a$ X3 C
- }
6 u3 E0 H3 Z+ \9 J' b5 e" Q
复制代码
( a) _+ h. {' n2 ~添加完之后,为了检查数据是否正常写入,再将数据读出: - /* 读取操作之后的数据 */
# u+ ~+ I9 e8 e- ^( x* G - printf("------------------- Read SD card block data after Write ------------------\r\n");- b3 { T" w* j# K: V m# I' s
- sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);
, Y1 Y' m" @3 A* m I, r - if(sdcard_status == 0)% F, t5 K: [; M
- {
9 C3 |4 m, n# ?$ r0 p - printf("Read block data ok \r\n" );
( m$ |3 `, Q: r' M" B) w - for(i = 0; i < 512; i++). e2 s* f) Z$ r! l% G
- {
( S) V" W6 s3 w: K" O, j% X - printf("0x%02x ", read_buf[i]);7 I" P" b/ }/ ?1 a6 M' B* x
- if((i+1)%16 == 0)
* s# c% K* S+ a$ Q- [ - {
# o: h) N2 Z. K4 [8 v - printf("\r\n");: b" u% ?2 m5 r+ L
- }
1 e" W( @, |( X* h) \! z$ u - }
. d F0 ~% z0 J7 G - }
* ^ t! S7 j6 D) l# m9 _0 A
复制代码 5 i) C7 f6 _& i: |5 ^5 h
将程序编译下载,最终的实验结果如下: - |$ T) Y R& e2 Q
, l0 F3 ]9 N& w/ U C& b, |
: _/ A$ [' S$ p7 v* N( w
/ z+ j3 T( n, b9 B" j# B% V# w
8 }: ^. L' [7 X0 t& o, n |