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