01SDIO简介1 W, a9 j$ T& J" o4 `7 ~1 t0 G
SDIO,全称:Secure Digital Input and Output,即安全数字输入输出接口。它是在SD卡接口的基础上发展而来,它可以兼容之前的SD卡,并可以连接SDIO接口设备,比如:蓝牙、WIFI、照相机等。1 j: Z+ m2 M4 a$ p3 d0 |
SDIO和SD卡规范间的一个重要区别是增加了低速标准。低速卡的目标应用是以最小的硬件开支支持低速I/O能力。低速卡支持类似调制解调器、条码扫描仪和GPS接收器等应用。- ^: o6 R; I: @
STM32的SDIO控制器支持多媒体卡(MMC卡)、SD存储卡、SDI/O卡和CE-ATA设备。5 h$ M+ n3 s* p: y; [1 ?
SDIO框图
; h( x- n1 b, V3 x( r& `" P
, O$ ?, E' Y; u ~% P5 [复位后SDIO_D0用于数据传输。初始化后主机可以改变数据总线的宽度(通过ACMD6命令设置)
1 d# N6 G6 H, ?0 b" L- _9 ?2 j: B如果一个多媒体卡接到了总线上,则SDIO_D0、SDIO_D[3:0]或SDIO_D[7:0]可以用于数据传输。 0 L' Y+ `' M" m4 }; C
MMC版本V3.31和之前版本的协议只支持1位数据线,所以只能用SDIO_D0(为了通用性考虑,在程序里面我们只要检测到是MMC卡就设置为1位总线数据)/ z2 N: o4 K4 r6 G! w
8 M, _% W ?! L/ A+ Q( C9 O' p
9 q3 V1 s# W2 W0 d$ u, P9 {
02SDIO特点* c; x- a% e3 `+ r- [/ L
①与多媒体卡系统规格书版本4.2全兼容。支持三种不同的数据总线模式:1位(默认)、4位和8位。$ _$ g7 Y8 S, f" l o
②与较早的多媒体卡系统规格版本全兼容(向前兼容)。; i1 w) n! G# U L
③与SD存储卡规格版本2.0全兼容。- p$ s7 { D# c* d; A5 R
④与SDI/O卡规格版本2.0全兼容:支持两种不同的数据总线模式:1位(默认)和4位。$ _. W: h* k3 G- L1 O
⑤完全支持CE-ATA功能(与CE-ATA数字协议版本1.1全兼容)。8位总线模式下数据传输速率可达48MHz。( Q, |: J$ o. O: N
⑥数据和命令输出使能信号,用于控制外部双向驱动器。
; K, A. }/ q0 {; f- t7 Q
6 q4 k W- ?5 b) H
8 V4 ]) {& O' L0 Q* I4 a03SDIO时钟
6 E }: k, K( I0 U& a卡时钟(SDIO_CK):每个时钟周期在命令和数据线上传输1位命令或数据。对于SD或SD I/O卡,时钟频率可以在0MHz至25MHz间变化。
* b* V% C2 q- f0 x! ^- r% ]SDIO适配器时钟(SDIOCLK):该时钟用于驱动SDIO适配器,可用于产生SDIO_CK时钟。SDIOCLK来自PLL48CK(48Mhz)。
0 l7 f6 Y ^6 A" v) }* L# f0 f. GAPB2总线接口时钟(PCLK2):该时钟用于驱动SDIO的APB2总线接口,其频率为PCLK2=84Mhz。* C' Q5 L( T& ~& h. E8 V8 I
SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV)3 `3 `$ _2 Z+ l1 l5 z
注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)
% P% F* D, r. W1 a" v
& l, X7 w3 I+ _5 ]3 S6 P/ M$ S n# v7 H3 r9 I3 V" [& Y
04SDIO的命令与响应
% |/ H; E& o, Z- O0 A+ T/ `+ l3 D" L& y命令% P2 \8 _+ y; P0 q
SDIO的命令分为:应用相关命令(ACMD)和通用命令(CMD)两部分。发送ACMD时,需先发送CMD55。7 {, B4 U' V' d7 Y' a
SDIO所有的命令和响应都是在SDIO_CMD引脚上面传输的,命令长度固定为48位,SDIO命令格式如下表所示:
" n) ~3 K% J; }% i7 _
, Q, P2 e; L$ j8 P, C
其中:除了命令索引和参数需要我们设置,其他都是由SDIO硬件自动控制。命令索引(如CMD0,CMD1之类)由SDIO_CMD寄存器设置,命令参数则由SDIO_ARG寄存器设置+ a" S4 `: l6 C9 L: k4 O
响应9 Q e* K A1 m0 B
一般SD卡在接收到命令行,都会有一个应答(CMD0例外),这个应答我们也称之为响应。STM32的SDIO接口,支持2种响应类型:短响应(48位)和长响应(136位)
2 k) A+ e: s. y! ?STM32 SDIO短响应(48位)格式如下表所示:
; N& D2 t C: I0 e
) f4 m( N3 ~, HSTM32 SDIO长响应(136位)格式如下表所示
) `( H4 Y0 X( O% ~( Y9 y+ q) Y
# j2 U3 \1 H8 E# i/ a0 }- l4 a9 \不论是短响应还是长响应,硬件都会自动滤除了起始位、传输位、CRC7以及结束位等信息,对于短响应,命令索引存放在SDIO_RESPCMD寄存器,参数则存放在SDIO_RESP1寄存器里面。对于长响应,则仅留CID/CSD位域,存放在SDIO_RESP1~SDIO_RESP4等4个寄存器。% Z) s7 Q0 L9 f" D# y3 \* T" I
SD卡的响应
7 x. c" `, I+ }# U; A6 nSD卡总共有6类响应(R1、R1b、R2、R3、R6、R7),我们这里以R1为例简单介绍一下。R1(普通响应命令)响应属于短响应,其长度为48位,如下表所示:
* _0 R% U. |! o
% e( d6 Z1 w2 Z7 h" K
在收到R1响应后,我们可以从SDIO_RESPCMD寄存器和SDIO_RESP1寄存器分别读出命令索引和卡状态信息。关于其他响应的介绍,请大家参考光盘:《SD卡2.0协议.pdf》
: {& h3 ?0 H3 c/ I3 b- H: Q( z- k9 ?4 {7 S/ x; O7 O# s. D X8 Q) P
0 l, R: r* ]" w4 I; T) `' P6 J
05SDIO块数据传输 O7 {" l2 I! L" V4 V$ s+ C4 j2 M
多数据块读操作% N5 }; o. P7 y8 h* v
SDIO与SD卡通信一般以数据块的形式进行传输,SDIO(多)数据块读操作,如下图所示
7 W1 U8 Y( m, E. ^, `4 C$ i) t2 m
; Z3 e4 A! h5 ~* _
从机在收到主机相关命令后,开始发送数据块给主机,所有数据块都带CRC校验(由硬件自动处理),单个数据块读的时候,在收到1个数据块以后即可以停止了,不需要发送停止命令(CMD12).但是多块数据读的时候,SD卡将一直发送数据给主机,直到接到主机发送的STOP命令(CMD12)
; n; F/ U4 T( _8 X4 a: e" E# a多数据块写操作
* \& Q# [9 Q3 z J5 t* I6 `SDIO(多)数据块写操作,如下图所示
( d5 r/ |' A9 U. y
8 y: ^) d, k' x7 x( ~! e0 v
数据块写操作同数据块读操作基本类似,只是数据块写的时候,多了一个繁忙判断,新的数据块必须在SD卡非繁忙的时候发送。这里的繁忙信号由SD卡拉低SDIO_D0,以表示繁忙,SDIO硬件自动控制,不需要我们软件处理。
5 ]) N/ O. E$ l2 w% ?' c- w注意:当SDIO_CK频率过快时,可能导致SD卡通信失败,此时,建议降低SDIO_CK试试。
9 U6 x& c; I3 x/ K; c) @
; A/ m S, Z- D9 I5 J1 \' v8 b2 M7 q+ c8 E6 l. f: r/ x% ~1 @
06代码
8 r3 E# ?0 r/ @. I% _% @5 D, WSTM32的SDIO控制器驱动还是很复杂的,ST提供了stm32f2x_sdio_sd.c驱动文件,我们组要修改对应的GPIO驱动即可。
( G+ ]2 W& e4 L& R: e. B- <font face="微软雅黑" size="3">/**
' ^. X3 G8 c, K9 w7 A3 C X - * @brief Initializes the SD Card and put it into StandBy State (Ready for 5 r1 X' L8 {( g2 c9 U
- * data transfer).9 N# ?( V u! G( T
- * @param None- z8 Z- h% ~+ }- D8 d
- * @retval None
" |# c/ G2 H3 o! |! p9 S& E - */
& F9 ~; y0 L) P. d5 Z - void SD_LowLevel_Init(void)+ c4 i% l6 k3 z
- {
# p3 v) _3 h/ p - GPIO_InitTypeDef GPIO_InitStructure;
5 c' u! |+ ~# _3 @ - 5 I9 l4 v5 n" a3 M
- /* GPIOC and GPIOD Periph clock enable */
3 g/ ^1 |* Q3 u8 t3 ]& w - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | SD_DETECT_GPIO_CLK, ENABLE);
+ a9 ?2 R' a s( Y - : M8 o" M# k/ T y$ o3 u
- GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_SDIO);
* {: V1 H8 E9 T/ ?: \ - GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SDIO);
8 W: J, ^! W4 b/ `$ s9 R - GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SDIO);- F. O0 f/ P4 D2 s
- GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SDIO);1 J+ u4 Z/ p* p% i6 u
- GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SDIO);
i, X0 N! J8 S/ f# Q - GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_SDIO);
2 O2 }: L- U5 d" r - , j( q# X$ D- l1 @/ e0 D
- /* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */& `% V! E+ `/ M& N, x# z
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
1 O _# F& J" u; ^; m - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
( y. R2 b7 J$ ~& q - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;2 \7 v' {$ e! {
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
9 J3 @+ w- ?8 s- | K# y - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;& d. U. N, v% W" F
- GPIO_Init(GPIOC, &GPIO_InitStructure);$ Q6 l8 C K* l7 W
- - d# q1 Q8 Y7 Q5 k! F5 l: ]) v
- /* Configure PD.02 CMD line */& E+ N5 l0 M1 |* i
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
( a/ p8 z4 e9 ^) l5 g* q2 O% f. D - GPIO_Init(GPIOD, &GPIO_InitStructure);0 F( A) x. I F, g
- # \) k$ X7 o: g. V. L
- /* Configure PC.12 pin: CLK pin */: V# L! u& O7 K9 L
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
. L- c# Q3 h7 k! r; d6 J1 g. J9 d - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;' ]3 z! [7 n* E
- GPIO_Init(GPIOC, &GPIO_InitStructure);
9 C8 X" e- V4 I) | - N) u9 q S( Z# T$ U
- /*!< Configure SD_SPI_DETECT_PIN pin: SD Card detect pin */
/ e: [$ { @& G1 t& v% t - /*GPIO_InitStructure.GPIO_Pin = SD_DETECT_PIN;
2 K6 A2 J( | E d: [ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
6 |* I2 V) {$ Z$ Z9 J( P - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;1 ~+ m' ]; ~' S% c+ P. L
- GPIO_Init(SD_DETECT_GPIO_PORT, &GPIO_InitStructure);*/
. E! @# C* d7 I% T$ S1 a - ) d* N9 c6 c% |1 M3 ?% f
- /* Enable the SDIO APB2 Clock */4 L% f1 g2 p2 R; `' l" k9 b2 Y
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);
s3 h& e6 O* j! Q1 N$ H9 z" Q
; r9 x9 M9 t8 C; o- /* Enable the DMA2 Clock */
6 O$ p, [4 m$ e5 E: _* q - RCC_AHB1PeriphClockCmd(SD_SDIO_DMA_CLK, ENABLE);
4 |! d5 ?, x1 R9 n9 Y; D - }
; b# C! [" }9 ~" A7 D+ P - </font>
复制代码 DMA等配置使用默认的即可。, N8 Y& v3 e4 f, b, q6 m
我们需要使用的函数接口有
# T* X" t5 R$ n- <font face="微软雅黑" size="3">SD_Error SD_Init(void);
+ Z# o. E% }- r - SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr);5 y- ]/ z7 R' n7 a' X) [5 _ n/ r9 E
- SD_Error SD_WriteBlock(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize);
! J6 e1 b4 u7 M, u - SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize);</font>
复制代码 下载验证
1 m1 x) c, U6 X, h9 s
, L u- |9 \* d0 g5 X8 J2 v
' ^- _' J2 f) [+ Z3 K: t& c |