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

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

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

    1 \0 E6 j. W- n' E5 }
SD非常小巧,可以嵌入到设备中,在嵌入式开发中,产品需要存储一些容量叫大的文件,就可能会运用到SD卡。SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示。

- t6 a% o8 w/ l
微信图片_20230411192315.png

( Q8 `8 O2 o6 V5 O
容量大小不同的SD卡,其内部的操作标准是不同的。很多外设会明确告诉用户本设备最大能够支持容量多大的外扩,不能无限制的扩存,因为容量越大,操作不同,对控制器的要求比较高。当前设备的控制器不足以支持大容量,所以有外扩限制。
4 g' A7 }0 d( D( j" ?
微信图片_20230411192311.png

) \, ~# `) t" i9 o4 @
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。
- Y/ k! \7 o1 m& I
" [$ E4 |7 |2 c+ d
微信图片_20230411192306.png
3 F1 R( c. p% L. x
! m- a: k) ^8 P5 j6 x6 S. o
  • SD卡的物理结构

    ' `$ t- g/ h+ }/ t; C2 H- w& ?$ D* m7 l
  o7 L! `" `% E+ U: V
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。

( T6 K4 m8 x* |7 e1 |% S5 O4 ^% T
微信图片_20230411192301.png
* y% k: q$ J, B. w( v1 B9 s
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。

( I/ |  `6 }" `* g3 J% t  B- ~% f+ o# u+ N

" E- v! K, f4 J! c
存储单元:是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;

8 z( {2 }6 x& q5 h8 c
电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;
5 D/ `  y- g! W7 ^
卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;
4 h5 j# Q& }, V9 n; f4 W; _
接口驱动器:控制SD卡引脚的输入输出。
  q" J# H+ k& D+ W7 n% t) k

4 {9 O; U6 n) l' n1 m. V
  • SD卡内部寄存器

    4 U$ U9 F9 o4 S2 Q6 D( h
SD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。

% t3 C- o6 T2 {  q! Z' E3 J: [
微信图片_20230411192255.png

9 e- k  Q- B9 l" X+ ?
  • SDIO接口
    7 n5 N% _# r/ ?, f+ w
SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡都有SDIO接口。STM32F407系列控制器有一个SDIO主机接口,它可以与MMC卡、SD卡、SD I/O卡以及CE-ATA设备进行数据传输。也就是说SDIO接口不仅仅只会操作SD卡,凡是符合SDIO接口通信的设备都可以被操作。
# r3 q; N0 c6 U1 r' X* x$ [
微信图片_20230411192250.png

; V# f  T! }, f7 }9 v
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 的通信模式。
" t, y1 Y7 @& M$ ^, X
  • SDIO组成
    3 f6 \' n" E* K; x6 y$ ~+ t
SDIO 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令和数据传输。

, B& O! B- V2 e
APB2 接口访问 SDIO 适配器寄存器,并且生成中断和 DMA 请求信号。

& _7 g  O/ ?5 S! U% P3 R1 k
微信图片_20230411192246.png

+ l6 U; J: D8 ^! ~+ i
注:
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 ?0 z) e. h6 [, A$ s  k/ f  t
注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)。
9 S  O$ z  _3 K7 [. P% _) u& t. }
微信图片_20230411192243.png

& Y& R$ b+ ]1 G& |( K$ b! B
该适配器由五个子单元组成:
1.适配器寄存器块 :适配器寄存器模块包含所有系统寄存器。
2.控制单元   : 控制单元包含电源管理功能和存储卡时钟的时钟分频器。
3.命令路径 :命令路径单元向卡发送命令并从卡接收响应。
4.数据路径 :数据路径子单元负责与卡相互传输数据。
5.数据 FIFO :数据 FIFO(先进先出)子单元是一个数据缓冲器,带发送和接收单元。FIFO 包含一个宽度为 32 位且深度为 32 字的数据缓冲器和发送/接收逻辑。(一共32 个单元,一个单元一个字)。所有的数据传输都要经过FIFO,便于管理。

8 f1 B( u; n- p# n2 S1 |, o5 Q
  • SDIO 命令简介
    $ J! V6 t; o; V2 a1 N+ p
SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。
* l/ D+ h( a0 p0 I2 c
SD命令格式固定为48bit,都是通过CMD线连续传输的,数据线不参与。

& `& N8 `. h* e/ Y: m4 U
微信图片_20230411192239.png

4 d3 k% ?+ W6 a. g( j! i
SD命令的组成如下:
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。1 d2 j) [1 @7 c, Z" H6 m2 o& ^. C

( a' x7 \1 t. [4 b
传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。
  e8 c# P$ k4 ]9 x6 M
命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。

& b7 w/ R+ }5 L# B, j& }6 f
命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。
( D4 D2 Q- e8 a' t" g; k
地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。
/ s# @/ p) J7 S! Q# N
CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。
! b5 ]0 k4 H* r& F9 ^0 e- v
SD命令有4种类型:
1.无响应广播命令(bc),发送到所有卡,不返回任务响应;
2.带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应;
3.寻址命令(ac),发送到选定卡,DAT线无数据传输;
5.寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。

2 L3 B) D- u. D' a2 T% F
在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD),也就是说在64个命令作为常规命令的基础上加了特定的命令
2 u) Z# W2 ~6 H, ]5 B
要使用SD卡制造商特定的ACMD命令如ACMD6,需要在发送该命令之前无发送CMD55命令,告知SD卡接下来的命令为特定应用命令。CMD55命令只对紧接的第一个命令有效,SD卡如果检测到CMD55之后的第一条命令为ACMD则执行其特定应用功能,如果检测发现不是ACMD命令,则执行标准命令。
2 a& U: n4 G: v, O$ p* f
SD命令响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:

7 e7 A  V( D& V- a
lSDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。
( P' s: y/ o& ^' I, K2 J8 g) ?
SDIO读数据

: @% F1 `$ o* Q
微信图片_20230411192236.png

+ H" i- [4 |5 ]- J% h
单个块读操作与多个块的读操作除命令不同外,还体现在读操作结束时,单个块读取一个块自动结束,而多个块还需要主机发送停止命令。写操作类似,也要多发一个结束命令,只不过写操作写数据前需要检查卡的状态是否为忙状态。
  y* e( W( V( y
SDIO写数据
6 d6 q! V" X9 q7 |
微信图片_20230411192231.png

1 E" i! w- Q+ P/ m3 k5 T! H
  • SD卡操作模式

    ( F! G! y/ Q* ^0 u
SD卡有多个版本,STM32控制器目前最高支持《Physical Layer SimplifiedSpecification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。
& F! u0 m4 @& c4 ?  n) H  N
SD卡系统定义了两种操作模式:卡识别模式和数据传输模式。
  C2 e! o& [  D9 v' R& v
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。

) t3 ~! O& ]+ E- E$ n
每个不同的操作模式下,SD卡都有不同状态,通过命令控制实现卡状态的切换,在不同的状态下做不同的事,比如在发送数据前需要SD卡处于传输状态,发送数据时,SD卡处于接收状态。

6 A* K' N, \% _! R. i+ ?6 }( q
微信图片_20230411192227.png
& |) F8 U! @/ I+ c( J" X
  • 卡识别模式

    ) M, e5 p' F: M9 Z: C" O' g: X
微信图片_20230411192220.png
4 I+ L. ~! i. r* n$ J
①上电后,主机发送CMD0让所有卡软复位从而进入空闲状态。
②主机发送CMD8确定卡的电压范围,并识别是否为2.0的卡
③主机发送ACMD41识别或拒绝不匹配它的电压范围的卡,SD卡需要工作在特定的电压范围之内
④主机发送CMD2来控制所有卡返回它们的卡识别号CID(128位)
⑤主机发送CMD3命令,让卡推荐一个RCA(16)地址作为以后通信的标识,之后都以RCA值作为身份标识进行信息交互。

+ y/ z! Y! r' h# H6 `  g% T( x$ n6 Q
注:在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下,在SD卡初始化时,SDIO_CK不可以超过400Khz
4 `2 K9 D! o6 V0 N1 Q
  • 数据传输模式

    4 Y8 P" X+ ?8 p# Y8 D& F; V
微信图片_20230411192214.png
& n8 j0 M2 [# K% o2 B. F
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。通过CMD7命令加上RCA值来选定指定的卡,选中后SD卡进入数据传输状态,就可以发送CMD17读单个块,CMD18读多个块,读多个块时只有发送CMD12命令才会停止。SD卡再次进入传输状态,若不想对卡有任何操作可以再次发送CMD7命令加上RCA值来取消指定的卡,写操作与上述原理相同。
" S# O. w; r5 r# @, o
  • SD卡普通模式操作实例
    5 N$ x6 P; \" n1 b
实验内容:向SD卡写入数据后读出
& K# t7 v$ `( [4 E1 {' q3 }! ~& P
实验步骤:
6 {3 P' z* z: Z% p) S/ K+ o8 R, K
1.配置RCC,与以往不同的是SDIO适配器的时钟是单独配置的,需要专用的SDIOCLK,标准工作在48MHz
8 }# Z( P+ h$ o) x. F- {4 X# \
微信图片_20230411192210.png

, e" m/ U& C2 [. c5 e7 G2 ?
2.配置SDIO) {: [' c2 N# a. P5 n2 T
0 R% @  s% C  ^- W" s8 G, B+ ]
微信图片_20230411192207.png

% @) s, v, [. _; q
微信图片_20230411192203.png

5 q4 y1 ^; q/ B3 n; Z
3.编写代码

  1. 6 f. t" Y4 H2 D1 V& h  k( W
  2. //mian.c
    ' F, [" t+ D& u9 D" g3 \  z
  3. #include "main.h"
    0 d* _7 f4 H2 P3 B4 ?
  4. #include "stm32f4xx_hal.h"7 t5 L! I7 }/ G8 ]. J3 [, s; p' ]
  5. #include "sdio.h"' }7 u" c, t) \7 q$ |
  6. #include "usart.h"
    . c9 X; |0 n4 a
  7. #include "gpio.h"
    : e5 r' p- Z$ Q: v6 B
  8. #define SDBUF_SIZE   1024
    . L) {2 j& O- U% j( n
  9. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收
    - L: N$ c. O' Q
  10. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出
    1 t) v/ p1 a4 q6 G% z
  11. int main(void)2 ?. U# c/ G7 [' d3 `! J( F/ \3 c6 S
  12. {0 A/ p+ k8 r% o3 U1 F6 N
  13.       uint32_t i;
    * \0 i0 A. S4 g6 a, \
  14.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
    ' h- g  ]8 w$ n3 S" D  _
  15.       HAL_Init();2 B% D% w4 r4 c9 X3 f( ^9 `' b6 [
  16.       SystemClock_Config();
    % Z& J0 C1 q( x' \+ [( t
  17.       MX_GPIO_Init();
    - P. n: p. c( D& s" {
  18.       MX_SDIO_SD_Init();
    , r: l" F( p7 h6 _7 V
  19.       MX_USART1_UART_Init();  E+ P+ y) ?9 o8 j
  20.       printf("this is sd test\n");! Z' c0 J% \. Q0 v' a2 a+ s8 ?
  21.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印. a3 m  ?+ i% v: c' S
  22.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
    4 w- n0 a& d) c( ]  J2 w
  23.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);& G, h+ p# P4 h  M3 h1 F6 f( W
  24.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本7 q+ T+ C0 x# L7 b; W+ K% v
  25.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数( H$ i/ A3 s* b+ p
  26.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
    # A8 O# ^0 R+ ~3 x
  27.       /*--------------------SD卡写测试----------------------------------*/4 C$ l2 S1 y0 m# J* Z( q
  28.       memset(SDBUF_TX, 0x8, SDBUF_SIZE);  /、填充TXbuf为0x8- F- e# P3 {* Y* a+ |! C5 o
  29.       /**
    % X! W% }0 g" _0 \  E
  30.          //函数功能及参数描述                             ; h* q7 r/ }" Q3 m
  31.         *HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData,
    0 T" p6 }% v8 O# J4 Y' R: i% u$ m
  32.                       *uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
    / [: O# }" q1 |# R# D
  33.         * @param  hsd Pointer to SD handle/ m3 k( F7 O' M$ I; V1 V# o
  34.         * @param  pData pointer to the buffer that will contain the data to transmit
    / B+ C! l7 y4 H$ ^1 R6 N- T2 d0 ?
  35.         * @param  BlockAdd Block Address where data will be written,从哪一个块开始写  6 {/ a+ C' Q6 F, y& q0 d" `, Y
  36.         * @param  NumberOfBlocks Number of SD blocks to write   写几个块
    ; l, u( H, o" K( ~4 d
  37.         * @param  Timeout Specify timeout value 超时时间' s* @, U* P2 ^6 O: Y( i
  38.         * @retval HAL status5 ?* u8 U# [/ T8 n! \3 C" U6 F, u
  39.         if( HAL_SD_WriteBlocks(&hsd, SDBUF_TX, 0 , 2, 1000) == HAL_OK); [$ M# u9 y  C! M0 y! T
  40.         {
    + A6 h: l, g1 v' V- y& N% I5 j
  41.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//处于传输状态退出  I5 _. k% t: C7 ?4 G2 z
  42.             printf("WriteBlocks Successfully\n");) r1 v' Q$ o# Q3 M$ [. \* l
  43.             for(i=0; i<SDBUF_SIZE; i++)+ G! j' V1 q* ^5 W; u6 q
  44.             {  r$ R+ U, C  ?6 e" ]
  45.                 printf("%d ",SDBUF_TX[i]);1 u. `7 J7 \1 J: m
  46.             }
    7 G. {+ w4 Z. Z& p  {' ^& A1 s
  47.             printf("\r\n");
    ) B/ ?: f* g2 a* P$ T/ h
  48.         }
    : [7 W8 |! \  [) {6 c6 j  Y
  49.         else
    ! e% i" C9 c+ u) s$ l) L% G
  50.         {7 m+ Z5 Q9 n8 L9 t) ]
  51.             printf("WriteBlocks Failed\n");7 s# M, _/ z: B2 J
  52.         }
    7 |$ v5 o  [7 J5 v2 `' a$ P& u
  53.         /*--------------------SD 卡读测试----------------------------------*/9 I3 H" c; J" t; Q. a% t
  54.         //与HAL_SD_WriteBlocks函数的参数功能相同. D6 U3 u% q7 f$ p5 _5 I. M( u
  55.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
    $ C9 C( O6 P( \6 s& U, X, Q
  56.         {; z3 `+ \; T( [8 c) c
  57.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//返回到传输状态退出
    # _- N, s$ [2 e& L
  58.             printf("ReadBlocks Successfully\n");
    + ]) Q# d2 G. E' w) Z' y: V
  59.             for(i=0; i<SDBUF_SIZE; i++); H/ c& G( {2 I
  60.             {
    3 r' `$ H. ^0 w% r
  61.                 printf("%d ",SDBUF_RX[i]);
    / w& _( `0 q( S3 v. o& A, a
  62.             }! }4 h( M- \3 m6 l* A6 z: n7 J9 H9 O. O
  63.             printf("\r\n");5 a( F  ~0 `( M$ a3 i' z
  64.         }
    1 m9 a# j+ B. N. f5 h
  65.         else5 U* x) Z( W: R! O0 R) `1 ~
  66.         {1 r0 o9 F7 m7 g5 ?
  67.             printf("ReadBlocks Failed\n");
    ! C6 T! z4 ]- d* m: u
  68.         }
    6 R# L" @' S3 Z. O7 H# [0 v6 H9 ^
  69.         /*--------------------SD 擦除测试----------------------------------*/
    0 u  D- e( O3 A' x% B7 I3 N' k
  70.         if (HAL_SD_Erase(&hsd, 0, 1) == HAL_OK )
    ; @' N2 A7 A- e8 B- `6 i/ q. e
  71.         {, x6 e8 @- T$ k& w6 A
  72.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
    2 {9 P% h3 w$ n2 b
  73.             printf("SD_Erase Successfully\n");
    1 Y2 {. w" p# i; K% \
  74.         }' y( Y- {3 P1 V  ^4 e- k  ^+ {
  75.         else
    3 E* K$ g0 H, T4 Q# M' e
  76.         {
    # b0 C; J" Z2 U* Z; o- i
  77.             printf("SD_Erase Failed\n");3 ^! W0 o1 @9 o! T4 }0 A
  78.         }
    / J# S% v* {; X$ }6 B( \! R5 L
  79.         /*--------------------SD 卡读测试----------------------------------*/
    8 v0 L' Y/ d  V: l' F2 c' R
  80.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
    ( z( m% f$ R" [& ?9 c4 o4 m
  81.         {
    & g' k. ~' z" S4 P+ R0 B
  82.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);- d1 f  y9 U8 o3 t0 t$ P  |' W
  83.             printf("ReadBlocks Successfully\n");
    " H$ @" @$ _$ C/ }
  84.             for(i=0; i<SDBUF_SIZE; i++)
    0 n) R9 w4 o3 {1 Q: k/ O
  85.             {
    # D, T; i! O  k0 G) H- u' \  M8 m
  86.                 printf("%d ",SDBUF_RX[i]);( U6 ~) e8 u6 l
  87.             }
    $ \- g; R7 Q' m7 ?  E
  88.             printf("\r\n");
    $ m2 Y/ Y7 q+ _
  89.             }0 T( H+ b( u3 z! N* Y/ v; G+ U
  90.         else5 j/ }) R; C; B$ |& o- I
  91.         {  ?/ t8 ^) O0 b* V+ K
  92.             printf("ReadBlocks Failed\n");9 O1 ]( r: a# x6 h; D
  93.         }
    " E4 G) W8 h. Z) @0 X
  94.         while (1)
      a3 Q: q1 ^2 k5 W1 p! x
  95.         {6 J  j: e7 T; t# O) ~
  96.         }  
    - _( Y/ c2 Y& [3 U/ ^8 P0 @5 p
  97. }
复制代码
  1. //sdio.c, a& Y" X1 S+ S  W7 U
  2. //此代码为工程自动生成,非用户编写,这里只做分析7 s* U  ]/ x6 r1 I+ `5 k
  3. #include "sdio.h"
    ; r( Z( c& [9 r# V; v, i/ B
  4. #include "gpio.h"" Y# z# J4 W: Z# x
  5. /* USER CODE BEGIN 0 */
    ; `1 s- E1 c- Z4 \, S8 r
  6. /* USER CODE END 0 */9 J7 G' T  i- {/ r9 |# u
  7. SD_HandleTypeDef hsd;
    6 r/ R6 n: M# l$ g$ V
  8. /* SDIO init function */' r  u) U5 x  o9 E6 z
  9. void MX_SDIO_SD_Init(void)   //初始化配置
    ) t; B6 Q" G/ u0 ^8 g
  10. {
    $ ~+ g  g( A& |% Q0 i. g
  11.   hsd.Instance = SDIO;    //SDIO句柄,整个系统中只有这一个  _+ A* G7 T$ H$ `/ [7 @
  12.   hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
    2 m0 M; u, x9 b- H" {; @
  13.   hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;; I. m$ e) s5 m- U3 ^
  14.   hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;0 f2 J) U, q8 h# t- o
  15.   hsd.Init.BusWide = SDIO_BUS_WIDE_1B;   //初始化时设置数据宽度为1位* H+ ?* {* y9 ^' V% b7 G3 u7 _
  16.   hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
    9 o7 I1 d7 J  f# R! C" v4 F. L
  17.   hsd.Init.ClockDiv = 0;/ \% y" \# [; r" @) X. M' K( X
  18. //HAL_SD_Init()函数内部做了很多事去识别SD卡,具体的卡识别代码分析在下方8 W  G& i; y0 R
  19. if (HAL_SD_Init(&hsd) != HAL_OK) ' A2 l- i, g) o0 d0 {2 C4 k. t
  20.   {( n: ^- u3 [! U7 I
  21.     _Error_Handler(__FILE__, __LINE__);1 B' W6 V5 S9 A$ X
  22.   }% U. i% W* Y% k$ `1 S: K
  23. //初始化结束后重新配置数据宽度为4位" O( _+ t% M2 @2 b
  24. if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
    & t0 j; ?$ z/ S: x# _
  25.   {- G  o, D7 l4 T4 K+ b
  26.     _Error_Handler(__FILE__, __LINE__);6 V4 B" k" D' G$ `3 u0 f& R& U& k1 _
  27.   }5 f" w  d# b' I9 b7 J% B2 }
  28. }
    : \9 R  u& N: w, |# a
  29. //卡识别代码如下
    : Z. E. K, ~  h" z+ r/ \
  30. HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd)
    " n. @4 n8 F+ J/ W0 K
  31. {
    * f: m$ s# S" w$ h% ^4 t" a
  32.   uint32_t errorstate = HAL_SD_ERROR_NONE;
    , b1 L) T+ N2 p+ o% w+ m
  33.   SD_InitTypeDef Init;
    % `6 A: ^3 w/ }& h4 J9 `' ]: v  b" I
  34. /* Default SDIO peripheral configuration for SD card initialization */" U9 c/ r" g. h. Y
  35. //完成卡识别
    ; M7 v' W5 T; P# b) ^9 a
  36.   Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;
    8 R; E0 z5 N& h# g  a$ m- e2 y
  37.   Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;
      D: q* z& F* W$ V0 G8 I5 r  F
  38.   Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;9 p/ N3 a+ C. b& I; q
  39.   Init.BusWide             = SDIO_BUS_WIDE_1B;
    2 F! m/ `: p8 Z
  40.   Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
    0 B/ s2 q1 Y4 b& Y/ K/ C+ [6 E# \0 {9 B
  41.   Init.ClockDiv            = SDIO_INIT_CLK_DIV;
    " v% l& r+ c/ C3 j9 |& W
  42. //SDIO_CK卡识别阶段要求时钟小于400KHz,宏定义0 V2 n# S7 x/ |3 `2 \% r
  43. //SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV)! t9 B5 j; z5 K  M1 z) N* r) P7 v3 Z
  44. /* SDIO Initialization Frequency (400KHz max) */
    1 M& X  a/ v: P2 z7 x$ u! e
  45. /*#define SDIO_INIT_CLK_DIV     ((uint8_t)0x76)*/5 t; ?1 B$ c" }0 O5 [$ r5 n' `
  46. /* Initialize SDIO peripheral interface with default configuration */: ^' H" g3 A/ W/ P
  47.   SDIO_Init(hsd->Instance, Init);
    & t: w: u& u9 ?: ?- D# e
  48. /* Disable SDIO Clock */5 Z; g0 f- D; u* Z6 r4 ?( e
  49.   __HAL_SD_DISABLE(hsd);
    0 H8 ~( `/ E' p+ O
  50. /* Set Power State to ON 对SD卡进行上电 */9 D- j: K1 D$ P" Z
  51.   SDIO_PowerState_ON(hsd->Instance);
      {& R* r2 q3 u; o; c
  52. /* Enable SDIO Clock */
    # D) k- L# _7 X1 [9 @
  53.   __HAL_SD_ENABLE(hsd);# _1 c. n* ~/ F. @
  54. /* Required power up waiting time before starting the SD initialization
    / b% K5 N, v5 m0 ?8 ^0 W4 L
  55.   sequence */+ A. @& W- x0 \
  56.   HAL_Delay(2U);4 b8 R' }. a; Y, I
  57. /* Identify card operating voltage *// ~7 c+ U3 j, ?+ S( m* B9 C! y7 [" `
  58.   errorstate = SD_PowerON(hsd);8 f) G+ @% w' W2 h  H1 ?* Q
  59. if(errorstate != HAL_SD_ERROR_NONE)
    " W. A3 \# r$ V& F
  60.   {
    + W' \+ m4 j2 E5 u# S; f' r
  61.     hsd->State = HAL_SD_STATE_READY;1 h5 Y3 J5 w; }, \6 d
  62.     hsd->ErrorCode |= errorstate;
    ' E1 ~" w& Z) s8 W" a# m# ]
  63. return HAL_ERROR;) U1 o% ?- w; b5 T6 C) A4 s. f( d# b! U
  64.   }- j$ F: X1 F( V+ Z: |
  65. /* Card initialization */
    / Y  p9 Y3 l0 {& U( B- y- w2 K, D1 K
  66.   errorstate = SD_InitCard(hsd);$ f) g; e/ u2 Q4 x' r( U
  67. if(errorstate != HAL_SD_ERROR_NONE). g4 R- a3 ?, E% x+ x7 u% C7 @
  68.   {1 a, |6 k! r/ g  H1 H! m
  69.     hsd->State = HAL_SD_STATE_READY;$ I$ a1 ]% ?) T) i
  70.     hsd->ErrorCode |= errorstate;$ m# D& O) `+ k& x; M
  71.     return HAL_ERROR;: F; J0 n5 g& y8 }: T
  72.   }
    # T3 a' _4 j" p, r3 z2 r4 L9 q7 g2 y# O
  73. return HAL_OK;4 N' G: r3 o/ R
  74. }
复制代码
8 M8 J) z' U( N- N, D) T
  • SD卡DMA模式操作实例
    / r" U3 p; q" x1 F

    5 F" L& T& y5 @1 a" e
当SD卡中有大量的音视频需要读取时,整个过程需要CPU干预,这样CPU的利用率就会降低,因此大量数据的传输最好启用DMA。

& t" a, ]8 u, y& \- D. W6 L% L
实验内容:向SD卡写入数据后读出。
( K9 X1 H6 D$ V0 ]1 Y$ {6 f8 v9 X' R
实验步骤:工程的时钟配置和SDIO配置与普通模式相同,只有一点不同需要打开SDIO的全局中断,因为SDIO发送与接收完成后都需要中断去生成DMA请求。设置SDIO全局中断优先级更高一些,因为它内部还有很多其他中断,发生错误时更需要处理。
; U3 K0 x6 @7 J+ N
微信图片_20230411192200.png

  p, p; ^- `- F* }
配置DMA
2 |0 h, p* R% n' H% D/ D, e
微信图片_20230411192156.png
9 U1 G( J* [: E+ \9 U# ^) I
编写代码:
  1. //mian.c0 x, J6 l! S% n- ~* i+ p, a- l
  2. 0 g4 i# \$ ?: e' q! d  c
  3. #include "main.h"
    3 }9 L! o3 t+ g
  4. #include "stm32f4xx_hal.h"
    * z7 o1 w: R! D" w; U* j% i, r
  5. #include "dma.h"
    5 `  C7 R2 w- c3 G2 }2 _
  6. #include "sdio.h"
    + Z0 E; C0 }# O9 g
  7. #include "usart.h"
    8 b& @8 E. `, j& Q4 h2 o
  8. #include "gpio.h"
    * c1 _/ G8 Y( q. u) m+ J

  9. 9 q: p2 ~  b/ _( b6 q
  10. #define SDBUF_SIZE   1024
    ; F: ~' l- U* u' H9 T8 \
  11. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收
    4 ]: w: ~  s$ b; L3 E1 ]
  12. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出7 p& M, [+ C5 G
  13. uint8_t DMA_SEND_OK, DMA_RCV_OK;
    " v) n; J9 O, F5 V  r
  14. , l5 K) n* k. T; Y
  15. int main(void)
    + E- b! r' a2 c2 V: M% s$ U
  16. {" }  r/ _* J5 E8 i! @1 D: y, n
  17.       uint32_t i;
    $ g# R6 m& s" a% S0 w2 N8 g
  18.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
    5 |5 K! k' ^: a7 E9 ]; Y8 Q& X, j
  19.       HAL_Init();
    4 b2 g  G: D8 F- [! X9 y
  20.       SystemClock_Config();9 |. G9 D5 Y7 ^' z2 `! |2 B2 E+ Q
  21.       MX_GPIO_Init();
    ; N/ u% G- @; f0 u
  22.       MX_DMA_Init();
    3 d5 Q- a' N+ ~% Z* m/ _
  23.       MX_SDIO_SD_Init();7 z  G9 e1 T8 h* N# i6 ?4 H
  24.       MX_USART1_UART_Init();; p0 D( ]" y' K$ w& z5 [) z8 ]

  25. # b7 F# F4 B2 l2 j4 N7 j8 _0 u
  26.       printf("this is sd test\n");
    / U& H; ~8 H5 [$ \. y
  27.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印
    ! g6 {. o( C3 Q
  28.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
    2 p) r# B3 R) L; t4 ?: c1 z+ X
  29.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);0 v& D" d9 v2 Q, y
  30.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本: P- C3 e9 V( L
  31.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
      e! F# W/ p% X" l$ `5 O- r' s. w
  32.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
    7 t2 Y  Y8 ~: X) E
  33.       /*------------------- SD DMA 写测试-------------------------------------*/9 a/ H+ F  v9 m! K
  34.       memset(SDBUF_TX, 0x2, SDBUF_SIZE );1 }4 d1 c  c$ e/ @, S
  35.       DMA_SEND_OK = 0;  //设置发送完成标志位为0,完成时为1
      n: @' N8 l7 A+ r( m, q
  36.        /**) ^1 F7 S0 {- {1 w& A) X' _& ~9 H( u
  37.        //函数原型
    8 A- j( q: O* O" e; Z1 x
  38.        *HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData,
    9 \* t5 _/ b) X- F- L
  39.                                              uint32_t BlockAdd, uint32_t NumberOfBlocks)$ u0 D6 s( f0 D2 s) \; f
  40.       
    % f5 S! y$ M  C. s- l1 ^
  41.         * @param  hsd Pointer to SD handle
    * }& x' C/ }/ J9 i( m5 B& X5 @
  42.         * @param  pData Pointer to the buffer that will contain the data to transmit4 X3 T! `- J9 o+ h: U5 j" Q, }
  43.         * @param  BlockAdd Block Address where data will be written  从哪个块开始写
    / n0 n! K- [) {. `* l8 V( D, M- {
  44.         * @param  NumberOfBlocks Number of blocks to write  写几个块
    * n: L" Q6 g' |% D* @9 d6 c& N
  45.         * @retval HAL status8 f* Z& y- q, |* Q  }
  46.         */; \/ o2 b: G/ w3 H" d
  47.       
    ; R5 {5 d' K% l  ?# I3 \6 ]  `- R
  48.       if( HAL_SD_WriteBlocks_DMA(&hsd, SDBUF_TX, 0, 1) == HAL_OK )6 t0 i$ f9 z% [$ ?9 X7 f$ T
  49.       {" a7 A% k) E9 |" R7 B* J
  50.           //等待DMA传输完成,并且SD卡状态为传输状态" j% _: F- A& P2 g0 `
  51.           while( (DMA_SEND_OK ==0 ) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));  ( @/ `4 p/ o" \; Q
  52.           printf("WriteBlocks Successfully\n");* {7 k6 e- G: _& u; }- r
  53.       4 T! M1 T2 Z  u9 y
  54.           for(i=0; i<SDBUF_SIZE; i++)1 [4 g* g: p' U" X# C( k" f8 u4 @0 j0 y
  55.           {
    / C& k. F, m& J& Q! I4 M) K1 z, b
  56.             printf("%d ",SDBUF_TX[i]);
    ) \" }2 z7 m: s3 B, u" i$ L$ T, U/ i
  57.           }
    ; }. Y" [% t9 g2 m  x
  58.           printf("\r\n");
    , j4 U+ `  l4 q) ~' c- U
  59.           }
    9 `! R3 y( O6 o- f+ y4 l
  60.           else
    ; p5 a7 K2 |% N' P/ B* d8 O
  61.           {3 A/ v7 ]2 v# Q& f+ E6 i
  62.              printf("WriteBlocks Failed\n");  G7 H2 T9 e0 [, V9 c
  63.           }% h1 l/ F3 k5 A9 q; L; c; j
  64.       
    9 t/ U8 r- q2 b0 X* U; C
  65.           /*------------------- SD DMA 读测试-------------------------------------*/& B8 v& L, }7 L6 S8 y' S! Z
  66.           DMA_RCV_OK = 0;   //设置读取完成标志位为0,完成时为1# ~+ e5 m% j3 i. @, ~
  67.           if(HAL_SD_ReadBlocks_DMA(&hsd, SDBUF_RX, 0, 1) == HAL_OK); B+ r  ]/ q: J
  68.           {
    $ ]: T6 p* U! i: B3 x3 A- w+ E
  69.               while( (DMA_RCV_OK ==0) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));         
    0 G/ |1 G( h& _. m/ f$ R
  70.               printf("ReadBlocks Successfully\n");          8 a7 V6 O* `# d1 b2 t
  71.               for(i=0; i<SDBUF_SIZE; i++)
    2 K. ~$ W2 y3 ^1 l2 Y2 M0 M
  72.               {
    9 w3 [4 k2 J8 D* e
  73.                 printf("%d ",SDBUF_RX[i]);7 Z! u. R) q+ L% W
  74.               }# C' [* I7 ]% J; m3 r0 j( q
  75.               printf("\r\n");
    % D6 w9 j% z: b% {/ g% w5 g5 G
  76.            }. @; p1 T5 _; G, A0 E% E3 N
  77.             else+ b! }# X" f$ [2 E8 E" e, N- L
  78.             {
    ( t0 f) A% k0 }- w! C8 K+ d- S% t
  79.               printf("ReadBlocks Failed\n");, p# [. p, z8 C! |  w2 a7 f5 {
  80.             }8 e2 S5 X& O$ q, U
  81.             while (1){}' F7 m; U+ f7 M8 q% E* J$ P. n+ F+ \
  82. }
复制代码

2 q1 d$ i# h- k* _5 ^( Q; G3 C

  1. 4 K/ u: i0 c1 s( `" O7 f
  2. //sdio.c! w. q1 r' y6 w2 z/ |( b' K

  3. * c% }% t% @5 g
  4. extern uint8_t DMA_SEND_OK, DMA_RCV_OK;6 Q' u, {( E2 a+ C9 c0 K* U
  5. //发送完成中断处理
    , K! l& l9 w8 u7 Y9 @  x( W
  6. void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
    - A! {$ c$ A) N3 K# R4 ^- f
  7. {
    + m9 T9 K* N, W' Z6 F$ T9 `. a
  8.   DMA_SEND_OK = 1;
    % C8 F, u! }+ q( i; S* ?1 m' Z
  9. }* M  W: _4 \+ P8 _2 N3 h2 G' q
  10. //接收完成中断处理
    " N+ T2 M, `. [, P8 U
  11. void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)/ X+ t  b8 B$ m7 Z
  12. {) M% \1 K) d5 l( h6 ~/ F( B
  13.   DMA_RCV_OK = 1;
    - Q  s, w: ^2 p9 m
  14. }
复制代码
% w; v8 P/ }$ g
至此,ARM开发进阶知识已全部更新完毕!下一系列介绍物联网操作系统开发!9 O0 H! J( c- [9 O' Z& ?
0 f- d1 y2 E0 i' c( N

( d. K+ |+ E7 d, d% D& ]2 W- {- r转载自: 骆驼听海- I9 x4 `9 M$ ^! b- V6 w
如有侵权请联系删除
! {2 W! V0 V) c
收藏 评论0 发布时间:2023-4-11 19:23

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版