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