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