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