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

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

[复制链接]
攻城狮Melo 发布时间:2023-4-11 19:23
微信图片_20230411192319.jpg
6 ^' m) x( s" @. p! B  q/ v
SD卡(Secure Digital Memory Card)即:安全数字内存卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。
9 P7 H, h5 W& Y# H" T
  • SD卡简介
    7 f5 ^4 g% Q: k# G+ Q& d
SD非常小巧,可以嵌入到设备中,在嵌入式开发中,产品需要存储一些容量叫大的文件,就可能会运用到SD卡。SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示。
7 a7 z2 [, P/ j+ F
微信图片_20230411192315.png
. ~; Q5 L2 w0 g
容量大小不同的SD卡,其内部的操作标准是不同的。很多外设会明确告诉用户本设备最大能够支持容量多大的外扩,不能无限制的扩存,因为容量越大,操作不同,对控制器的要求比较高。当前设备的控制器不足以支持大容量,所以有外扩限制。
& U8 R" l2 y2 _& `3 A: v, N# k; G
微信图片_20230411192311.png
( f3 E- |1 h. e, o; \
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。

2 E9 t5 b% l( s# P$ @6 ?9 Y  e

$ D1 @$ q7 J5 L, w: \" ]/ K2 o. b
微信图片_20230411192306.png * r. L9 S+ z# z# B/ U: K

- \+ W0 Q. f: j% S# J1 W0 p! r
  • SD卡的物理结构
    * Z) b6 d6 q/ o1 v5 E, S. N* Z
* [0 T% |- G* F  e9 i% `
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。
; k3 S; ?9 j0 X2 q* N. w
微信图片_20230411192301.png
8 A- M: u4 J0 W4 `# y
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。

) H$ l) G4 m$ n& I
% K. W! e8 T5 K) L' d: i7 z- E
8 G8 W5 b/ z( {  D2 C
存储单元:是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;
$ S, R& d4 |; }# }: y' A
电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;

8 \6 d( f2 @- g: Z0 c! J
卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;

' o/ q. M' q. T& I9 v
接口驱动器:控制SD卡引脚的输入输出。
' v! C- A/ E/ t0 ?1 U( ~

! ~6 _1 T( O8 o  |; H
  • SD卡内部寄存器

    % Y0 K; i$ S% b3 g' r, w- _1 Q
SD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。
$ _) M3 s8 W+ b
微信图片_20230411192255.png
$ ~+ [- P& U- Z  S# b% w
  • SDIO接口

    , V7 n/ `' J7 V& d* _
SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡都有SDIO接口。STM32F407系列控制器有一个SDIO主机接口,它可以与MMC卡、SD卡、SD I/O卡以及CE-ATA设备进行数据传输。也就是说SDIO接口不仅仅只会操作SD卡,凡是符合SDIO接口通信的设备都可以被操作。
! l: I" ?. M7 F" w- a7 O9 |
微信图片_20230411192250.png
( w5 e1 y+ n6 W. ?0 X8 |+ g- 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 的通信模式。

; r; s" r0 N5 }9 g
  • SDIO组成

    % E! w$ h8 x2 S* h1 t5 n
SDIO 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令和数据传输。

0 h, I, ?& @  I& q+ C
APB2 接口访问 SDIO 适配器寄存器,并且生成中断和 DMA 请求信号。

' S7 D$ e2 x2 O
微信图片_20230411192246.png
/ n+ V5 W2 C$ n5 {, V7 m! P
注:
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。

: {/ v! Y0 U, g) s1 ?& d
注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)。
# V; v, l1 x1 \" \; {$ ?2 r0 G; @
微信图片_20230411192243.png
6 Y* d& P# D3 D- F6 x  L
该适配器由五个子单元组成:
1.适配器寄存器块 :适配器寄存器模块包含所有系统寄存器。
2.控制单元   : 控制单元包含电源管理功能和存储卡时钟的时钟分频器。
3.命令路径 :命令路径单元向卡发送命令并从卡接收响应。
4.数据路径 :数据路径子单元负责与卡相互传输数据。
5.数据 FIFO :数据 FIFO(先进先出)子单元是一个数据缓冲器,带发送和接收单元。FIFO 包含一个宽度为 32 位且深度为 32 字的数据缓冲器和发送/接收逻辑。(一共32 个单元,一个单元一个字)。所有的数据传输都要经过FIFO,便于管理。

5 p( f) y1 B! d! [5 j0 R( D
  • SDIO 命令简介

    - n0 J: H  C" O  p) ~, S
SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。

! y! H! i- H" o, e8 J
SD命令格式固定为48bit,都是通过CMD线连续传输的,数据线不参与。

" l) o+ H' [1 R
微信图片_20230411192239.png
3 B8 ~2 U, O/ u9 F: W! g5 d
SD命令的组成如下:
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。# ~9 D3 `+ a: U2 f0 i

3 m3 q% I' e& e7 [2 V# `" x9 f# C* Y
传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。
" H9 V  n7 Z- D9 \! P. c8 k
命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。
" ^0 N  @% b: P: Y$ [  D
命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。

( ]8 h. G: s* e' k. X# @
地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。
6 L& s! ^) d3 Q& U1 u1 v
CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。

: f0 p6 [* G, p8 I- g
SD命令有4种类型:
1.无响应广播命令(bc),发送到所有卡,不返回任务响应;
2.带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应;
3.寻址命令(ac),发送到选定卡,DAT线无数据传输;
5.寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。
3 y! |- R; K/ h  C. r
在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD),也就是说在64个命令作为常规命令的基础上加了特定的命令

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

  X" @6 g  j; M6 a3 x4 A3 @
SD命令响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:

& o% Y+ v" L4 S6 z: e& F
lSDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。

, Z  D+ r6 Y6 g
SDIO读数据

; M' I- w: T7 N: X) N$ B
微信图片_20230411192236.png

; N% G, s# W. f
单个块读操作与多个块的读操作除命令不同外,还体现在读操作结束时,单个块读取一个块自动结束,而多个块还需要主机发送停止命令。写操作类似,也要多发一个结束命令,只不过写操作写数据前需要检查卡的状态是否为忙状态。
: {/ ~6 m5 }& ^  f3 f! _
SDIO写数据

! [9 X7 }; W1 ~+ O- g% i
微信图片_20230411192231.png
* u" C- S4 q, k& J
  • SD卡操作模式
    . C- b& ^6 e2 F# l
SD卡有多个版本,STM32控制器目前最高支持《Physical Layer SimplifiedSpecification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。
. |) @0 n: _) y/ D* c! M
SD卡系统定义了两种操作模式:卡识别模式和数据传输模式。

* ~  c/ |' r5 ^
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。
6 v9 b. @* v/ C6 A
每个不同的操作模式下,SD卡都有不同状态,通过命令控制实现卡状态的切换,在不同的状态下做不同的事,比如在发送数据前需要SD卡处于传输状态,发送数据时,SD卡处于接收状态。

7 D; {6 g' x- v9 T- r
微信图片_20230411192227.png

) `0 P4 m* Z" F
  • 卡识别模式

    * }5 X& _' ?% E
微信图片_20230411192220.png

7 t9 _' Q1 f6 b3 r& u% k( ~
①上电后,主机发送CMD0让所有卡软复位从而进入空闲状态。
②主机发送CMD8确定卡的电压范围,并识别是否为2.0的卡
③主机发送ACMD41识别或拒绝不匹配它的电压范围的卡,SD卡需要工作在特定的电压范围之内
④主机发送CMD2来控制所有卡返回它们的卡识别号CID(128位)
⑤主机发送CMD3命令,让卡推荐一个RCA(16)地址作为以后通信的标识,之后都以RCA值作为身份标识进行信息交互。

' k8 ~! r) ^( D& {
注:在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下,在SD卡初始化时,SDIO_CK不可以超过400Khz
8 t6 n9 u! R1 V
  • 数据传输模式
    2 w: ]' x0 `" }( G5 [: _
微信图片_20230411192214.png
! q% T! |  Q# H6 _- ]" X
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。通过CMD7命令加上RCA值来选定指定的卡,选中后SD卡进入数据传输状态,就可以发送CMD17读单个块,CMD18读多个块,读多个块时只有发送CMD12命令才会停止。SD卡再次进入传输状态,若不想对卡有任何操作可以再次发送CMD7命令加上RCA值来取消指定的卡,写操作与上述原理相同。

6 |. }* I% E8 X2 |$ G( C9 ?0 _- Q
  • SD卡普通模式操作实例

    9 A) P. d, q4 U: |( p' Z/ ?
实验内容:向SD卡写入数据后读出
. I# L( X9 p5 \- L8 ?  D
实验步骤:

: E! M7 Z8 q7 }1 e" B6 Q
1.配置RCC,与以往不同的是SDIO适配器的时钟是单独配置的,需要专用的SDIOCLK,标准工作在48MHz

) r9 A, o; @$ v5 Y) f
微信图片_20230411192210.png
% H9 l$ N5 s% ]/ ?/ R8 ?- M
2.配置SDIO* I- {+ ~' i. |- @

6 X+ Z% H9 R' C2 P3 K; I
微信图片_20230411192207.png

* M8 Q# M) |: ~2 u* M! d9 U
微信图片_20230411192203.png
! ^! E" K; ?3 I; W2 z
3.编写代码

  1. % p. S0 H6 Z3 d, R0 {) F8 N
  2. //mian.c
    " n3 @( F; R6 u! V2 o1 _, [/ b
  3. #include "main.h"
    ! x  O; b5 J- _% [+ x: K0 m
  4. #include "stm32f4xx_hal.h"9 J9 n9 Z  e/ }# \
  5. #include "sdio.h"! W6 q) ]( j; E3 P  V) Y
  6. #include "usart.h"
    5 q! y7 Z) V* d3 ~9 C
  7. #include "gpio.h"% `# ?( T/ Q7 A1 w  j" r
  8. #define SDBUF_SIZE   1024
    1 ^; h9 b6 M8 ]- W% |0 y! H  m
  9. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收' P9 s7 t$ W9 ~  @8 X2 z5 n* c, f
  10. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出6 m3 ?6 x% _" r# {8 a
  11. int main(void): M# ^: y% l# H
  12. {) B; A7 V  L, z4 K0 s4 W/ k
  13.       uint32_t i;
    9 d8 @. ~4 h: s9 y
  14.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息1 w5 T3 Y" w: O# i  m
  15.       HAL_Init();8 n# i' B( n* q9 k) H0 u: n
  16.       SystemClock_Config();" }% J9 q5 Y, I- U! d
  17.       MX_GPIO_Init();
    3 w- W6 d1 _% e7 I( R, f
  18.       MX_SDIO_SD_Init();
    8 B' H4 i1 X; a) x+ q
  19.       MX_USART1_UART_Init();
    & _/ H% v/ x6 H6 k' x, |
  20.       printf("this is sd test\n");
    ( u+ g3 @' j. f& F
  21.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印4 b6 A6 ~( ?& ?+ U; S; F2 ]$ m: z0 P
  22.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);' a, P2 H3 U1 x5 o! Z2 i
  23.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);
    ) }' x' B. ~1 m, A
  24.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
      i* v9 b$ B6 Q9 g9 d8 \1 f
  25.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
    6 |4 B# E1 `  j: e
  26.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小1 N# v- @2 d- Y" ~* @) A
  27.       /*--------------------SD卡写测试----------------------------------*/8 W, P# K4 o" P' F5 B
  28.       memset(SDBUF_TX, 0x8, SDBUF_SIZE);  /、填充TXbuf为0x8( m6 [) L9 C1 x: Z/ R9 g
  29.       /**
    ; f' c/ @7 J  E, L" H  A
  30.          //函数功能及参数描述                             & q. V0 e/ ?# W
  31.         *HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, 8 g$ x- X) J$ ]- ]! Z
  32.                       *uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
    ( L1 `# p" b3 k; B
  33.         * @param  hsd Pointer to SD handle# X% w% u6 m2 F  _8 J' e" ]
  34.         * @param  pData pointer to the buffer that will contain the data to transmit
    ' n: _- c: c$ n) G* e/ o
  35.         * @param  BlockAdd Block Address where data will be written,从哪一个块开始写  
    ! [5 A3 h# ?2 m  t/ M0 j
  36.         * @param  NumberOfBlocks Number of SD blocks to write   写几个块
    , y6 H1 T6 k. E$ K% O, k
  37.         * @param  Timeout Specify timeout value 超时时间  T$ c  G8 `; p7 s( B$ I. p
  38.         * @retval HAL status& H# z. Y' h, J# V# L6 H
  39.         if( HAL_SD_WriteBlocks(&hsd, SDBUF_TX, 0 , 2, 1000) == HAL_OK)
    / X: Q$ C* O! I: C) ?4 d
  40.         {& N$ q5 _0 P& d1 \% u3 {3 E
  41.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//处于传输状态退出
    , W$ ^. y8 m: C. ?& v. t
  42.             printf("WriteBlocks Successfully\n");
    ' O* u8 M. p( H" k7 M' @
  43.             for(i=0; i<SDBUF_SIZE; i++)! y! j% R" S: {3 W1 t& e! |  w
  44.             {
    # A( p9 w% f" v, i% P: l9 h, x0 S
  45.                 printf("%d ",SDBUF_TX[i]);
    , X, h( ]4 K. k$ }* W* P7 s. C0 ~+ y
  46.             }9 A& C3 x; x3 t# r& N- U! U
  47.             printf("\r\n");" j% `5 b$ _3 y, l8 v! N! H
  48.         }# ]% S# W! T; n5 V
  49.         else' S! O) t# m1 m6 z7 _) c3 r6 Y
  50.         {2 F- m, i8 v, }1 T/ c/ f
  51.             printf("WriteBlocks Failed\n");
    ; x" L5 O( L- L& S' H
  52.         }
    $ W% s/ T. j( ^5 E' J
  53.         /*--------------------SD 卡读测试----------------------------------*/1 [3 B4 X3 l3 ^
  54.         //与HAL_SD_WriteBlocks函数的参数功能相同
    ) t: ]& v) w8 P- N
  55.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
    1 k2 [$ {0 V0 _2 @
  56.         {
    & w5 n: H- R8 p  E/ e+ W
  57.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//返回到传输状态退出
    8 P$ a: Y9 ^; y: k: ]$ [0 N- K
  58.             printf("ReadBlocks Successfully\n");/ ]. g) M6 o3 z' E' _: P! L- A# L4 m
  59.             for(i=0; i<SDBUF_SIZE; i++)
    2 a- w' W6 F. \$ J4 Q6 o) Y% w
  60.             {
    7 t) U% \  F& M; G# w; b  _6 ?
  61.                 printf("%d ",SDBUF_RX[i]);+ d3 [* X2 }9 x  B
  62.             }
    ) v6 {. F' D! K" i: ^, L7 M
  63.             printf("\r\n");
    . Z1 i; a5 ?" Q3 B2 P
  64.         }
    ) P$ F2 |% A6 n- N% \: y. n
  65.         else
    7 y" S! `5 L. j* ?' ?( r* N  I
  66.         {
    ; Z3 @& }4 {' k& B. a3 Z# ]
  67.             printf("ReadBlocks Failed\n");5 R+ d# H* ]6 z" ]/ ~
  68.         }
    7 N$ ~: O* P, Z8 O
  69.         /*--------------------SD 擦除测试----------------------------------*/
    7 f3 t/ v' r2 e2 e, x4 p$ o
  70.         if (HAL_SD_Erase(&hsd, 0, 1) == HAL_OK )4 J/ S. H. }% N; A0 c: [
  71.         {
    1 c# ~, g. ?3 Y0 R# g/ t) y
  72.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
    : Y, V& x. d+ h* ^3 O' H5 ~/ j" ?
  73.             printf("SD_Erase Successfully\n");
    % d5 T  @, o9 L' [8 B& q
  74.         }: j+ r6 O: \; v+ ]+ s* y
  75.         else: M7 u+ [. t2 w5 z+ l
  76.         {" I9 x  Y( q* V$ _; E6 v
  77.             printf("SD_Erase Failed\n");
    / e% o' Q9 E& v0 T' `$ m7 H/ M9 I
  78.         }' n8 {  r2 ]) N
  79.         /*--------------------SD 卡读测试----------------------------------*/
    1 j/ t0 o* @; Q. g9 T
  80.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)0 h% o& t5 J' \* ]6 U
  81.         {
    & f4 R8 a4 S0 `  L1 O, L+ N
  82.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
    6 k& u) O7 U9 Z6 [8 {" [( C
  83.             printf("ReadBlocks Successfully\n");
      h% ~, D9 e+ P2 ~7 x1 M/ {9 ^
  84.             for(i=0; i<SDBUF_SIZE; i++)# T; b3 `6 h/ d/ W  ?
  85.             {! C+ I7 u& S; o2 i8 `: x; L
  86.                 printf("%d ",SDBUF_RX[i]);, D2 [' h1 B+ C) ?
  87.             }
    ' ^  }! _4 t! e, o$ m
  88.             printf("\r\n");
    ( a5 p& Z( |! U. A+ w
  89.             }0 I2 C# j+ w) k9 F3 }5 m8 C. X
  90.         else
    . b5 H0 |8 [, K4 [! W' U
  91.         {2 h, h& n7 w' E4 |1 ]
  92.             printf("ReadBlocks Failed\n");: |" J1 o9 V0 e( Y
  93.         }
    : B$ q, G) T. p9 H
  94.         while (1)) V; u. T9 V0 L! `! f& u
  95.         {5 |2 ]7 O' \6 X9 N* O" d5 U: _
  96.         }  
    0 `5 z" a3 p3 h7 f$ c
  97. }
复制代码
  1. //sdio.c
    4 a" [: b* p* I/ `5 U6 }2 B# ~
  2. //此代码为工程自动生成,非用户编写,这里只做分析0 J' u% T: ~3 ~0 M% b) k
  3. #include "sdio.h"& A+ a' a, e  x  O. ^2 _  z: Q8 p
  4. #include "gpio.h"
    ) c' H1 L9 N5 o# O# \
  5. /* USER CODE BEGIN 0 */$ f/ e( l1 y: p! v( C
  6. /* USER CODE END 0 */
    ; O7 q$ J4 t8 ~; i' @
  7. SD_HandleTypeDef hsd;
    6 Y+ @" }+ }4 O7 E: O5 m
  8. /* SDIO init function */
    $ ^0 k5 |$ }/ K  B$ F
  9. void MX_SDIO_SD_Init(void)   //初始化配置- x, w8 p5 l" n! N, [
  10. {2 v" e) l7 i+ W: X* e4 e
  11.   hsd.Instance = SDIO;    //SDIO句柄,整个系统中只有这一个
    1 Q2 U4 R- j! i& O
  12.   hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;: Q( Z* _* H  R% _+ F7 t
  13.   hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;/ D- R4 M) \1 c* q9 w& M
  14.   hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;. T  Y% _2 n5 }: H* m
  15.   hsd.Init.BusWide = SDIO_BUS_WIDE_1B;   //初始化时设置数据宽度为1位' S+ [5 e# \$ \' E0 u
  16.   hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
    ' G; O3 ?, f# r# R$ B" T4 b
  17.   hsd.Init.ClockDiv = 0;1 i% P! s6 r* x7 \
  18. //HAL_SD_Init()函数内部做了很多事去识别SD卡,具体的卡识别代码分析在下方
    * L! E6 h( T- k: |0 J
  19. if (HAL_SD_Init(&hsd) != HAL_OK) 1 n5 [$ a$ m3 Q" p9 O2 f
  20.   {( ?5 D/ z% r. T
  21.     _Error_Handler(__FILE__, __LINE__);3 G" ]4 h1 T( Q
  22.   }
    - p4 C! U4 h: i- H3 ~
  23. //初始化结束后重新配置数据宽度为4位
    : a# {: S2 N1 ]) X2 ]
  24. if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
    6 n+ K8 F: K; |4 `, p
  25.   {8 S3 F4 M2 d, ^# s2 S
  26.     _Error_Handler(__FILE__, __LINE__);: R5 u) v9 z! e$ P4 U$ a
  27.   }
    3 s5 `/ F2 y  G( T
  28. }
    * U# h7 [0 f1 p5 s
  29. //卡识别代码如下5 ]2 u8 G  {6 U4 T! |  k
  30. HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd)! Q" O  d3 ^/ u3 N  X8 K) d
  31. {- n" u2 @7 \1 ?$ C
  32.   uint32_t errorstate = HAL_SD_ERROR_NONE;
    3 E+ p  {7 U3 b2 ?+ {5 S2 F9 L1 ?
  33.   SD_InitTypeDef Init;9 x6 N" O2 Q. W+ o. C2 Q# r% _% s
  34. /* Default SDIO peripheral configuration for SD card initialization */. a! }. T0 a3 m/ U3 w6 M6 r0 f
  35. //完成卡识别7 X: q7 i4 t" [1 J; M, A1 P
  36.   Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;7 J, B" s7 h$ ?: [0 _$ [
  37.   Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;
    9 |6 V( P9 c) R. Z, H% k& y+ C
  38.   Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;
    # h0 U% ^0 }+ w  X; r9 g
  39.   Init.BusWide             = SDIO_BUS_WIDE_1B;% Z0 W7 m; y  T0 u# @2 h! m
  40.   Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;, w8 {! n. O2 l7 Q% j6 C- C
  41.   Init.ClockDiv            = SDIO_INIT_CLK_DIV;
    & x( C" d9 ^4 m, F5 g
  42. //SDIO_CK卡识别阶段要求时钟小于400KHz,宏定义
      n- T6 `; a# U% w" F
  43. //SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV). F2 L/ @1 f7 o$ k' e$ C
  44. /* SDIO Initialization Frequency (400KHz max) */1 ]' n5 ]8 i/ V5 ^5 N! |/ Z  T$ I
  45. /*#define SDIO_INIT_CLK_DIV     ((uint8_t)0x76)*/0 E, p$ Z6 C$ v" I
  46. /* Initialize SDIO peripheral interface with default configuration */
    - k" H& ^+ G; F0 e
  47.   SDIO_Init(hsd->Instance, Init);
    " q6 {9 A! _- m+ m% q
  48. /* Disable SDIO Clock */
    3 b+ B0 o, `9 i) E$ g0 e
  49.   __HAL_SD_DISABLE(hsd);
    ' |# Q: r% ]  P, G% ?% B
  50. /* Set Power State to ON 对SD卡进行上电 */8 v7 O- j$ C9 h8 |
  51.   SDIO_PowerState_ON(hsd->Instance);
    7 _% E5 X, Z  Y0 j8 p. W
  52. /* Enable SDIO Clock */4 ^0 M. {) ?! A# v  P
  53.   __HAL_SD_ENABLE(hsd);4 s* y5 N% G& l# V& _/ I
  54. /* Required power up waiting time before starting the SD initialization % C5 l# _6 b3 W. b
  55.   sequence */
    3 S# F3 D2 l( I& n+ l% |
  56.   HAL_Delay(2U);! z$ h: d0 m$ Q; W: i
  57. /* Identify card operating voltage */# f6 s3 \" B5 a/ k( V
  58.   errorstate = SD_PowerON(hsd);, P5 V. a- c( K% I
  59. if(errorstate != HAL_SD_ERROR_NONE)6 L2 n+ c9 x6 F  g" m1 E
  60.   {  x3 l- c* I9 C
  61.     hsd->State = HAL_SD_STATE_READY;
    0 n8 s6 q8 M! E0 W7 r- ]4 K
  62.     hsd->ErrorCode |= errorstate;
    " }$ o& R* Q+ D8 e
  63. return HAL_ERROR;
    " Y- P& ?# W% _6 J: W% g
  64.   }6 O6 Y; S  e6 K# m1 K) `5 R
  65. /* Card initialization */. ]/ E! t$ e, t/ ?! X
  66.   errorstate = SD_InitCard(hsd);
    $ p/ e5 X( J  Q
  67. if(errorstate != HAL_SD_ERROR_NONE)
    . k$ l- q# {- E- O+ l2 W
  68.   {
    ! z4 h! E; R  X9 |, B( u: {
  69.     hsd->State = HAL_SD_STATE_READY;
    6 M% s4 K5 Z1 Y/ p3 r
  70.     hsd->ErrorCode |= errorstate;
    : X/ I0 v$ [4 Q8 R
  71.     return HAL_ERROR;& m$ Y5 }! Y2 B0 Q( h
  72.   }3 G& v4 d5 u! V- N; c$ q4 a1 L; n
  73. return HAL_OK;4 l' z/ w) T9 X6 ^  l$ Z7 G4 P
  74. }
复制代码
( o) ^! i" _  ~& v9 [. z
  • SD卡DMA模式操作实例
    : F1 G$ P' |5 J6 Q& u

    % n1 n2 W  w7 w6 p) v
当SD卡中有大量的音视频需要读取时,整个过程需要CPU干预,这样CPU的利用率就会降低,因此大量数据的传输最好启用DMA。
5 s, P, x( ?8 }/ \" g
实验内容:向SD卡写入数据后读出。

  o$ `1 F  }9 ~- P) E
实验步骤:工程的时钟配置和SDIO配置与普通模式相同,只有一点不同需要打开SDIO的全局中断,因为SDIO发送与接收完成后都需要中断去生成DMA请求。设置SDIO全局中断优先级更高一些,因为它内部还有很多其他中断,发生错误时更需要处理。

* F5 C, \9 y8 W9 c! o
微信图片_20230411192200.png
/ F8 }, ^) |0 N/ G- s2 \; F
配置DMA
; v1 @3 y* O& X9 B& [" [2 a
微信图片_20230411192156.png

( d8 Y4 m9 W# Z0 ^7 ]7 M
编写代码:
  1. //mian.c
    9 D" s1 x; x0 p& k

  2.   v6 r7 h! I: b$ U8 t# F! G4 s
  3. #include "main.h"- `* D9 m) L! M) N
  4. #include "stm32f4xx_hal.h"
    7 V% l7 _1 l8 e% l6 s& ^$ p
  5. #include "dma.h"- j0 M, B7 F9 P* G- \; M7 E
  6. #include "sdio.h"
    ) v1 e+ A0 @9 L: h
  7. #include "usart.h"
    " b" H% h8 @3 u# u# X3 T
  8. #include "gpio.h"
    ; o: A7 S0 i  M& y3 w

  9. # A& T( Q& v2 s
  10. #define SDBUF_SIZE   1024
    , \# `* z' ]! o! m4 n! B
  11. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收- r8 ~# m- I! q) w+ l  o. d
  12. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出' K2 y0 i0 h8 k( a$ o6 ~; v
  13. uint8_t DMA_SEND_OK, DMA_RCV_OK;6 N: v7 M# K" }9 \! Q# J
  14. . u; U1 x0 j/ ~' K6 [2 m
  15. int main(void)5 d% T+ M: R6 i) N0 K* ]8 N0 b# B
  16. {3 S& Z- h# a+ U4 V$ ]- t! G" t
  17.       uint32_t i;
    , i% t- x% r; j5 E
  18.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
    + ~5 E/ Y( U4 n
  19.       HAL_Init();
    3 V) Y' x% L/ K9 O& Q
  20.       SystemClock_Config();4 h8 q$ w" ?. X+ |
  21.       MX_GPIO_Init();' ?6 x' Z+ s  @: a6 p: m" A* l
  22.       MX_DMA_Init();4 m9 B  b. n" q& ~5 x5 j5 g( o& d
  23.       MX_SDIO_SD_Init();, X! H! N5 l4 g3 ^# `4 ^6 `4 N! X. H
  24.       MX_USART1_UART_Init();/ S7 V" x, d' o( \# R
  25. 7 c* B2 r0 d- q! F- n
  26.       printf("this is sd test\n");$ G1 {9 ?) G* i% z$ d% A" @4 u0 d" h
  27.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印
    * G1 ]5 @+ q- U9 d  e' O
  28.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);: L0 J( V  e5 K9 ?  ?+ F. ^
  29.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);, c+ b: j/ W# }0 |' N# ~
  30.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
    - [6 s- b0 M+ a4 U" q( u8 `! Q
  31.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数, L7 u; ^4 T) J
  32.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
    3 S6 [* _) H) ]2 {0 h
  33.       /*------------------- SD DMA 写测试-------------------------------------*/& |% |+ B6 W5 O  m8 D8 N* {
  34.       memset(SDBUF_TX, 0x2, SDBUF_SIZE );/ R8 v+ |9 U5 ~. o1 ^! [
  35.       DMA_SEND_OK = 0;  //设置发送完成标志位为0,完成时为14 O2 G; s, |5 _- p* y/ c, f
  36.        /**6 |  F. t" s  u. O7 D5 z( H
  37.        //函数原型
    ( P: [( @; t  V" w$ l  ?% K
  38.        *HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, 4 D) S8 N$ P4 ]- Q5 [$ n
  39.                                              uint32_t BlockAdd, uint32_t NumberOfBlocks)
    ! @; [) u2 }" U2 q! [9 V
  40.       # B8 B- x# X' i8 M& c0 w/ A4 O
  41.         * @param  hsd Pointer to SD handle
    8 L: a6 K% b( l+ u" G
  42.         * @param  pData Pointer to the buffer that will contain the data to transmit7 y& }8 z+ _7 D; Z9 a- z, m
  43.         * @param  BlockAdd Block Address where data will be written  从哪个块开始写  C9 N6 O3 D, u# j. Y/ p
  44.         * @param  NumberOfBlocks Number of blocks to write  写几个块8 \9 E* A& H# Y9 n' w+ \! P" S, E
  45.         * @retval HAL status
    : O0 i. x( l. J; l
  46.         */
    ; J# L, o: k0 c4 \$ S" w
  47.       " [  r2 ?9 C0 `  _9 r
  48.       if( HAL_SD_WriteBlocks_DMA(&hsd, SDBUF_TX, 0, 1) == HAL_OK )' a2 j: B3 V. l
  49.       {
    2 H6 u0 y9 P0 N4 t  Y: z8 _
  50.           //等待DMA传输完成,并且SD卡状态为传输状态
    / Q7 f* r4 P4 W0 D; u8 p
  51.           while( (DMA_SEND_OK ==0 ) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));  
    ) {, x4 u6 c7 `9 Z+ t# v  b' P
  52.           printf("WriteBlocks Successfully\n");
    ' e' t5 i* o0 x5 p. d
  53.       
    * B4 M: a  U6 o  J  A0 M' M
  54.           for(i=0; i<SDBUF_SIZE; i++)1 i' u2 |. e$ R' N+ a/ d
  55.           {
    ( y: T' s, {7 r  q
  56.             printf("%d ",SDBUF_TX[i]);7 T0 I) a7 {/ l
  57.           }  @' v% A0 M+ X
  58.           printf("\r\n"); 7 @' U1 v, d2 y0 \# _% ?: ^+ l' T$ S
  59.           }
      Z) _/ m+ d7 ?
  60.           else: m, Z2 l$ Y8 W+ a8 e+ K
  61.           {
    $ n' O2 h9 H* o$ n
  62.              printf("WriteBlocks Failed\n");! [/ v1 O8 E& B, _# E7 G* g: l
  63.           }) ?) [& _' d- B; }$ M
  64.       
    ( P7 z0 P+ \- J. v, l8 ]
  65.           /*------------------- SD DMA 读测试-------------------------------------*/
    % H% |3 h. T2 k8 D. K0 f
  66.           DMA_RCV_OK = 0;   //设置读取完成标志位为0,完成时为1( p6 V* p6 H$ q8 N2 {* y* `% ~* p
  67.           if(HAL_SD_ReadBlocks_DMA(&hsd, SDBUF_RX, 0, 1) == HAL_OK)
    ; L! Y0 g$ j; M
  68.           {
    ) \$ B$ z* G1 |3 {
  69.               while( (DMA_RCV_OK ==0) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));         
    . h! `% V5 T5 Q4 t5 q
  70.               printf("ReadBlocks Successfully\n");          : o3 ]; f/ }* {2 j' p; `
  71.               for(i=0; i<SDBUF_SIZE; i++)
    ( |. U: G4 g. @5 t1 H! ]
  72.               {* _5 [, P# ~* @$ ^0 l
  73.                 printf("%d ",SDBUF_RX[i]);
    1 r, F& q3 ~5 K, a2 N1 u3 r
  74.               }
    1 G" s. e" j8 K1 b7 }7 E
  75.               printf("\r\n");& m* k- d# }5 w/ L+ O
  76.            }
    " k0 J7 _9 d8 t+ _: x) @
  77.             else% Y- W$ Y, @' h8 _' t
  78.             {
    ' |1 w" L, U8 b2 @, t
  79.               printf("ReadBlocks Failed\n");8 V' B/ c/ {; u8 S
  80.             }# r: n; `6 ], L3 d& U
  81.             while (1){}
    + z5 E9 ^7 t( m$ m
  82. }
复制代码

* L  u: z$ c# `
  1. 5 v9 R$ B: }4 F
  2. //sdio.c0 l3 x$ _  ]! c( [7 l

  3. 9 v( ~! R; W2 P3 ~3 i
  4. extern uint8_t DMA_SEND_OK, DMA_RCV_OK;- s+ {1 C# o' a- L/ Y, K
  5. //发送完成中断处理
    : V, Z' X2 g% S0 v" N' z0 s; I! ]
  6. void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)  {! d6 X8 x/ D2 B7 A
  7. {+ \9 K: O- o" u6 s
  8.   DMA_SEND_OK = 1;
    6 e0 F3 @0 B9 L( y/ E$ ]. \
  9. }
    4 C* a; u8 i8 M$ A4 M
  10. //接收完成中断处理; i3 h& L& e+ _. \0 |! B7 @
  11. void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)0 V/ h& f5 Z) [; F( X/ `- `
  12. {3 {7 {6 a* v* l" [0 z' p2 B. B
  13.   DMA_RCV_OK = 1;5 z. L+ t5 e5 R- y% [' ], n0 G
  14. }
复制代码
5 y* H9 W% f/ g9 [; ]* e9 ]" N
至此,ARM开发进阶知识已全部更新完毕!下一系列介绍物联网操作系统开发!) T0 ^. J7 A$ }" @6 {5 F
9 K5 ^# M" h; c  o( e' |

* [* k) |' S# V! @$ q7 F转载自: 骆驼听海
5 j5 h; a7 ~/ y  B9 @' A如有侵权请联系删除
) I& m; E3 R# J- @$ H- a" Z. p, V( }
收藏 评论0 发布时间:2023-4-11 19:23

举报

0个回答

所属标签

相似分享

官网相关资源

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