|
1.SD卡介绍 # G$ ]4 H3 j! i r
1.1 SD卡简介 很多单片机系统都需要大容量存储设备,以存储数据(常用的有U盘、FLASH芯片、SD卡等),比较而言SD卡是单片机大容量外部存储的首选,只需要少数几个IO口即可外扩一个容量从几十M到几十G的,且有多种体积尺寸可选(标准SD卡、TF卡等)的外部存储器。 : M0 ^3 B" {! w- W1 J2 T! b
SD卡(Secure Digital Memory Card)即:安全数码卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。
# n0 L( X. E5 {: ^2 [$ l. l5 q! w) F" I8 a7 s
; ]; g& V1 V6 i; t. e* s
SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示: " F7 c$ f; x W1 {
. O# Q; f( J6 N( z
1.2 SD卡的物理结构及内部框图 SD卡的物理结构一般包括以下5个部分: 2 H9 {8 ^1 c3 {! M
存储单元:是存储数据部件; 存储单元接口:存储单元通过存储单元接口与卡控制单元进行数据传输; 电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位; 卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器; 7 \" G: I: x4 u2 N' ?' V
接口驱动器:控制SD卡引脚的输入输出。
, t/ B3 ]9 o0 \/ e( H% r2 d# m* _; c- p5 I8 O2 F
6 Z8 @6 c- [7 R9 L8 b e7 ~/ v- a* v! Z
* x7 q: ^7 o ^3 F1 y
0 X: i% L5 ^6 O2 l. p
SDIO由SDIO适配器和APB2接口两部分组成:
" N% T; w W$ j* ~ 名称 | 功能 | SDIO适配器 | 提供特定于MMC/SD/SD I/O卡的所有功能,如时钟生成单元、命令和数据传输 | APB2接口 | 访问SDIO适配器寄存器,并且生成中断和DMA请求信号 |
% ?& J- ?% U/ u" O& N2 R4 u( `1 a
下图是SDIO功能框图及SDIO适配器框图: {/ [/ ^* T) G- d0 d7 r, n- [
; W+ y6 ~, ~3 M% P- R/ x5 r" j
. \% G. J- s& b3 S, h4 P
1.3 SD卡命令 SD卡命令由主机发出,命令格式固定为48位,通过CMD线连续传输,数据线不参与。SD命令结构如下图示:由6个字节组成,字节1的最高2位固定为01、低6位为命令号(比如CMD16);字节2 ~ 5为命令参数(有的命令没有参数);字节6的高7位为CRC、最低位恒定为1 SD命令组成的详细说明如下: ' B" ]; A) }, j& W! [/ ?# {2 O
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。 传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。 命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分 命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。 地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。
A8 P: n8 M- E( R/ M( ^ CCRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。
" }8 @- V- f& l6 C* e3 B
& M! g$ N# O/ O2 n8 ` 1.4 SD卡响应2 n! R+ {* A! T/ Q
SD卡命令的响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下: ' Y* T2 L- H% W5 X' P
SDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。 与命令一样,SD卡的响应也是通过CMD线连续传输的。 / f- D, k% a$ g; S8 I
根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。 , Q* i. ?0 v$ x9 U. u! R/ B& u
: P K* k5 {0 X. a" {
" u1 { T8 I- O% N+ c* E
2 m1 b! K/ a! q: R$ R. S, V! k ?- V; g. z
SD的读写操作是以块为操作对象。先发送命令开始传输,然后传输数据块,传输完数据块紧接着传输CRC检验值。最好发送停止命令停止数据传输
2 {# t, n, j/ H
6 f* C3 a1 b$ D$ a
1.5 SD卡的操作模式及切换 SD卡有多个版本,STM32控制器目前最高支持《Physical Layer Simplified Specification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。
( |5 p6 [" J$ c1 c
SD卡系统定义了两种操作模式:卡识别模式和数据传输模式
6 f* ~8 c6 ]% K. d
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。
8 q0 N) v3 s- a6 f
" d+ ?( R& x5 V: \1 Z
5 Y& C& k6 p* b) z( i+ f# a
8 \5 x( g, W K+ x' B8 |
2.硬件设计  D1指示灯用来提示系统运行状态,K_UP写入数据,K_DOWN读取数据,TFTLCD用来显示SD卡的容量、类型等信息,串口1用来打印调试信息 3 b. Z' R: l1 w) U/ d1 J
D1指示灯 K_UP/K_DOWN/K_LEFT/K_RIGHT按键
: w6 q$ _% B/ N4 d$ Z5 y USART1串口 TFTLCD 2 l6 A' H+ l% s2 g
TF卡 2 g$ ?0 B M% L# M
9 V* M# b" v3 I# {# o& ~
$ }" t* _$ I, Z4 a) g% l! R: B
从以上电路图可以看出,SD卡支持SPI和SDIO模式,两种模式可以通过端子进行切换,P4端子与SD卡连接,SD端子与STM32F1的SDIO接口连接,IO端子与STM32F1的SPI2接口连接,本例程使用SDIO模式(将P4端子与SD端子短接) + s; ~, ]4 y+ O( E8 q
3.软件设计 3.1 STM32CubeMX设置 ➡️ RCC设置外接HSE,时钟设置为72M ➡️ PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平 ➡️ PA0设置为GPIO输入模式、下拉模式;PE2/PE3/PE4设置为GPIO输入模式、上拉模式 ➡️ USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位 ➡️ 激活FSMC ➡️ 激活SDIO,选择4线SD模式,分频因子设为4,使能流控,其余默认设置
+ m- W$ s4 J% R1 R; d& ]
i, \$ m7 T6 V1 P
➡️ 激活CRC功能,以避免后续读写SD卡报CRC校验错误
: a" ^, k. J" B
{* Q' _4 b& t
➡️输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
7 Y" B5 V* U( q/ W; E; i$ O
3.2 MDK-ARM软件编程 ➡️ 创建按键驱动文件key.c和key.h,参考按键输入例程 ➡️ 创建LCD驱动文件tftlcd.c 和tftlcd.h,参考TFTLCD显示例程 ➡️ 在sdio.c文件下可以看到sdio初始化函数,同时在该文件下添加显示SD卡信息函数
; g) t9 d0 h1 r) f R W( k/ o C
- void MX_SDIO_SD_Init(void){+ a0 _& I" _ _ \) G7 |; |* |& C3 {
- hsd.Instance = SDIO;- v) f$ a& \0 [1 G) k
- hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;//上升沿, s, R6 m( x, V& F
- hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;//比使用bypass,直接用HCLK分配得到SDIO_CK
) G) G! D) U4 {# l- P, D6 k - hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;//空闲时不关闭时钟电源& k0 l; }- z |9 Q
- hsd.Init.BusWide = SDIO_BUS_WIDE_1B;//1位数据线; \, d. w+ F1 h3 o. |8 ]; \
- hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_ENABLE;//开启硬件流控
( r6 }2 v0 P# h W- Y - hsd.Init.ClockDiv = 4;//4分频
, V/ t3 _. d( J4 z. ? - if (HAL_SD_Init(&hsd) != HAL_OK){
& w( @! T; U" j3 H( ?2 } U - Error_Handler();! p& o0 ]% P* Q( l2 N( F# [/ F, a1 I
- }
5 a/ E& ^2 f6 I, J/ w - if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK){$ x5 _4 Z5 H; X/ L: E3 Y. [5 x$ L
- Error_Handler();//初始化完成后使能宽总线(4位)模式2 d* a# H, A8 y; b- z! z9 p
- }
% E7 e1 O% K; t5 w - }
) X" \; p/ }! v. U$ ~, h - //显示SD卡信息函数
1 F( N# J! B) S" s - void Show_SDMMC_Info(void){$ J& P7 o4 ^3 Y! K2 o9 H. t# z
- HAL_SD_CardCIDTypeDef SDCard_CID;
' k$ D! ]7 }& s1 d ] - HAL_SD_CardInfoTypeDef SDCard_INFO;! r8 ^! i4 ~& P- A# g8 g5 K& K
- uint64_t CardCap; //SD卡容量 F: e6 Q; Z; D3 i% m
- HAL_SD_GetCardCID(&hsd,&SDCard_CID); //获取CID
, G/ d* _) N. H0 Q( i( `0 Y - HAL_SD_GetCardInfo(&hsd,&SDCard_INFO);//获取SD卡信息
- r. P% N( y/ f1 [5 {; ~8 A - FRONT_COLOR=BROWN;
% T: h* p' L& e( t/ A - switch(SDCard_INFO.CardType) //SD卡类型6 v9 M1 M# Q/ l2 a
- {
: O5 w/ P. l+ P; j% Y- \9 C - case CARD_SDSC:% @0 C6 w* f. o' {5 c1 r4 i
- if(SDCard_INFO.CardVersion == CARD_V1_X){
# t, J" r# @/ Y - LCD_ShowString(120,190,50,16,16,(uint8_t *)"SDSC V1");
! I3 ~/ O7 s6 V, p$ l' L7 g$ O+ ]- J* i - printf("Card Type: SDSC V1\r\n");
( e& _) S+ U7 y+ [9 n# ` - }
5 d1 O: b" V9 E5 H8 F1 V# y7 G: y% f - else if(SDCard_INFO.CardVersion == CARD_V2_X){4 M# B- l9 d+ v, [1 M1 D
- LCD_ShowString(120,190,50,16,16,(uint8_t *)"SDSC V2");7 |% f) ]; R! |9 X% W& |
- printf("Card Type: SDSC V2\r\n");. J6 J3 L3 G9 N& N8 } g; r
- }
w! t4 I' D; c B& ? - break;2 L" L/ ]' q3 |$ U# n9 u9 s' d; s4 H2 s
- case CARD_SDHC_SDXC:
( L- E+ k2 m9 |& `; u: w: ~ - LCD_ShowString(120,190,50,16,16,(uint8_t *)"SDHC");/ {1 {% l$ \$ q, q% T. a" ` q! d
- printf("Card Type: SDHC\r\n");
7 y. _2 I) @6 d, A t5 u - break;+ ^: d8 d4 w. |: c8 T
- }
/ i3 o b- B, v' G/ \* e - ( Q4 L! S% |3 L6 G @0 U# @5 W t
- CardCap = (uint64_t)(SDCard_INFO.LogBlockNbr)*(uint64_t)(SDCard_INFO.LogBlockSize)/1024/1024; //计算SD卡容量2 W6 d; T' ]- k0 M* T/ H- P
- printf("Card ManufacturerID:%d\r\n",SDCard_CID.ManufacturerID);//制造商ID
" {% @: S% ^) c" h0 t% P - printf("Card RCA:%d\r\n",SDCard_INFO.RelCardAdd); //卡相对地址 , y# b% { {, Y9 J. p9 z
- printf("LogBlockNbr:%d\r\n",SDCard_INFO.LogBlockNbr); //逻辑块数量
" k7 r9 |; ]) w) Z - printf("LogBlockSize:%d\r\n",SDCard_INFO.LogBlockSize);//逻辑块大小- G: a4 a. B7 W7 H7 n
- printf("Card Capacity:%d MB\r\n",(uint32_t)CardCap); //显示容量
) S) g3 s% P$ `) W! H - printf("Card BlockSize:%d\r\n",SDCard_INFO.BlockSize);//块大小3 S+ D* q# t9 ]7 f4 R
- LCD_ShowNum(120,210,CardCap,4,16);
( {0 b' a3 w2 R$ R" t0 Q Q - LCD_ShowString(160,210,50,16,16,(uint8_t *)"MB");
5 X0 B* ]4 J) C. w: h& [ - }
复制代码 - K: t; J% d6 V8 q
➡️ 在main.c文件下编写SD卡读写测试代码
; |' k/ f6 q' [* f k
- uint8_t Buffer_Tx[512],Buffer_Rx[512] = {0};' f3 n+ x. K' [
- uint32_t i;6 x$ ]+ M# P4 V- v5 _% c, ^
- int main(void){
& U6 v+ Y9 }/ d+ R - uint8_t key;
; i0 z9 V* R5 E& g' C( \) C, ^ - HAL_Init();7 |- G5 d0 i) z) ~
- SystemClock_Config();
5 T# \& `0 `9 ]& v - MX_GPIO_Init();; U6 r* z! D: e$ j; K
- MX_FSMC_Init();
( W5 {( @4 w7 d; ]5 w. A - MX_SDIO_SD_Init();
$ @. W3 d l! D. d - MX_USART1_UART_Init();
/ B% G2 C+ I, O+ y: j - MX_CRC_Init();" Z$ c4 v, U1 a
- /* USER CODE BEGIN 2 */
3 d* b6 K; j- R: _ - TFTLCD_Init(); + v! _' E3 O" H/ N7 e& f3 C
- FRONT_COLOR=BROWN;
8 ~% [. g/ y8 {/ n& y7 N J; ` - LCD_DrawRectangle(25,25,215,135);
5 o# G& M3 |& q - FRONT_COLOR=RED;
; n6 }' N$ Z6 t) H8 o$ v - LCD_ShowString(30,30,200,16,16,(uint8_t *)"ANDYXI STM32"); X+ w! v; K2 n: l
- LCD_ShowString(30,50,200,16,16,(uint8_t *)"STM32CubeMX");
: q* k/ i/ Q" R! e2 f6 X3 B - LCD_ShowString(30,70,200,16,16,(uint8_t *)"SDIO TEST");9 X* w6 J, }2 _6 N; H6 x) o$ H
- FRONT_COLOR=BLACK;
( p* ~4 U1 H! R( B1 \6 K; @" l - LCD_ShowString(30,90,200,16,16, (uint8_t *)"K_U:ReadSD K_D:WriteSD");
& h& i- _& f% Z( e. Y% k- n8 p - LCD_ShowString(30,110,200,16,16,(uint8_t *)"K_R:EaseSD K_L:None");
/ ^7 R4 _' o- P( ?6 W) r - FRONT_COLOR=BLUE;) ?' j0 Q8 Q( u: _' B4 d
- LCD_ShowString(30,170,200,16,16,(uint8_t *)"SD Card Information");4 c5 V# W1 u- P( V8 b
- LCD_ShowString(30,190,80,16,16,(uint8_t *)"Card Type: "); ~/ ]* @2 q+ {# ~- l M! K
- LCD_ShowString(30,210,80,16,16,(uint8_t *)"Card Capa:"); 1 ?% |! p, {) Q. ^/ x( I- G
- Show_SDMMC_Info();3 x V: F6 ]; U; C: A
- memset(Buffer_Tx,0x15,sizeof(Buffer_Tx)); ' M+ c+ a1 F( d/ Q) J
- /* USER CODE END 2 */
( E4 J- N" n7 o: Y& M6 r2 Y - while (1){
' Y/ @% ? C7 J - key = KEY_Scan(0);
i+ [' v1 i& z2 n - switch(key)* q* x' h8 G% _" G! _
- {
& U' o4 T/ r9 T8 d6 l - case KEY_RIGHT_PRES:1 K+ C S9 R& z" K$ C
- if(HAL_SD_Erase(&hsd,0,1) == HAL_OK){
5 R# s8 t$ u" d Q; W+ q: G* C - while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);% W; g( @: d1 M! J5 v% Z: Q7 g
- printf("\r\nErase Block Success!\r\n");8 V: R' W- w, ]; J' i0 q, [; z
- LCD_ShowString(30,250,200,16,16,(uint8_t *)"Erase Block Success!");1 M3 g' d. ^( X" p4 T4 K4 C7 E" R
- }
3 w% @+ I) |& ~. G7 F9 X, y) V - else{5 Q9 _6 d( A' o3 [: C7 s$ a4 b
- printf("\r\nErase Block Failed!\r\n"); + R. V3 q5 G; K
- LCD_ShowString(30,250,200,16,16,(uint8_t *)"Erase Block Failed!");
6 J1 a5 K7 q3 n2 @" @0 W0 F. K - }# b) U d( ~4 c% s5 ^( X- U
- break;
) d+ O$ X* X6 {* @" [ - case KEY_UP_PRES:, P& f) _4 {# @" S. q
- if(HAL_SD_ReadBlocks(&hsd,Buffer_Rx,0,1,0xffffffff) == HAL_OK){" D1 r* a+ R$ G1 I( ^
- while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
D. g3 d. R/ o0 C0 i2 } - printf("\r\nRead Block Success!\r\n");: G5 p* Q, N! L5 Y( ^( \
- for(i=0;i<sizeof(Buffer_Rx);i++)
/ C9 \" K3 H V& B: v7 X+ w T9 e - printf("%02x ",Buffer_Rx[i]);
( s! \2 p9 _# o K, {) W - printf("\r\n");9 S- U) e4 I! S4 Y7 L1 N1 L e
- LCD_ShowString(30,250,200,16,16,(uint8_t *)"Read Block Success!");
1 P! R3 ~6 q2 H - }else{
& N }# m- e) i( v, M - printf("\r\nRead Block Failed!\r\n");
# P$ @1 D' Z9 D9 A1 u+ y3 Y- S - LCD_ShowString(30,250,200,16,16,(uint8_t *)"Read Block Success!"); ' D3 W: h. h& p3 T
- }1 j; M: l3 ^1 d7 _2 {+ y! S- [9 m+ @4 o! a
- break;
% n6 J% D7 _# ^) G( b: o - case KEY_DOWN_PRES:+ ?9 A G3 y" c( |/ P
- if(HAL_SD_WriteBlocks(&hsd,Buffer_Tx,0,1,0xffffffff) == HAL_OK){3 A5 [9 |5 ~, ?
- while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);8 ]5 d+ Z! k5 |
- printf("\r\nWrite Block Success!\r\n");
4 E3 C" U4 Z+ |9 ~' R$ k& C - for(i=0;i<sizeof(Buffer_Tx);i++)3 T q0 Z) J9 [ y& y% }3 B1 { P
- printf("0x%02x:%02x ",i,Buffer_Tx[i]);
7 p' Q8 D q8 G4 W" ]; d1 j - printf("\r\n");+ X1 K1 J" [- W8 O! T2 ]$ j& U
- LCD_ShowString(30,250,200,16,16,(uint8_t *)"Write Block Success!");5 n; T0 _8 k% H9 K: F& k
- }else{2 r! J: ?. {2 v' d7 J( f
- printf("\r\nWrite Block Failed!\r\n");
& ^0 r( B7 D, Q6 ]! Z$ p) T. `6 m - LCD_ShowString(30,250,200,16,16,(uint8_t *)"Write Block Failed!");
& e& @6 e% I* x3 p+ j+ y - }
$ L5 X0 T5 j, }2 M, O! @) [* g# u& r - break;
/ V% x+ u. f& H. l - }4 I" V* Q2 {! h" m0 @9 U+ `% K
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);. ^$ w9 e$ f7 B' D# B; z: L; c
- HAL_Delay(200);* [" \& Y9 d, A& V
- }
" s- [/ ?* i% y7 P8 i1 a4 A& w; V - }
复制代码 1 u3 `1 ]( U* c+ Z+ a) _3 a e1 J
4.下载验证 " B. h2 B5 v7 @% k0 r! _# i4 |4 [
编译无误下载到开发板后,可以看到D1指示灯不断闪烁,按下KEY_UP键读取SD卡数据,按下KEY_DOWN键写入数据,按下KEY_RIGHT键擦除数据,SD卡信息以及更新的信息都会显示在LCD屏上
! n, h% R9 m) ~" u+ R5 h/ t Q# r* s7 j- S
: j8 R; s% i' B
: r% f, |. F3 j
|