6 ^' m) x( s" @. p! B q/ v
SD卡(Secure Digital Memory Card)即:安全数字内存卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。 9 P7 H, h5 W& Y# H" T
SD卡简介 7 f5 ^4 g% Q: k# G+ Q& d
SD非常小巧,可以嵌入到设备中,在嵌入式开发中,产品需要存储一些容量叫大的文件,就可能会运用到SD卡。SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示。 7 a7 z2 [, P/ j+ F
. ~; Q5 L2 w0 g
容量大小不同的SD卡,其内部的操作标准是不同的。很多外设会明确告诉用户本设备最大能够支持容量多大的外扩,不能无限制的扩存,因为容量越大,操作不同,对控制器的要求比较高。当前设备的控制器不足以支持大容量,所以有外扩限制。 & U8 R" l2 y2 _& `3 A: v, N# k; G
( f3 E- |1 h. e, o; \
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。
2 E9 t5 b% l( s# P$ @6 ?9 Y e
$ D1 @$ q7 J5 L, w: \" ]/ K2 o. b
* r. L9 S+ z# z# B/ U: K
- \+ W0 Q. f: j% S# J1 W0 p! rSD卡的物理结构 * Z) b6 d6 q/ o1 v5 E, S. N* Z
* [0 T% |- G* F e9 i% `
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。 ; k3 S; ?9 j0 X2 q* N. w
8 A- M: u4 J0 W4 `# y
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。
) H$ l) G4 m$ n& I
% K. W! e8 T5 K) L' d: i7 z- E
8 G8 W5 b/ z( { D2 C存储单元:是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输; $ S, R& d4 |; }# }: y' A
电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;
8 \6 d( f2 @- g: Z0 c! J
卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;
' o/ q. M' q. T& I9 v
接口驱动器:控制SD卡引脚的输入输出。 ' v! C- A/ E/ t0 ?1 U( ~
! ~6 _1 T( O8 o |; H
SD卡内部寄存器
% Y0 K; i$ S% b3 g' r, w- _1 Q
SD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。 $ _) M3 s8 W+ b
$ ~+ [- P& U- Z S# b% w
SDIO接口
, V7 n/ `' J7 V& d* _
SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡都有SDIO接口。STM32F407系列控制器有一个SDIO主机接口,它可以与MMC卡、SD卡、SD I/O卡以及CE-ATA设备进行数据传输。也就是说SDIO接口不仅仅只会操作SD卡,凡是符合SDIO接口通信的设备都可以被操作。 ! l: I" ?. M7 F" w- a7 O9 |
( w5 e1 y+ n6 W. ?0 X8 |+ g- W
SDIO 具有以下特性: 完全兼容 多媒体卡系统规范版本 4.2。卡支持三种不同数据总线模式:1 位(默认)、4 位和 8 位 完全兼容先前版本的多媒体卡(向前兼容性) 完全兼容 SD 存储卡规范版本2.0 完全兼容 SD I/O 卡规范版本 2.0 : 卡支持两种不同数据总线模式:1 位(默认)和 4 位 完全支持 CE-ATA 功能(完全符合 CE-ATA 数字协议版本 1.1) 对于 8 位模式,数据传输高达 48 MHz 数据和命令输出使能信号,控制外部双向驱动程序。 SDIO 不具备兼容 SPI 的通信模式。
; r; s" r0 N5 }9 g
SDIO组成
% E! w$ h8 x2 S* h1 t5 n
SDIO 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令和数据传输。
0 h, I, ?& @ I& q+ C
APB2 接口访问 SDIO 适配器寄存器,并且生成中断和 DMA 请求信号。
' S7 D$ e2 x2 O
/ n+ V5 W2 C$ n5 {, V7 m! P
注: 1、默认情况下,SDIO_D0用于数据传输。初始化后,主机可以更改数据总线宽度。 2、SD 卡连接到总线,主机可以将数据传输配置为SDIO_D[3:0] 3、SDIO使用两个时钟信号:SDIO适配器时钟(SDIOCLK= 48 MHz)和APB2总线时钟(PCLK2)。 4、卡时钟(SDIO_CK):每个时钟周期在命令和数据线上传输1位命令或数据。对于SD或SDI/O卡,时钟频率可以在0MHz至25MHz间变化,当数据正式稳定传输的时候配置为24MHz。SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV) 5、SDIO适配器时钟(SDIOCLK):该时钟用于驱动SDIO适配器,可用于产生SDIO_CK时钟。对F4来说,SDIOCLK来自PLL48CK(48Mhz)。 6、F4:APB2总线接口时钟(PCLK2):该时钟用于驱动SDIO的APB2总线接口,其频率为PCLK2=84Mhz。
: {/ v! Y0 U, g) s1 ?& d
注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)。 # V; v, l1 x1 \" \; {$ ?2 r0 G; @
6 Y* d& P# D3 D- F6 x L
该适配器由五个子单元组成: 1.适配器寄存器块 :适配器寄存器模块包含所有系统寄存器。 2.控制单元 : 控制单元包含电源管理功能和存储卡时钟的时钟分频器。 3.命令路径 :命令路径单元向卡发送命令并从卡接收响应。 4.数据路径 :数据路径子单元负责与卡相互传输数据。 5.数据 FIFO :数据 FIFO(先进先出)子单元是一个数据缓冲器,带发送和接收单元。FIFO 包含一个宽度为 32 位且深度为 32 字的数据缓冲器和发送/接收逻辑。(一共32 个单元,一个单元一个字)。所有的数据传输都要经过FIFO,便于管理。
5 p( f) y1 B! d! [5 j0 R( D
SDIO 命令简介
- n0 J: H C" O p) ~, S
SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。
! y! H! i- H" o, e8 J
SD命令格式固定为48bit,都是通过CMD线连续传输的,数据线不参与。
" l) o+ H' [1 R
3 B8 ~2 U, O/ u9 F: W! g5 d
SD命令的组成如下: 起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。# ~9 D3 `+ a: U2 f0 i
3 m3 q% I' e& e7 [2 V# `" x9 f# C* Y
传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。 " H9 V n7 Z- D9 \! P. c8 k
命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。 " ^0 N @% b: P: Y$ [ D
命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。
( ]8 h. G: s* e' k. X# @
地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。 6 L& s! ^) d3 Q& U1 u1 v
CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。
: f0 p6 [* G, p8 I- g
SD命令有4种类型: 1.无响应广播命令(bc),发送到所有卡,不返回任务响应; 2.带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应; 3.寻址命令(ac),发送到选定卡,DAT线无数据传输; 5.寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。 3 y! |- R; K/ h C. r
在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD),也就是说在64个命令作为常规命令的基础上加了特定的命令
) U. U. ?& @; d' k3 K) f
要使用SD卡制造商特定的ACMD命令如ACMD6,需要在发送该命令之前无发送CMD55命令,告知SD卡接下来的命令为特定应用命令。CMD55命令只对紧接的第一个命令有效,SD卡如果检测到CMD55之后的第一条命令为ACMD则执行其特定应用功能,如果检测发现不是ACMD命令,则执行标准命令。
X" @6 g j; M6 a3 x4 A3 @
SD命令响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:
& o% Y+ v" L4 S6 z: e& F
lSDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。
, Z D+ r6 Y6 g
SDIO读数据
; M' I- w: T7 N: X) N$ B
; N% G, s# W. f
单个块读操作与多个块的读操作除命令不同外,还体现在读操作结束时,单个块读取一个块自动结束,而多个块还需要主机发送停止命令。写操作类似,也要多发一个结束命令,只不过写操作写数据前需要检查卡的状态是否为忙状态。 : {/ ~6 m5 }& ^ f3 f! _
SDIO写数据
! [9 X7 }; W1 ~+ O- g% i
* u" C- S4 q, k& J
SD卡操作模式 . C- b& ^6 e2 F# l
SD卡有多个版本,STM32控制器目前最高支持《Physical Layer SimplifiedSpecification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。 . |) @0 n: _) y/ D* c! M
SD卡系统定义了两种操作模式:卡识别模式和数据传输模式。
* ~ c/ |' r5 ^
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。 6 v9 b. @* v/ C6 A
每个不同的操作模式下,SD卡都有不同状态,通过命令控制实现卡状态的切换,在不同的状态下做不同的事,比如在发送数据前需要SD卡处于传输状态,发送数据时,SD卡处于接收状态。
7 D; {6 g' x- v9 T- r
) `0 P4 m* Z" F
7 t9 _' Q1 f6 b3 r& u% k( ~
①上电后,主机发送CMD0让所有卡软复位从而进入空闲状态。 ②主机发送CMD8确定卡的电压范围,并识别是否为2.0的卡 ③主机发送ACMD41识别或拒绝不匹配它的电压范围的卡,SD卡需要工作在特定的电压范围之内 ④主机发送CMD2来控制所有卡返回它们的卡识别号CID(128位) ⑤主机发送CMD3命令,让卡推荐一个RCA(16)地址作为以后通信的标识,之后都以RCA值作为身份标识进行信息交互。
' k8 ~! r) ^( D& {
注:在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下,在SD卡初始化时,SDIO_CK不可以超过400Khz 8 t6 n9 u! R1 V
数据传输模式 2 w: ]' x0 `" }( G5 [: _
! q% T! | Q# H6 _- ]" X
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。通过CMD7命令加上RCA值来选定指定的卡,选中后SD卡进入数据传输状态,就可以发送CMD17读单个块,CMD18读多个块,读多个块时只有发送CMD12命令才会停止。SD卡再次进入传输状态,若不想对卡有任何操作可以再次发送CMD7命令加上RCA值来取消指定的卡,写操作与上述原理相同。
6 |. }* I% E8 X2 |$ G( C9 ?0 _- Q
SD卡普通模式操作实例
9 A) P. d, q4 U: |( p' Z/ ?
实验内容:向SD卡写入数据后读出 . I# L( X9 p5 \- L8 ? D
实验步骤:
: E! M7 Z8 q7 }1 e" B6 Q
1.配置RCC,与以往不同的是SDIO适配器的时钟是单独配置的,需要专用的SDIOCLK,标准工作在48MHz
) r9 A, o; @$ v5 Y) f% H9 l$ N5 s% ]/ ?/ R8 ?- M
2.配置SDIO* I- {+ ~' i. |- @
6 X+ Z% H9 R' C2 P3 K; I
* M8 Q# M) |: ~2 u* M! d9 U! ^! E" K; ?3 I; W2 z
3.编写代码
% p. S0 H6 Z3 d, R0 {) F8 N- //mian.c
" n3 @( F; R6 u! V2 o1 _, [/ b - #include "main.h"
! x O; b5 J- _% [+ x: K0 m - #include "stm32f4xx_hal.h"9 J9 n9 Z e/ }# \
- #include "sdio.h"! W6 q) ]( j; E3 P V) Y
- #include "usart.h"
5 q! y7 Z) V* d3 ~9 C - #include "gpio.h"% `# ?( T/ Q7 A1 w j" r
- #define SDBUF_SIZE 1024
1 ^; h9 b6 M8 ]- W% |0 y! H m - uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收' P9 s7 t$ W9 ~ @8 X2 z5 n* c, f
- //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出6 m3 ?6 x% _" r# {8 a
- int main(void): M# ^: y% l# H
- {) B; A7 V L, z4 K0 s4 W/ k
- uint32_t i;
9 d8 @. ~4 h: s9 y - HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息1 w5 T3 Y" w: O# i m
- HAL_Init();8 n# i' B( n* q9 k) H0 u: n
- SystemClock_Config();" }% J9 q5 Y, I- U! d
- MX_GPIO_Init();
3 w- W6 d1 _% e7 I( R, f - MX_SDIO_SD_Init();
8 B' H4 i1 X; a) x+ q - MX_USART1_UART_Init();
& _/ H% v/ x6 H6 k' x, | - printf("this is sd test\n");
( u+ g3 @' j. f& F - //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印4 b6 A6 ~( ?& ?+ U; S; F2 ]$ m: z0 P
- HAL_SD_GetCardInfo(&hsd, &pCardInfo);' a, P2 H3 U1 x5 o! Z2 i
- printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);
) }' x' B. ~1 m, A - printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
i* v9 b$ B6 Q9 g9 d8 \1 f - printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
6 |4 B# E1 ` j: e - printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小1 N# v- @2 d- Y" ~* @) A
- /*--------------------SD卡写测试----------------------------------*/8 W, P# K4 o" P' F5 B
- memset(SDBUF_TX, 0x8, SDBUF_SIZE); /、填充TXbuf为0x8( m6 [) L9 C1 x: Z/ R9 g
- /**
; f' c/ @7 J E, L" H A - //函数功能及参数描述 & q. V0 e/ ?# W
- *HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, 8 g$ x- X) J$ ]- ]! Z
- *uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
( L1 `# p" b3 k; B - * @param hsd Pointer to SD handle# X% w% u6 m2 F _8 J' e" ]
- * @param pData pointer to the buffer that will contain the data to transmit
' n: _- c: c$ n) G* e/ o - * @param BlockAdd Block Address where data will be written,从哪一个块开始写
! [5 A3 h# ?2 m t/ M0 j - * @param NumberOfBlocks Number of SD blocks to write 写几个块
, y6 H1 T6 k. E$ K% O, k - * @param Timeout Specify timeout value 超时时间 T$ c G8 `; p7 s( B$ I. p
- * @retval HAL status& H# z. Y' h, J# V# L6 H
- if( HAL_SD_WriteBlocks(&hsd, SDBUF_TX, 0 , 2, 1000) == HAL_OK)
/ X: Q$ C* O! I: C) ?4 d - {& N$ q5 _0 P& d1 \% u3 {3 E
- while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//处于传输状态退出
, W$ ^. y8 m: C. ?& v. t - printf("WriteBlocks Successfully\n");
' O* u8 M. p( H" k7 M' @ - for(i=0; i<SDBUF_SIZE; i++)! y! j% R" S: {3 W1 t& e! | w
- {
# A( p9 w% f" v, i% P: l9 h, x0 S - printf("%d ",SDBUF_TX[i]);
, X, h( ]4 K. k$ }* W* P7 s. C0 ~+ y - }9 A& C3 x; x3 t# r& N- U! U
- printf("\r\n");" j% `5 b$ _3 y, l8 v! N! H
- }# ]% S# W! T; n5 V
- else' S! O) t# m1 m6 z7 _) c3 r6 Y
- {2 F- m, i8 v, }1 T/ c/ f
- printf("WriteBlocks Failed\n");
; x" L5 O( L- L& S' H - }
$ W% s/ T. j( ^5 E' J - /*--------------------SD 卡读测试----------------------------------*/1 [3 B4 X3 l3 ^
- //与HAL_SD_WriteBlocks函数的参数功能相同
) t: ]& v) w8 P- N - if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
1 k2 [$ {0 V0 _2 @ - {
& w5 n: H- R8 p E/ e+ W - while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//返回到传输状态退出
8 P$ a: Y9 ^; y: k: ]$ [0 N- K - printf("ReadBlocks Successfully\n");/ ]. g) M6 o3 z' E' _: P! L- A# L4 m
- for(i=0; i<SDBUF_SIZE; i++)
2 a- w' W6 F. \$ J4 Q6 o) Y% w - {
7 t) U% \ F& M; G# w; b _6 ? - printf("%d ",SDBUF_RX[i]);+ d3 [* X2 }9 x B
- }
) v6 {. F' D! K" i: ^, L7 M - printf("\r\n");
. Z1 i; a5 ?" Q3 B2 P - }
) P$ F2 |% A6 n- N% \: y. n - else
7 y" S! `5 L. j* ?' ?( r* N I - {
; Z3 @& }4 {' k& B. a3 Z# ] - printf("ReadBlocks Failed\n");5 R+ d# H* ]6 z" ]/ ~
- }
7 N$ ~: O* P, Z8 O - /*--------------------SD 擦除测试----------------------------------*/
7 f3 t/ v' r2 e2 e, x4 p$ o - if (HAL_SD_Erase(&hsd, 0, 1) == HAL_OK )4 J/ S. H. }% N; A0 c: [
- {
1 c# ~, g. ?3 Y0 R# g/ t) y - while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
: Y, V& x. d+ h* ^3 O' H5 ~/ j" ? - printf("SD_Erase Successfully\n");
% d5 T @, o9 L' [8 B& q - }: j+ r6 O: \; v+ ]+ s* y
- else: M7 u+ [. t2 w5 z+ l
- {" I9 x Y( q* V$ _; E6 v
- printf("SD_Erase Failed\n");
/ e% o' Q9 E& v0 T' `$ m7 H/ M9 I - }' n8 { r2 ]) N
- /*--------------------SD 卡读测试----------------------------------*/
1 j/ t0 o* @; Q. g9 T - if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)0 h% o& t5 J' \* ]6 U
- {
& f4 R8 a4 S0 ` L1 O, L+ N - while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
6 k& u) O7 U9 Z6 [8 {" [( C - printf("ReadBlocks Successfully\n");
h% ~, D9 e+ P2 ~7 x1 M/ {9 ^ - for(i=0; i<SDBUF_SIZE; i++)# T; b3 `6 h/ d/ W ?
- {! C+ I7 u& S; o2 i8 `: x; L
- printf("%d ",SDBUF_RX[i]);, D2 [' h1 B+ C) ?
- }
' ^ }! _4 t! e, o$ m - printf("\r\n");
( a5 p& Z( |! U. A+ w - }0 I2 C# j+ w) k9 F3 }5 m8 C. X
- else
. b5 H0 |8 [, K4 [! W' U - {2 h, h& n7 w' E4 |1 ]
- printf("ReadBlocks Failed\n");: |" J1 o9 V0 e( Y
- }
: B$ q, G) T. p9 H - while (1)) V; u. T9 V0 L! `! f& u
- {5 |2 ]7 O' \6 X9 N* O" d5 U: _
- }
0 `5 z" a3 p3 h7 f$ c - }
复制代码- //sdio.c
4 a" [: b* p* I/ `5 U6 }2 B# ~ - //此代码为工程自动生成,非用户编写,这里只做分析0 J' u% T: ~3 ~0 M% b) k
- #include "sdio.h"& A+ a' a, e x O. ^2 _ z: Q8 p
- #include "gpio.h"
) c' H1 L9 N5 o# O# \ - /* USER CODE BEGIN 0 */$ f/ e( l1 y: p! v( C
- /* USER CODE END 0 */
; O7 q$ J4 t8 ~; i' @ - SD_HandleTypeDef hsd;
6 Y+ @" }+ }4 O7 E: O5 m - /* SDIO init function */
$ ^0 k5 |$ }/ K B$ F - void MX_SDIO_SD_Init(void) //初始化配置- x, w8 p5 l" n! N, [
- {2 v" e) l7 i+ W: X* e4 e
- hsd.Instance = SDIO; //SDIO句柄,整个系统中只有这一个
1 Q2 U4 R- j! i& O - hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;: Q( Z* _* H R% _+ F7 t
- hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;/ D- R4 M) \1 c* q9 w& M
- hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;. T Y% _2 n5 }: H* m
- hsd.Init.BusWide = SDIO_BUS_WIDE_1B; //初始化时设置数据宽度为1位' S+ [5 e# \$ \' E0 u
- hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
' G; O3 ?, f# r# R$ B" T4 b - hsd.Init.ClockDiv = 0;1 i% P! s6 r* x7 \
- //HAL_SD_Init()函数内部做了很多事去识别SD卡,具体的卡识别代码分析在下方
* L! E6 h( T- k: |0 J - if (HAL_SD_Init(&hsd) != HAL_OK) 1 n5 [$ a$ m3 Q" p9 O2 f
- {( ?5 D/ z% r. T
- _Error_Handler(__FILE__, __LINE__);3 G" ]4 h1 T( Q
- }
- p4 C! U4 h: i- H3 ~ - //初始化结束后重新配置数据宽度为4位
: a# {: S2 N1 ]) X2 ] - if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
6 n+ K8 F: K; |4 `, p - {8 S3 F4 M2 d, ^# s2 S
- _Error_Handler(__FILE__, __LINE__);: R5 u) v9 z! e$ P4 U$ a
- }
3 s5 `/ F2 y G( T - }
* U# h7 [0 f1 p5 s - //卡识别代码如下5 ]2 u8 G {6 U4 T! | k
- HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd)! Q" O d3 ^/ u3 N X8 K) d
- {- n" u2 @7 \1 ?$ C
- uint32_t errorstate = HAL_SD_ERROR_NONE;
3 E+ p {7 U3 b2 ?+ {5 S2 F9 L1 ? - SD_InitTypeDef Init;9 x6 N" O2 Q. W+ o. C2 Q# r% _% s
- /* Default SDIO peripheral configuration for SD card initialization */. a! }. T0 a3 m/ U3 w6 M6 r0 f
- //完成卡识别7 X: q7 i4 t" [1 J; M, A1 P
- Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;7 J, B" s7 h$ ?: [0 _$ [
- Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
9 |6 V( P9 c) R. Z, H% k& y+ C - Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
# h0 U% ^0 }+ w X; r9 g - Init.BusWide = SDIO_BUS_WIDE_1B;% Z0 W7 m; y T0 u# @2 h! m
- Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;, w8 {! n. O2 l7 Q% j6 C- C
- Init.ClockDiv = SDIO_INIT_CLK_DIV;
& x( C" d9 ^4 m, F5 g - //SDIO_CK卡识别阶段要求时钟小于400KHz,宏定义
n- T6 `; a# U% w" F - //SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV). F2 L/ @1 f7 o$ k' e$ C
- /* SDIO Initialization Frequency (400KHz max) */1 ]' n5 ]8 i/ V5 ^5 N! |/ Z T$ I
- /*#define SDIO_INIT_CLK_DIV ((uint8_t)0x76)*/0 E, p$ Z6 C$ v" I
- /* Initialize SDIO peripheral interface with default configuration */
- k" H& ^+ G; F0 e - SDIO_Init(hsd->Instance, Init);
" q6 {9 A! _- m+ m% q - /* Disable SDIO Clock */
3 b+ B0 o, `9 i) E$ g0 e - __HAL_SD_DISABLE(hsd);
' |# Q: r% ] P, G% ?% B - /* Set Power State to ON 对SD卡进行上电 */8 v7 O- j$ C9 h8 |
- SDIO_PowerState_ON(hsd->Instance);
7 _% E5 X, Z Y0 j8 p. W - /* Enable SDIO Clock */4 ^0 M. {) ?! A# v P
- __HAL_SD_ENABLE(hsd);4 s* y5 N% G& l# V& _/ I
- /* Required power up waiting time before starting the SD initialization % C5 l# _6 b3 W. b
- sequence */
3 S# F3 D2 l( I& n+ l% | - HAL_Delay(2U);! z$ h: d0 m$ Q; W: i
- /* Identify card operating voltage */# f6 s3 \" B5 a/ k( V
- errorstate = SD_PowerON(hsd);, P5 V. a- c( K% I
- if(errorstate != HAL_SD_ERROR_NONE)6 L2 n+ c9 x6 F g" m1 E
- { x3 l- c* I9 C
- hsd->State = HAL_SD_STATE_READY;
0 n8 s6 q8 M! E0 W7 r- ]4 K - hsd->ErrorCode |= errorstate;
" }$ o& R* Q+ D8 e - return HAL_ERROR;
" Y- P& ?# W% _6 J: W% g - }6 O6 Y; S e6 K# m1 K) `5 R
- /* Card initialization */. ]/ E! t$ e, t/ ?! X
- errorstate = SD_InitCard(hsd);
$ p/ e5 X( J Q - if(errorstate != HAL_SD_ERROR_NONE)
. k$ l- q# {- E- O+ l2 W - {
! z4 h! E; R X9 |, B( u: { - hsd->State = HAL_SD_STATE_READY;
6 M% s4 K5 Z1 Y/ p3 r - hsd->ErrorCode |= errorstate;
: X/ I0 v$ [4 Q8 R - return HAL_ERROR;& m$ Y5 }! Y2 B0 Q( h
- }3 G& v4 d5 u! V- N; c$ q4 a1 L; n
- return HAL_OK;4 l' z/ w) T9 X6 ^ l$ Z7 G4 P
- }
复制代码 ( o) ^! i" _ ~& v9 [. z
当SD卡中有大量的音视频需要读取时,整个过程需要CPU干预,这样CPU的利用率就会降低,因此大量数据的传输最好启用DMA。 5 s, P, x( ?8 }/ \" g
实验内容:向SD卡写入数据后读出。
o$ `1 F }9 ~- P) E
实验步骤:工程的时钟配置和SDIO配置与普通模式相同,只有一点不同需要打开SDIO的全局中断,因为SDIO发送与接收完成后都需要中断去生成DMA请求。设置SDIO全局中断优先级更高一些,因为它内部还有很多其他中断,发生错误时更需要处理。
* F5 C, \9 y8 W9 c! o / F8 }, ^) |0 N/ G- s2 \; F
配置DMA ; v1 @3 y* O& X9 B& [" [2 a
( d8 Y4 m9 W# Z0 ^7 ]7 M
编写代码: - //mian.c
9 D" s1 x; x0 p& k
v6 r7 h! I: b$ U8 t# F! G4 s- #include "main.h"- `* D9 m) L! M) N
- #include "stm32f4xx_hal.h"
7 V% l7 _1 l8 e% l6 s& ^$ p - #include "dma.h"- j0 M, B7 F9 P* G- \; M7 E
- #include "sdio.h"
) v1 e+ A0 @9 L: h - #include "usart.h"
" b" H% h8 @3 u# u# X3 T - #include "gpio.h"
; o: A7 S0 i M& y3 w
# A& T( Q& v2 s- #define SDBUF_SIZE 1024
, \# `* z' ]! o! m4 n! B - uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收- r8 ~# m- I! q) w+ l o. d
- //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出' K2 y0 i0 h8 k( a$ o6 ~; v
- uint8_t DMA_SEND_OK, DMA_RCV_OK;6 N: v7 M# K" }9 \! Q# J
- . u; U1 x0 j/ ~' K6 [2 m
- int main(void)5 d% T+ M: R6 i) N0 K* ]8 N0 b# B
- {3 S& Z- h# a+ U4 V$ ]- t! G" t
- uint32_t i;
, i% t- x% r; j5 E - HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
+ ~5 E/ Y( U4 n - HAL_Init();
3 V) Y' x% L/ K9 O& Q - SystemClock_Config();4 h8 q$ w" ?. X+ |
- MX_GPIO_Init();' ?6 x' Z+ s @: a6 p: m" A* l
- MX_DMA_Init();4 m9 B b. n" q& ~5 x5 j5 g( o& d
- MX_SDIO_SD_Init();, X! H! N5 l4 g3 ^# `4 ^6 `4 N! X. H
- MX_USART1_UART_Init();/ S7 V" x, d' o( \# R
- 7 c* B2 r0 d- q! F- n
- printf("this is sd test\n");$ G1 {9 ?) G* i% z$ d% A" @4 u0 d" h
- //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印
* G1 ]5 @+ q- U9 d e' O - HAL_SD_GetCardInfo(&hsd, &pCardInfo);: L0 J( V e5 K9 ? ?+ F. ^
- printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);, c+ b: j/ W# }0 |' N# ~
- printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
- [6 s- b0 M+ a4 U" q( u8 `! Q - printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数, L7 u; ^4 T) J
- printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
3 S6 [* _) H) ]2 {0 h - /*------------------- SD DMA 写测试-------------------------------------*/& |% |+ B6 W5 O m8 D8 N* {
- memset(SDBUF_TX, 0x2, SDBUF_SIZE );/ R8 v+ |9 U5 ~. o1 ^! [
- DMA_SEND_OK = 0; //设置发送完成标志位为0,完成时为14 O2 G; s, |5 _- p* y/ c, f
- /**6 | F. t" s u. O7 D5 z( H
- //函数原型
( P: [( @; t V" w$ l ?% K - *HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, 4 D) S8 N$ P4 ]- Q5 [$ n
- uint32_t BlockAdd, uint32_t NumberOfBlocks)
! @; [) u2 }" U2 q! [9 V - # B8 B- x# X' i8 M& c0 w/ A4 O
- * @param hsd Pointer to SD handle
8 L: a6 K% b( l+ u" G - * @param pData Pointer to the buffer that will contain the data to transmit7 y& }8 z+ _7 D; Z9 a- z, m
- * @param BlockAdd Block Address where data will be written 从哪个块开始写 C9 N6 O3 D, u# j. Y/ p
- * @param NumberOfBlocks Number of blocks to write 写几个块8 \9 E* A& H# Y9 n' w+ \! P" S, E
- * @retval HAL status
: O0 i. x( l. J; l - */
; J# L, o: k0 c4 \$ S" w - " [ r2 ?9 C0 ` _9 r
- if( HAL_SD_WriteBlocks_DMA(&hsd, SDBUF_TX, 0, 1) == HAL_OK )' a2 j: B3 V. l
- {
2 H6 u0 y9 P0 N4 t Y: z8 _ - //等待DMA传输完成,并且SD卡状态为传输状态
/ Q7 f* r4 P4 W0 D; u8 p - while( (DMA_SEND_OK ==0 ) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));
) {, x4 u6 c7 `9 Z+ t# v b' P - printf("WriteBlocks Successfully\n");
' e' t5 i* o0 x5 p. d -
* B4 M: a U6 o J A0 M' M - for(i=0; i<SDBUF_SIZE; i++)1 i' u2 |. e$ R' N+ a/ d
- {
( y: T' s, {7 r q - printf("%d ",SDBUF_TX[i]);7 T0 I) a7 {/ l
- } @' v% A0 M+ X
- printf("\r\n"); 7 @' U1 v, d2 y0 \# _% ?: ^+ l' T$ S
- }
Z) _/ m+ d7 ? - else: m, Z2 l$ Y8 W+ a8 e+ K
- {
$ n' O2 h9 H* o$ n - printf("WriteBlocks Failed\n");! [/ v1 O8 E& B, _# E7 G* g: l
- }) ?) [& _' d- B; }$ M
-
( P7 z0 P+ \- J. v, l8 ] - /*------------------- SD DMA 读测试-------------------------------------*/
% H% |3 h. T2 k8 D. K0 f - DMA_RCV_OK = 0; //设置读取完成标志位为0,完成时为1( p6 V* p6 H$ q8 N2 {* y* `% ~* p
- if(HAL_SD_ReadBlocks_DMA(&hsd, SDBUF_RX, 0, 1) == HAL_OK)
; L! Y0 g$ j; M - {
) \$ B$ z* G1 |3 { - while( (DMA_RCV_OK ==0) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));
. h! `% V5 T5 Q4 t5 q - printf("ReadBlocks Successfully\n"); : o3 ]; f/ }* {2 j' p; `
- for(i=0; i<SDBUF_SIZE; i++)
( |. U: G4 g. @5 t1 H! ] - {* _5 [, P# ~* @$ ^0 l
- printf("%d ",SDBUF_RX[i]);
1 r, F& q3 ~5 K, a2 N1 u3 r - }
1 G" s. e" j8 K1 b7 }7 E - printf("\r\n");& m* k- d# }5 w/ L+ O
- }
" k0 J7 _9 d8 t+ _: x) @ - else% Y- W$ Y, @' h8 _' t
- {
' |1 w" L, U8 b2 @, t - printf("ReadBlocks Failed\n");8 V' B/ c/ {; u8 S
- }# r: n; `6 ], L3 d& U
- while (1){}
+ z5 E9 ^7 t( m$ m - }
复制代码
* L u: z$ c# `- 5 v9 R$ B: }4 F
- //sdio.c0 l3 x$ _ ]! c( [7 l
9 v( ~! R; W2 P3 ~3 i- extern uint8_t DMA_SEND_OK, DMA_RCV_OK;- s+ {1 C# o' a- L/ Y, K
- //发送完成中断处理
: V, Z' X2 g% S0 v" N' z0 s; I! ] - void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd) {! d6 X8 x/ D2 B7 A
- {+ \9 K: O- o" u6 s
- DMA_SEND_OK = 1;
6 e0 F3 @0 B9 L( y/ E$ ]. \ - }
4 C* a; u8 i8 M$ A4 M - //接收完成中断处理; i3 h& L& e+ _. \0 |! B7 @
- void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)0 V/ h& f5 Z) [; F( X/ `- `
- {3 {7 {6 a* v* l" [0 z' p2 B. B
- DMA_RCV_OK = 1;5 z. L+ t5 e5 R- y% [' ], n0 G
- }
复制代码 5 y* H9 W% f/ g9 [; ]* e9 ]" N
至此,ARM开发进阶知识已全部更新完毕!下一系列介绍物联网操作系统开发!) T0 ^. J7 A$ }" @6 {5 F
9 K5 ^# M" h; c o( e' |
* [* k) |' S# V! @$ q7 F转载自: 骆驼听海
5 j5 h; a7 ~/ y B9 @' A如有侵权请联系删除
) I& m; E3 R# J- @$ H- a" Z. p, V( } |