你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32开发进阶SD卡操作经验分享

[复制链接]
攻城狮Melo 发布时间:2023-4-11 19:23
微信图片_20230411192319.jpg
: ~/ r& r" f6 o' P  m0 y* I
SD卡(Secure Digital Memory Card)即:安全数字内存卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。

$ w/ _& U8 F5 u$ k2 i9 f' Z
  • SD卡简介

    , M1 B, C, F3 I4 ?4 ^/ L
SD非常小巧,可以嵌入到设备中,在嵌入式开发中,产品需要存储一些容量叫大的文件,就可能会运用到SD卡。SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示。
# M/ Z5 b2 E' y$ |- x: ]) T% J/ e4 }
微信图片_20230411192315.png

3 f/ Q8 {' E: N3 b
容量大小不同的SD卡,其内部的操作标准是不同的。很多外设会明确告诉用户本设备最大能够支持容量多大的外扩,不能无限制的扩存,因为容量越大,操作不同,对控制器的要求比较高。当前设备的控制器不足以支持大容量,所以有外扩限制。

! M  b0 v3 p2 m, L( v
微信图片_20230411192311.png
+ L. {* H) v. r
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。
4 ]: e. E9 N: V) s! f

8 v& T/ d$ h$ D( h- F. F
微信图片_20230411192306.png & h" B# P4 ]: Z
2 y4 f, P( _7 f; ^; D
  • SD卡的物理结构

    " m8 H) E9 ^9 o. d7 `9 N. U1 i, G

5 F3 y( h3 s+ s
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。
  @; N/ h/ Z" g, t7 P% X
微信图片_20230411192301.png

8 ], S: W7 ]; U: Y  s
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。

/ F7 k% h- T' l" }: v3 S: o' K4 X: `6 u. L2 z2 D3 x. Y3 a

0 b* N9 G& A9 n8 x7 ^5 D( U/ @
存储单元:是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;

& Q) {# p% N+ w  W
电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;
, ?0 l# ?# T9 O/ c( S9 i0 N; Z
卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;
; U" p4 T- O& V: N$ i
接口驱动器:控制SD卡引脚的输入输出。

9 X) I5 o8 e6 R/ Z* k9 A. @6 U. j
: ?7 X# \4 y, i& \, l9 g) ?4 S
  • SD卡内部寄存器

    & c+ b- W+ L# i$ O9 A3 {
SD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。

% b# N( ]2 C; c8 I5 G  `3 T
微信图片_20230411192255.png

% ~5 N2 [+ Q; d
  • SDIO接口

    ; V6 G% F0 |" x- G; T8 P& V
SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡都有SDIO接口。STM32F407系列控制器有一个SDIO主机接口,它可以与MMC卡、SD卡、SD I/O卡以及CE-ATA设备进行数据传输。也就是说SDIO接口不仅仅只会操作SD卡,凡是符合SDIO接口通信的设备都可以被操作。

( ]/ G3 n9 _, y  p  S! F- C7 `# e
微信图片_20230411192250.png

! n' P9 T, i* X0 k
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 的通信模式。

& \4 {# V3 E6 H6 L" ^: L4 f; E
  • SDIO组成
    ) z" T, e0 H; P. y) A
SDIO 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令和数据传输。

* m! `4 V8 s# E. D; D* q  y  G
APB2 接口访问 SDIO 适配器寄存器,并且生成中断和 DMA 请求信号。

$ ]. G) S/ c5 Z( i: `6 ~- y
微信图片_20230411192246.png
& [. w  T; f8 n) Y
注:
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。
$ I! p6 L/ Q$ V$ b+ @
注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)。

$ Y1 c0 g, T6 y8 Q
微信图片_20230411192243.png
& b8 d! {5 l1 t9 }, W) c
该适配器由五个子单元组成:
1.适配器寄存器块 :适配器寄存器模块包含所有系统寄存器。
2.控制单元   : 控制单元包含电源管理功能和存储卡时钟的时钟分频器。
3.命令路径 :命令路径单元向卡发送命令并从卡接收响应。
4.数据路径 :数据路径子单元负责与卡相互传输数据。
5.数据 FIFO :数据 FIFO(先进先出)子单元是一个数据缓冲器,带发送和接收单元。FIFO 包含一个宽度为 32 位且深度为 32 字的数据缓冲器和发送/接收逻辑。(一共32 个单元,一个单元一个字)。所有的数据传输都要经过FIFO,便于管理。

' K# y% m0 K. p% M- J4 z
  • SDIO 命令简介

    - P+ D; Z4 p1 K5 C: j# ]8 q! y
SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。

- ?; z; [8 w+ h' g$ S' r- d& E8 x) h
SD命令格式固定为48bit,都是通过CMD线连续传输的,数据线不参与。

( z" k2 n0 L& F! a- t
微信图片_20230411192239.png
* s* u$ d5 B6 d5 o( i4 f) c$ h
SD命令的组成如下:
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。
/ Y) m& `, r1 f0 @% ]$ O; ~
- T7 ]% K# h# K6 _  X
传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。
+ x  [3 A( F# H3 M3 Y& ]
命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。
9 M/ `! q8 ?& N. k( \
命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。

3 C, F& H! ~  N3 r
地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。

2 Z! y: \- @7 Q
CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。

/ u6 ]# m0 E# L; A( l
SD命令有4种类型:
1.无响应广播命令(bc),发送到所有卡,不返回任务响应;
2.带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应;
3.寻址命令(ac),发送到选定卡,DAT线无数据传输;
5.寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。
1 n* I0 ^$ v" W0 x1 |, Y7 }
在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD),也就是说在64个命令作为常规命令的基础上加了特定的命令

2 V3 ?. R9 ~; j: ?) t& C
要使用SD卡制造商特定的ACMD命令如ACMD6,需要在发送该命令之前无发送CMD55命令,告知SD卡接下来的命令为特定应用命令。CMD55命令只对紧接的第一个命令有效,SD卡如果检测到CMD55之后的第一条命令为ACMD则执行其特定应用功能,如果检测发现不是ACMD命令,则执行标准命令。

- Q& B2 x5 g3 j9 _# f
SD命令响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:

% L6 B- O; B1 ?2 w7 A1 P
lSDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。

! y* I  l' o6 ^
SDIO读数据

6 c( }0 w- p" ?, x2 L/ y
微信图片_20230411192236.png

: F# _; u7 t- J0 I' a( E
单个块读操作与多个块的读操作除命令不同外,还体现在读操作结束时,单个块读取一个块自动结束,而多个块还需要主机发送停止命令。写操作类似,也要多发一个结束命令,只不过写操作写数据前需要检查卡的状态是否为忙状态。
+ [; G0 W% G$ J. G% q5 D, \
SDIO写数据
& H' h% }: \0 e5 e; x
微信图片_20230411192231.png
- ]% I' ]. e* {) g% C' G) M
  • SD卡操作模式

    3 B! e, Q2 `$ P8 \. k
SD卡有多个版本,STM32控制器目前最高支持《Physical Layer SimplifiedSpecification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。

5 ~0 {- U1 M5 E' Z" M
SD卡系统定义了两种操作模式:卡识别模式和数据传输模式。
' A; U' ]; `: Q" P3 G/ K& W; T- P! G
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。
* l/ C$ M; i" E# u2 \# R9 f& Q
每个不同的操作模式下,SD卡都有不同状态,通过命令控制实现卡状态的切换,在不同的状态下做不同的事,比如在发送数据前需要SD卡处于传输状态,发送数据时,SD卡处于接收状态。
" L% C7 v- z0 h! E
微信图片_20230411192227.png
( f9 H+ Z) g5 v4 M( }
  • 卡识别模式
    ' G6 D0 _- L( Q% i+ i
微信图片_20230411192220.png

" P  O3 h" ^- z" i& r
①上电后,主机发送CMD0让所有卡软复位从而进入空闲状态。
②主机发送CMD8确定卡的电压范围,并识别是否为2.0的卡
③主机发送ACMD41识别或拒绝不匹配它的电压范围的卡,SD卡需要工作在特定的电压范围之内
④主机发送CMD2来控制所有卡返回它们的卡识别号CID(128位)
⑤主机发送CMD3命令,让卡推荐一个RCA(16)地址作为以后通信的标识,之后都以RCA值作为身份标识进行信息交互。
4 n  V+ v5 ]$ M! ^0 P$ \) X
注:在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下,在SD卡初始化时,SDIO_CK不可以超过400Khz

/ r7 M6 G* Z6 I& c& M8 |) B, L# a) L
  • 数据传输模式

    * w+ o  i8 V4 _1 g8 c' M
微信图片_20230411192214.png
" ?* ~/ _& r& c* T& ^
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。通过CMD7命令加上RCA值来选定指定的卡,选中后SD卡进入数据传输状态,就可以发送CMD17读单个块,CMD18读多个块,读多个块时只有发送CMD12命令才会停止。SD卡再次进入传输状态,若不想对卡有任何操作可以再次发送CMD7命令加上RCA值来取消指定的卡,写操作与上述原理相同。

, i. s# z8 \" G
  • SD卡普通模式操作实例

    / b" L1 a5 w1 s9 u) i- U- Q& O
实验内容:向SD卡写入数据后读出
* C6 U: b# B+ G0 C7 o/ d; U6 [- G$ A
实验步骤:

7 T' \, k; a- ~# F: v) m% h6 G5 N
1.配置RCC,与以往不同的是SDIO适配器的时钟是单独配置的,需要专用的SDIOCLK,标准工作在48MHz
3 s8 s/ y3 q% z7 b3 ~
微信图片_20230411192210.png

7 s+ C8 A" I9 V: w" X+ t2 F: O( R' m. p
2.配置SDIO
( o: S: B1 v) Y" n  z5 ^* y
6 p) P; b& u- c1 E. g! D
微信图片_20230411192207.png
% f9 W! X* e1 }) G1 m) n
微信图片_20230411192203.png

% I! z0 Z1 V9 D/ l% `' ]
3.编写代码
  1. ; `6 V6 P" ]3 m
  2. //mian.c
    $ n% T) e$ V' t3 G
  3. #include "main.h"
    5 t( t( n0 J* X  m$ D/ E
  4. #include "stm32f4xx_hal.h"
    # }. b7 @$ g% O9 Z8 Y' w
  5. #include "sdio.h". T8 x3 O' d) F0 _# z
  6. #include "usart.h"
    4 g( L  s: d: U& Q7 C! ^6 d" I
  7. #include "gpio.h"
    / e* w8 Y. [5 \( p4 F
  8. #define SDBUF_SIZE   1024" O& x+ z5 K+ W" K
  9. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收" T3 y8 d& i) y  ?+ y# ]& l/ ?8 F
  10. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出* o3 }6 M! q: m& R7 m0 Z
  11. int main(void)
    6 U% b4 q/ m# k) s' ~$ l/ s8 W
  12. {
    ; ^1 E. r0 a" H" u1 r. o+ r4 J
  13.       uint32_t i;
    4 ~! i; n9 [* W1 y+ ^
  14.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息5 o# F" O: D6 X, Z
  15.       HAL_Init();& D( [/ U- v. Q% e2 }+ Q
  16.       SystemClock_Config();: U/ w% n, l& m1 a: [+ W: O+ L9 H0 c
  17.       MX_GPIO_Init();
    " H, @% y! \: H, J
  18.       MX_SDIO_SD_Init();# C7 j* C' `( K5 G6 B( _
  19.       MX_USART1_UART_Init();
    ( h+ x! \9 d! J% w( u
  20.       printf("this is sd test\n");
    " h! t3 _3 X% X0 |+ a
  21.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印+ E$ P* S5 l" _7 B
  22.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);3 C( ~- j* B: v, [; f
  23.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);
    # {. n  W. d  t; j3 j
  24.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
    ) O' i/ V/ o4 k' q% R, I
  25.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
    + X( I. P! @: u, f) B7 M& i  P' [
  26.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
    8 V9 [% Z& w5 U" ]% C' T7 R5 F# U3 A
  27.       /*--------------------SD卡写测试----------------------------------*/
    3 {8 \" n1 R- N! n- Q7 z
  28.       memset(SDBUF_TX, 0x8, SDBUF_SIZE);  /、填充TXbuf为0x8
    + _' S7 q* B% ~6 M: @7 h: t) S( q& v0 K
  29.       /**9 N0 P; |" X! Y1 ^" {
  30.          //函数功能及参数描述                             . e1 g4 L) X# q, [
  31.         *HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData,
    " Q7 \; \! \4 q  z
  32.                       *uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
    * H3 }- E: E$ I1 i# W; b
  33.         * @param  hsd Pointer to SD handle4 R! ?+ W& n) ]6 d; h
  34.         * @param  pData pointer to the buffer that will contain the data to transmit. ]9 G: ]1 E' |8 G; [* }
  35.         * @param  BlockAdd Block Address where data will be written,从哪一个块开始写  / N" G! M, g' E) Q2 @
  36.         * @param  NumberOfBlocks Number of SD blocks to write   写几个块" I1 I; L1 T) n+ [, x, a9 N
  37.         * @param  Timeout Specify timeout value 超时时间8 Z! m- H: ~6 h6 j7 O) h/ S" J
  38.         * @retval HAL status
    $ \! f: M7 R1 F6 ^
  39.         if( HAL_SD_WriteBlocks(&hsd, SDBUF_TX, 0 , 2, 1000) == HAL_OK)
    ( E6 a; Q) h+ n( b( }) b2 f2 O7 k5 J
  40.         {
    * ]  r3 C+ A+ }  J7 p6 `* F
  41.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//处于传输状态退出6 |( e0 p1 Y& k) U- W
  42.             printf("WriteBlocks Successfully\n");9 i! Z$ n4 `. n0 Q  j# U, G$ T) Q( ]
  43.             for(i=0; i<SDBUF_SIZE; i++)
    9 F0 @/ W4 J; t
  44.             {, a6 J+ l+ I1 c- [
  45.                 printf("%d ",SDBUF_TX[i]);
    - y* n8 ?, a  h" C- e
  46.             }
    ; l) e4 T3 A' d- N( f
  47.             printf("\r\n");. C1 r( P, `5 h
  48.         }
    1 c9 Y% U) ^+ X6 _7 ?: S
  49.         else. ^/ d- A+ W6 D
  50.         {7 B5 }1 B0 m! Z: P# @$ |) G
  51.             printf("WriteBlocks Failed\n");# \' s/ j% g  Q
  52.         }
    4 A9 j( u$ T" b( q" N9 p
  53.         /*--------------------SD 卡读测试----------------------------------*/
    , Y) o0 j1 E; ^: p5 p" Q8 o
  54.         //与HAL_SD_WriteBlocks函数的参数功能相同3 o1 ?0 U; y) A! I/ v5 u, J) n. e
  55.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
    2 q! y6 _/ @; k
  56.         {
    ( f7 a6 @' F' z$ S  w
  57.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//返回到传输状态退出
    - E( ]) t8 r6 C6 x$ L5 v# g
  58.             printf("ReadBlocks Successfully\n");
    ( S6 X) f. U) E3 \: |
  59.             for(i=0; i<SDBUF_SIZE; i++)
    % V/ w% w# h$ X2 R$ H
  60.             {
    " m! R1 ^3 Z, d) y. ^1 ~
  61.                 printf("%d ",SDBUF_RX[i]);  R6 W( r0 u% ~/ o" F3 V6 \: i
  62.             }$ u- T* P. F  w
  63.             printf("\r\n");" M6 J/ [+ x/ h, q
  64.         }( L  K* e) e2 Z" X6 j* m, [
  65.         else6 D  ?1 ~+ M4 A, N
  66.         {! |6 @# n" E( z
  67.             printf("ReadBlocks Failed\n");
    . c3 B) e7 G+ ]1 D+ b. |8 {
  68.         }
    / ^6 X) I) \7 J
  69.         /*--------------------SD 擦除测试----------------------------------*/
    . {( m! g, A& v6 v, w  F0 D
  70.         if (HAL_SD_Erase(&hsd, 0, 1) == HAL_OK )& A7 X7 \# E( U' J  u7 B: `
  71.         {
    3 @2 F8 p8 O+ w
  72.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);% D$ j. \1 R; X3 ]8 {
  73.             printf("SD_Erase Successfully\n");
    * @0 N9 W6 ~6 I+ `
  74.         }: C3 H  I& V- R- n/ c( I. Z
  75.         else8 {: c3 z* J; {' F3 }. P
  76.         {
    3 Y  P- q2 ]5 M5 b, X
  77.             printf("SD_Erase Failed\n");
    8 ~8 m: v1 z4 ^& C$ x
  78.         }+ H' T0 U2 C" }3 O
  79.         /*--------------------SD 卡读测试----------------------------------*/
    ; n( ]- X4 Q) O
  80.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
    + w2 C1 M- g, |( j& r
  81.         {" l! k* C- W' q) M
  82.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
    % L* M8 ^9 A. a3 t8 Q, U4 B
  83.             printf("ReadBlocks Successfully\n");
    7 k- N/ `9 F+ m/ U6 u2 }4 ]% ~
  84.             for(i=0; i<SDBUF_SIZE; i++)- h* X& [9 ^+ L. c5 i# M& \8 u
  85.             {
    : F6 B, a1 M8 q) G5 S8 E
  86.                 printf("%d ",SDBUF_RX[i]);
    : d3 `' A1 s2 ]4 Q! C! ], s
  87.             }+ d$ U' }% l5 K  u9 o7 ~( r
  88.             printf("\r\n");" B1 N; X9 [2 i$ e. `8 E! D
  89.             }# X! m% U" \1 B! g* ?/ z- i
  90.         else
    9 G) S! P8 G8 S
  91.         {( n% W. z2 U& \$ Y- Q
  92.             printf("ReadBlocks Failed\n");
      b! q/ b  ]: p- F4 U2 I2 q9 v* M
  93.         }* w1 l+ s" N5 p+ Z
  94.         while (1)( ~% v9 _, `5 B) ]
  95.         {
    & V* g" H# b) N( Y
  96.         }  1 g3 Z, s% X" t0 t
  97. }
复制代码
  1. //sdio.c
    : _& x8 r  I6 o5 m) [
  2. //此代码为工程自动生成,非用户编写,这里只做分析
    ! Z+ v! u3 o$ w# e, Q' ]
  3. #include "sdio.h"% P$ d4 L1 |: M: r" Q8 j. u) @
  4. #include "gpio.h"
    + H9 D# z6 n5 h8 h
  5. /* USER CODE BEGIN 0 */- G$ z2 [5 d# C7 W. j, A4 O0 z% r5 u
  6. /* USER CODE END 0 */
    2 ^# X. T  D$ B, x
  7. SD_HandleTypeDef hsd;
    7 P! i+ `+ Q, `
  8. /* SDIO init function */: R7 o, i1 S8 x
  9. void MX_SDIO_SD_Init(void)   //初始化配置
    2 ?* D7 N0 E6 T0 \1 O
  10. {
    5 E9 X6 M( ^! w  v* W9 i
  11.   hsd.Instance = SDIO;    //SDIO句柄,整个系统中只有这一个2 _( _+ h9 J% X6 J
  12.   hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
    . J& ^; d3 N+ ~$ V2 }. C6 |' ~
  13.   hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
      m- C) o3 ?) c+ g- L; {, J
  14.   hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
    ; e0 R5 @: t2 q( W+ {4 j/ w8 R
  15.   hsd.Init.BusWide = SDIO_BUS_WIDE_1B;   //初始化时设置数据宽度为1位
    9 W+ x, _# _9 B0 |8 j! b
  16.   hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
    3 ~  }( n. i2 O) h
  17.   hsd.Init.ClockDiv = 0;
    , {  U7 q( a2 q; G6 y
  18. //HAL_SD_Init()函数内部做了很多事去识别SD卡,具体的卡识别代码分析在下方  U7 N$ g, I7 m- k3 p# K
  19. if (HAL_SD_Init(&hsd) != HAL_OK)
    8 Y2 j: t- y' K# @! P* w$ x
  20.   {
    6 |+ L& N5 j) I+ K& [; {
  21.     _Error_Handler(__FILE__, __LINE__);
    ! S  m9 q  T: J- A7 I! e: I
  22.   }
    3 \0 e( D% t; ?4 Q2 A' e
  23. //初始化结束后重新配置数据宽度为4位
    ( P% `; R  }2 z$ v  ~: V
  24. if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) , A- J% g# X" A. ]- @
  25.   {
    0 }: m4 }1 T* N8 s8 e( n
  26.     _Error_Handler(__FILE__, __LINE__);
    % l1 z# _: O3 w5 T, P; T
  27.   }' u4 |0 |6 ~7 ]* C4 V1 R1 h
  28. }
    3 M9 C% L- h- b/ V
  29. //卡识别代码如下
    $ U! P+ T7 x5 h" u8 s/ B6 n2 a8 a
  30. HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd)2 q# Z" {. y6 ]" ^7 T7 X0 j3 [
  31. {+ P, ^+ |( y4 m: |1 ~3 M: J6 A
  32.   uint32_t errorstate = HAL_SD_ERROR_NONE;$ O/ N. Z5 ~7 _$ j+ G
  33.   SD_InitTypeDef Init;- A, f3 g% [/ m3 f
  34. /* Default SDIO peripheral configuration for SD card initialization */
    * C) O/ N! T9 @( Q7 |2 n$ ?; G! l
  35. //完成卡识别$ m7 k9 l( Q7 u
  36.   Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;: j( c6 n1 E, ~" ?
  37.   Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;
    9 }, e$ j5 g1 n$ b
  38.   Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;, J" D2 X8 i- P# |6 |" @5 ?
  39.   Init.BusWide             = SDIO_BUS_WIDE_1B;+ k' s+ a( U( Q1 y( `0 l& I
  40.   Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
    ; D' R! _( @& L& ]6 i9 M' Q3 y
  41.   Init.ClockDiv            = SDIO_INIT_CLK_DIV;
    2 z, v& A! p& X" e  w# c& f
  42. //SDIO_CK卡识别阶段要求时钟小于400KHz,宏定义
    1 Q  }. z; ?- F* E
  43. //SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV)8 G3 Y; S$ s2 w4 n( V2 _
  44. /* SDIO Initialization Frequency (400KHz max) */
    / d* |$ o$ F! k0 m, a5 c0 |
  45. /*#define SDIO_INIT_CLK_DIV     ((uint8_t)0x76)*/
    6 {+ d3 u. S- T
  46. /* Initialize SDIO peripheral interface with default configuration */
    4 v1 n' R, H2 {, G/ a- a. x
  47.   SDIO_Init(hsd->Instance, Init);
    ' h& ?1 }" {- i4 z2 i
  48. /* Disable SDIO Clock */
    # t* x% k# Z: P. h; A
  49.   __HAL_SD_DISABLE(hsd);
    2 C: k: e/ C) Q$ @: t2 d  M
  50. /* Set Power State to ON 对SD卡进行上电 */9 K- p/ J! ^3 |$ M
  51.   SDIO_PowerState_ON(hsd->Instance);
    . h% B8 l! d. I7 `& O( E4 C+ C8 v
  52. /* Enable SDIO Clock */
    + K: i0 b3 C2 l5 R9 _6 F* W- b
  53.   __HAL_SD_ENABLE(hsd);! c1 E$ w  n. G* ^, `
  54. /* Required power up waiting time before starting the SD initialization * S, |7 q  }  A; u! c9 r6 _; U$ Z& \3 H+ D
  55.   sequence */! A* B' n" }* O
  56.   HAL_Delay(2U);' v. R! [* V) ?; e: x
  57. /* Identify card operating voltage */
    ) h/ H3 I( e9 K% ?2 V
  58.   errorstate = SD_PowerON(hsd);' u6 j4 _9 M7 h( J
  59. if(errorstate != HAL_SD_ERROR_NONE)
    ; x4 E) A7 D, R
  60.   {
    5 w  {. J# p5 e6 r
  61.     hsd->State = HAL_SD_STATE_READY;, |* h: w* E8 j
  62.     hsd->ErrorCode |= errorstate;# E# [  @& J( k8 }7 @
  63. return HAL_ERROR;5 q3 G/ O+ S* X# _1 A1 a
  64.   }; r* K/ Y6 V: F4 |$ ?! K% i! e
  65. /* Card initialization */' u6 P) `* g3 {! T
  66.   errorstate = SD_InitCard(hsd);
    % f& b$ ^# `/ S  n3 l
  67. if(errorstate != HAL_SD_ERROR_NONE)- G6 p" g7 |! L% j% ]7 h$ `
  68.   {
      K! t+ O( s: B- e. x$ T9 |# H
  69.     hsd->State = HAL_SD_STATE_READY;% S% t" J8 I2 i2 h' C, _
  70.     hsd->ErrorCode |= errorstate;, _  ]1 Q5 ]2 T! B
  71.     return HAL_ERROR;0 d2 Y- U% Q( O4 ]5 l
  72.   }
    5 k( ^) v# r6 E$ B1 ~+ r4 P' E3 c9 V
  73. return HAL_OK;
    3 P5 j# R& j7 s% ^
  74. }
复制代码

# v" ~" S( f$ ?# A
  • SD卡DMA模式操作实例- d" `5 M. y4 F$ z3 o" J
    # D4 o* z7 f: J8 d" Q
当SD卡中有大量的音视频需要读取时,整个过程需要CPU干预,这样CPU的利用率就会降低,因此大量数据的传输最好启用DMA。

( k# G' v. Y& C$ D) j$ g* P
实验内容:向SD卡写入数据后读出。
2 X7 e# _* j; x8 N: q
实验步骤:工程的时钟配置和SDIO配置与普通模式相同,只有一点不同需要打开SDIO的全局中断,因为SDIO发送与接收完成后都需要中断去生成DMA请求。设置SDIO全局中断优先级更高一些,因为它内部还有很多其他中断,发生错误时更需要处理。
4 C$ `" L3 F  `/ e0 H# y- c
微信图片_20230411192200.png

: F2 K: r6 A  C8 x- t
配置DMA
- F3 F' `7 I/ ?; q9 X2 }
微信图片_20230411192156.png

; N8 L9 Q5 A8 y* X: \
编写代码:
  1. //mian.c
    $ w& b9 M" @( E  w& H& w
  2. / F6 I, V9 d# \/ _9 D
  3. #include "main.h". V% c8 r' r; V9 s
  4. #include "stm32f4xx_hal.h"
    * @# |9 p: F2 w- [: _. Q! _) p
  5. #include "dma.h"
    # E# a+ H0 [" i! A; `
  6. #include "sdio.h"  K5 H1 C+ P. K/ n- i# Z2 @; K( t( S4 {
  7. #include "usart.h"2 @+ G8 d9 l) a7 j
  8. #include "gpio.h"
    # p+ n* l, I" H

  9. : N2 V0 ~% J2 t) [  W# {2 j! T: C
  10. #define SDBUF_SIZE   1024# c. Q! w' R/ T  Q$ c$ \7 I# B4 y
  11. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收
    + D  L# j, j% H# T" o8 o- B
  12. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出
    0 ^7 j$ y, X6 C8 |. v. h4 w8 J
  13. uint8_t DMA_SEND_OK, DMA_RCV_OK;  z/ q# w  H3 @+ J$ j

  14. $ h3 ?8 ~0 {/ h
  15. int main(void)2 E- [3 v; G7 Z# d; W) l( O5 y8 d
  16. {
    ! g! x. x) g; }- H2 s  V
  17.       uint32_t i;
    7 y3 H7 ]0 ?, e. A
  18.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息( {5 D, @9 H0 {/ b
  19.       HAL_Init();$ i) q1 a2 _! C+ g2 F6 i# A( |  S
  20.       SystemClock_Config();
    * W: Y+ f0 d8 H/ R; B
  21.       MX_GPIO_Init();
      s$ W8 r4 f# h# ^" H6 k) B
  22.       MX_DMA_Init();+ N' p' k& @" B% W
  23.       MX_SDIO_SD_Init();" k5 A/ o2 y1 a& y8 J4 p# B
  24.       MX_USART1_UART_Init();
      I7 f" a% H4 N% V  U' I3 m
  25. 2 i( z/ O7 `8 [4 `$ b# L
  26.       printf("this is sd test\n");; s& h9 @* B. h2 Z! F# g' m+ z6 V& n
  27.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印
    % X/ n0 Q) T8 @' j- m! M0 n8 t
  28.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
    9 _9 Y+ ^: s9 L
  29.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);2 Q' w( F4 m, S5 f6 H
  30.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
    7 w) x; c* L8 B( K
  31.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数% f, M0 \6 k# Q" Z4 {
  32.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小1 m& j  S5 D# H, E3 g
  33.       /*------------------- SD DMA 写测试-------------------------------------*/
    : [0 z9 k" o4 {# V( |, J
  34.       memset(SDBUF_TX, 0x2, SDBUF_SIZE );
    8 B9 [; h' N3 n6 w" j6 I
  35.       DMA_SEND_OK = 0;  //设置发送完成标志位为0,完成时为16 N. z. X! b; }- H* A& n
  36.        /**
    - I6 I2 _% F# Y. |+ r: R
  37.        //函数原型
    % D7 P5 F9 t5 J4 \9 Z0 Y' I/ _
  38.        *HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData,
    / m# ?3 T1 q% f# `- V  c1 x- c
  39.                                              uint32_t BlockAdd, uint32_t NumberOfBlocks)
    : X5 J0 Y& p& x$ D0 k/ `5 Q
  40.       
    , [) v! B* X7 M+ A; N! `% u
  41.         * @param  hsd Pointer to SD handle
    ( y. ?* w% M# ^- S
  42.         * @param  pData Pointer to the buffer that will contain the data to transmit
    $ x8 ]  W8 ^; p+ t" V
  43.         * @param  BlockAdd Block Address where data will be written  从哪个块开始写9 j! [4 A% ^3 e: s( U
  44.         * @param  NumberOfBlocks Number of blocks to write  写几个块
    - o5 }7 S% F' t0 ~8 g5 R! [
  45.         * @retval HAL status
    ) X( |' L6 Y0 E; j
  46.         */
    , B; B4 \( h6 e8 i- S8 C9 J
  47.       " q9 l, M: ?0 A" \0 R
  48.       if( HAL_SD_WriteBlocks_DMA(&hsd, SDBUF_TX, 0, 1) == HAL_OK )
    2 Z8 [& {: }3 o% e7 c
  49.       {& ?" J4 K' y4 @1 m
  50.           //等待DMA传输完成,并且SD卡状态为传输状态
    0 ~7 n  ]) D: H6 o1 w3 a
  51.           while( (DMA_SEND_OK ==0 ) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));  9 u2 y# {1 _5 F( k- b
  52.           printf("WriteBlocks Successfully\n");
    ( ^$ W6 [" G' I
  53.       
    ( z7 ~' S. ?8 h* Q) p2 a
  54.           for(i=0; i<SDBUF_SIZE; i++)0 `3 }/ W% d% i9 b, z/ ^4 f
  55.           {* u/ {+ {$ n  b% R
  56.             printf("%d ",SDBUF_TX[i]);
    " U( t; b+ L; i7 F4 ]. c3 ^
  57.           }
    7 q1 r# s; n* v( @& [$ N; x
  58.           printf("\r\n");
    : J3 _6 ?& ^7 f" H  D' Y4 u
  59.           }% ^. e. o( D+ k6 L" L
  60.           else
    " L+ n: Y" D6 ~% Q8 M
  61.           {
    ! H- {9 \# H8 g$ v0 y7 q
  62.              printf("WriteBlocks Failed\n");9 w& m2 i2 g, b  T7 P
  63.           }
    0 w3 D1 d( q/ u; N
  64.       : z' \1 ?9 ?. Y2 L# L- I0 b6 J8 w" K
  65.           /*------------------- SD DMA 读测试-------------------------------------*/
    6 Z% S, F- R1 B$ t: Z
  66.           DMA_RCV_OK = 0;   //设置读取完成标志位为0,完成时为1
    . T3 A( U, s6 i( w. ?) \& r, Q3 L
  67.           if(HAL_SD_ReadBlocks_DMA(&hsd, SDBUF_RX, 0, 1) == HAL_OK)* |! Q  ?% m+ b5 n6 S& I
  68.           {
    8 x, W4 `2 d% \. B: j9 N& @7 T
  69.               while( (DMA_RCV_OK ==0) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));          ) U' J( o6 @! E
  70.               printf("ReadBlocks Successfully\n");         
    . n0 p: u# b( V0 A+ x- L/ h3 j
  71.               for(i=0; i<SDBUF_SIZE; i++)
    7 s. l  ]  N! I/ }; `  f7 r$ N
  72.               {6 }7 ~, u6 a( C! T+ e; _
  73.                 printf("%d ",SDBUF_RX[i]);
    . j/ e, |5 g. H. @8 r, ^
  74.               }
    . m1 W7 q$ Z' |9 n) Y
  75.               printf("\r\n");: [. c& k! c' j) Y0 q. p
  76.            }7 z6 X+ `- t; i. J
  77.             else4 k3 Y; L2 O# a7 K, M5 y! M/ m- U
  78.             {5 m" O, n( J6 S' V
  79.               printf("ReadBlocks Failed\n");1 W. A( a" ^' F
  80.             }
    8 [& [% c& z2 f
  81.             while (1){}6 ]! V) l7 X( s3 a1 E- }, k5 P
  82. }
复制代码

0 e+ U  {( Z5 M* r% S
  1. , S7 ?* P* d  M8 V. N/ p
  2. //sdio.c
    2 }2 d; d( G0 h0 C
  3. , F4 @4 o' n! Q* l7 {1 C1 k
  4. extern uint8_t DMA_SEND_OK, DMA_RCV_OK;
    ; y4 |4 E( ]* v
  5. //发送完成中断处理
    ) N! o% P, ~& \, g* c7 g) m2 g5 K
  6. void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
    2 ]9 ~4 ^: d2 S; V, x) r, \9 o9 o
  7. {2 k+ D- M# r6 Y5 r6 x  p; Z
  8.   DMA_SEND_OK = 1;  Z: B, h; ?0 P, R7 K& M$ K+ F
  9. }$ t. T) X( g* H) u" P
  10. //接收完成中断处理
    # d5 r& n" z6 W$ U( F
  11. void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
    # }/ d2 J7 |0 _0 H9 k# P& ^' k
  12. {6 i# C" G# [# w8 ?5 r! Y
  13.   DMA_RCV_OK = 1;- r* I8 k9 P+ [- N  v6 G6 Q$ V' |
  14. }
复制代码

' }* `3 q/ r  O9 i* X% s至此,ARM开发进阶知识已全部更新完毕!下一系列介绍物联网操作系统开发!
  _7 [% R  W5 q" l6 J( ]
0 ^, J; N3 M, \4 Z

, |# F7 u0 c$ a: _) B" x转载自: 骆驼听海$ S* t  _5 K9 j1 ]* a1 p) F
如有侵权请联系删除  T8 D- l8 d6 J5 A5 w, q+ `
赞 收藏 评论0 发布时间:2023-4-11 19:23

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版