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

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

[复制链接]
攻城狮Melo 发布时间:2023-4-11 19:23
微信图片_20230411192319.jpg
' y8 T6 W& I, G0 l. x. U/ d$ {, t
SD卡(Secure Digital Memory Card)即:安全数字内存卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。
6 n" s* `$ j* V
  • SD卡简介
    : j; d8 w  z. E% D* _3 [
SD非常小巧,可以嵌入到设备中,在嵌入式开发中,产品需要存储一些容量叫大的文件,就可能会运用到SD卡。SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示。
+ ?/ `' m' K% V2 N. H
微信图片_20230411192315.png
; g/ g9 m( C6 t3 K  a- _& l) l
容量大小不同的SD卡,其内部的操作标准是不同的。很多外设会明确告诉用户本设备最大能够支持容量多大的外扩,不能无限制的扩存,因为容量越大,操作不同,对控制器的要求比较高。当前设备的控制器不足以支持大容量,所以有外扩限制。

- n  w; y- V' D7 e/ M
微信图片_20230411192311.png

8 I) f! R$ e6 E8 |' G$ e- U
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。

3 h0 J3 e+ [, a5 R
. Y& A4 Z# i' ~
微信图片_20230411192306.png ; F% a% B. \/ v6 Z  p5 \

7 o' @  a" r1 q' a2 m7 h
  • SD卡的物理结构
    $ ~# C* p6 h* v2 N& S8 l
# |. V5 m# s- {2 i
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。
, d  X9 T) ]8 [1 J: k
微信图片_20230411192301.png
' o& }, C6 v- C$ V
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。

+ A1 B2 Z( Y% G- R/ Q" W" c7 K9 @$ `4 H
! }% n1 ^# T& c0 U" I7 {
存储单元:是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;

. T; q4 I$ n: q: f+ l
电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;
$ [- @, c0 I3 d
卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;

5 J! q! X4 P5 x
接口驱动器:控制SD卡引脚的输入输出。
4 |* E$ U+ b- c2 S9 J- I, d9 |
. P7 ^; ?0 Z; J* }
  • SD卡内部寄存器

    1 T2 q2 J# j% V1 |& \
SD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。
1 I( a+ l, L' @, K. W  R8 S) v
微信图片_20230411192255.png
2 Y  r0 [* ?- d. K
  • SDIO接口

    2 j/ k* S. ]( Q/ v
SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡都有SDIO接口。STM32F407系列控制器有一个SDIO主机接口,它可以与MMC卡、SD卡、SD I/O卡以及CE-ATA设备进行数据传输。也就是说SDIO接口不仅仅只会操作SD卡,凡是符合SDIO接口通信的设备都可以被操作。
" b: M2 Y$ a$ y8 P. b
微信图片_20230411192250.png
7 n+ i+ o) O7 Z( O
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 的通信模式。
0 }$ Q2 V. s; i# s
  • SDIO组成

    $ G! v3 n+ A( r$ e+ D
SDIO 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令和数据传输。

8 b/ `+ n6 n/ Z$ L0 K2 {
APB2 接口访问 SDIO 适配器寄存器,并且生成中断和 DMA 请求信号。
- Z: d* L9 s9 i9 z7 Q
微信图片_20230411192246.png

! r; u; x- D' G/ _/ J4 b
注:
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。

; D; E2 A& z  s
注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)。
3 W0 i- k2 t/ q+ t
微信图片_20230411192243.png

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

; L2 ~6 l; Q1 p, E: C/ z5 q
  • SDIO 命令简介
    , i" w) M- m4 G# I4 y2 h; W& f
SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。
" ~8 T, X6 p0 S/ \3 I
SD命令格式固定为48bit,都是通过CMD线连续传输的,数据线不参与。

1 Y: P& A% U' B5 X
微信图片_20230411192239.png
) v4 q* k  g- n$ l
SD命令的组成如下:
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。0 c4 H* M. e! M/ p

% K0 M/ J1 m+ I6 ~* w
传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。

/ p, I! r6 C5 N2 O: y! c& S
命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。
9 d& g) B# }& k) ?7 a5 T# `: z
命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。

# _/ J8 R; s- Y0 S+ @( {
地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。
) m( L# o% X" o% c: \$ v
CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。
1 a% p* g, ^, j
SD命令有4种类型:
1.无响应广播命令(bc),发送到所有卡,不返回任务响应;
2.带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应;
3.寻址命令(ac),发送到选定卡,DAT线无数据传输;
5.寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。
2 `% O& s2 R* d1 V5 O6 C
在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD),也就是说在64个命令作为常规命令的基础上加了特定的命令
0 R5 t; _" ~3 L+ f4 E: E
要使用SD卡制造商特定的ACMD命令如ACMD6,需要在发送该命令之前无发送CMD55命令,告知SD卡接下来的命令为特定应用命令。CMD55命令只对紧接的第一个命令有效,SD卡如果检测到CMD55之后的第一条命令为ACMD则执行其特定应用功能,如果检测发现不是ACMD命令,则执行标准命令。

/ b1 o8 X+ S+ W7 h: X" C1 j
SD命令响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:
( }: @# X5 M4 d/ K. k1 p& |% p5 T+ c
lSDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。

; s3 t4 C1 e) a" \8 j0 X" D
SDIO读数据

2 g7 Y* c2 {8 x) D5 v/ d, X% u
微信图片_20230411192236.png

/ A! L! v# j- L! U
单个块读操作与多个块的读操作除命令不同外,还体现在读操作结束时,单个块读取一个块自动结束,而多个块还需要主机发送停止命令。写操作类似,也要多发一个结束命令,只不过写操作写数据前需要检查卡的状态是否为忙状态。

! R$ k; X7 }9 L
SDIO写数据
; `( S0 N% J1 o# ~1 W! B
微信图片_20230411192231.png
7 P+ G* @& A9 w5 F) r6 @8 g/ l
  • SD卡操作模式
    ! F$ Z' z1 v6 P$ ?( I7 I1 k
SD卡有多个版本,STM32控制器目前最高支持《Physical Layer SimplifiedSpecification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。
4 O/ w/ C3 Q# D
SD卡系统定义了两种操作模式:卡识别模式和数据传输模式。

' l: e5 t/ x! f" K5 c! E6 M$ Y4 v
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。

) z8 f* N: f& m, [" k
每个不同的操作模式下,SD卡都有不同状态,通过命令控制实现卡状态的切换,在不同的状态下做不同的事,比如在发送数据前需要SD卡处于传输状态,发送数据时,SD卡处于接收状态。
0 Q" o2 p( T7 C" }! _
微信图片_20230411192227.png
! a) G1 q0 B6 |3 p" }) R' q
  • 卡识别模式
    / N. T4 x5 h) S( W9 W
微信图片_20230411192220.png

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

6 |8 [- r0 g7 X, K( K" S1 ]( h5 x
  • 数据传输模式
    + \" T1 I9 f! p6 b0 C
微信图片_20230411192214.png

5 {. H! @3 X) V, l/ g5 r
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。通过CMD7命令加上RCA值来选定指定的卡,选中后SD卡进入数据传输状态,就可以发送CMD17读单个块,CMD18读多个块,读多个块时只有发送CMD12命令才会停止。SD卡再次进入传输状态,若不想对卡有任何操作可以再次发送CMD7命令加上RCA值来取消指定的卡,写操作与上述原理相同。

; `$ z6 U9 p  H- `
  • SD卡普通模式操作实例
    $ N6 s; ^% ?4 U' [& ?  p
实验内容:向SD卡写入数据后读出
  ^( ~  |3 m7 v0 |
实验步骤:
8 K# V, l  ?* a7 R- X' g: L) w2 U
1.配置RCC,与以往不同的是SDIO适配器的时钟是单独配置的,需要专用的SDIOCLK,标准工作在48MHz

* f# L9 z4 D5 R) r
微信图片_20230411192210.png

- @6 Y. \5 }$ m1 K  i8 a0 ]+ v
2.配置SDIO* f- W8 P- a1 K$ D
, d# e/ H! Y2 ^( l6 f9 S
微信图片_20230411192207.png

( g+ h% r/ E- z* ^* D8 a4 I
微信图片_20230411192203.png

4 J; ?7 o7 ^1 y
3.编写代码

  1.   Q+ C# d: ]( g! W# X! T: d
  2. //mian.c! d% ^  x. g- K5 F5 `; H# n, X
  3. #include "main.h"
    4 z: x5 {: s% T$ W6 Z: A/ ^, z
  4. #include "stm32f4xx_hal.h"; w/ a+ _8 Y/ l6 d* C
  5. #include "sdio.h"0 [- _8 [( |1 L+ U% T0 J
  6. #include "usart.h"
    2 X0 ^$ T0 d  _. q$ E/ e% h
  7. #include "gpio.h"6 {: N1 c. U! P1 E: I7 n. }) E( G
  8. #define SDBUF_SIZE   1024* A; i& }- E1 z' N0 d- ?1 E) U  ]
  9. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收
    / u7 m' c* X5 `+ o5 d; @- l/ g
  10. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出5 H& b8 x; t; k$ y2 M. b( ?3 c$ Z7 {" H
  11. int main(void)2 B8 ~3 M# p3 N5 y
  12. {' K- b+ u" H) x+ L9 _* u2 y& r2 i
  13.       uint32_t i;, I  p. \$ q( Y' @
  14.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息6 S: T; r7 p0 F
  15.       HAL_Init();7 C) I* A8 @" ~
  16.       SystemClock_Config();- P6 W- A! ~: s6 S# V
  17.       MX_GPIO_Init();
    # |" q9 q4 c& ?
  18.       MX_SDIO_SD_Init();: i1 c( L' }" o) S
  19.       MX_USART1_UART_Init();
    - u/ S& `7 M/ ^1 C( i( o
  20.       printf("this is sd test\n");* N: ?4 Y. t: Q) z0 `4 k2 D
  21.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印8 K* E$ Q4 |2 @2 e
  22.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
    3 d" R! z9 P5 k
  23.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);3 h0 f  |2 D$ G2 x. H- H: G
  24.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
    , X8 Q: S" @# j/ `/ Y
  25.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数% J. P! }7 b1 V$ p" D8 b; ?
  26.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小4 D  T7 a) y. ^/ G7 q
  27.       /*--------------------SD卡写测试----------------------------------*/" n9 k% w8 ^" x+ v: Q* _; f
  28.       memset(SDBUF_TX, 0x8, SDBUF_SIZE);  /、填充TXbuf为0x8
    , c/ ]2 W$ x- j
  29.       /**# G! t! E4 D6 E/ [! h% r. c
  30.          //函数功能及参数描述                             2 w% C% C! O- y* f% W
  31.         *HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, 5 v3 ?. Z6 ?$ g- }8 _
  32.                       *uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)1 r% S: B! H8 N6 m
  33.         * @param  hsd Pointer to SD handle$ o! D& v: G0 x( Y: \9 a& F/ ?+ p7 l4 \
  34.         * @param  pData pointer to the buffer that will contain the data to transmit+ c4 v. Q$ X; o/ f* p" P
  35.         * @param  BlockAdd Block Address where data will be written,从哪一个块开始写  7 z7 O2 f+ a6 i5 w1 n+ Q! O
  36.         * @param  NumberOfBlocks Number of SD blocks to write   写几个块) I; T$ H! \0 D  k* w5 c3 l
  37.         * @param  Timeout Specify timeout value 超时时间
    $ ~* `  k  P0 h/ i, ~+ D2 m
  38.         * @retval HAL status- A! @/ z9 q7 l) B, {
  39.         if( HAL_SD_WriteBlocks(&hsd, SDBUF_TX, 0 , 2, 1000) == HAL_OK)
    * d6 A4 K5 M6 [7 ~
  40.         {
    ' a; a0 U$ v$ A4 P! x
  41.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//处于传输状态退出. ^( ]0 G0 O, X0 n  ^! x
  42.             printf("WriteBlocks Successfully\n");
    # @: f$ y8 R* {3 H6 }
  43.             for(i=0; i<SDBUF_SIZE; i++)( e& i1 U1 s8 ^
  44.             {
    & \9 M  s& h' S- R. O  z& P
  45.                 printf("%d ",SDBUF_TX[i]);4 ^) p8 [& N; I; Z2 p! k
  46.             }
    ' B" m  z0 ?7 W6 g  m  m
  47.             printf("\r\n");
    6 U8 q9 w; j* ]
  48.         }
      `' z. O5 c' j1 S" h. l" K% Z6 `
  49.         else
    4 C0 O- O) @9 U# k2 [8 q
  50.         {
    ; h6 s% K' a: X) h7 M2 L# Q) }* B
  51.             printf("WriteBlocks Failed\n");1 F2 m" ?2 X/ r* v
  52.         }- B: A/ N5 ?9 d3 i
  53.         /*--------------------SD 卡读测试----------------------------------*/# ]7 Q8 r$ n4 [6 h
  54.         //与HAL_SD_WriteBlocks函数的参数功能相同- W2 n5 b! b1 S- ]
  55.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)' y/ ^. Z+ ?  s$ \2 \: t- y2 T
  56.         {& s  u7 f' K) N( y! G0 e; ]. u
  57.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//返回到传输状态退出3 a5 W/ ^# d' @
  58.             printf("ReadBlocks Successfully\n");/ J: Y0 Q6 w8 T
  59.             for(i=0; i<SDBUF_SIZE; i++)
    + C$ `( h9 {8 \0 U; H
  60.             {2 f8 [. p  ^2 a9 M" x7 V4 ?
  61.                 printf("%d ",SDBUF_RX[i]);
    0 o) e9 n4 w7 i: `
  62.             }
    ! y! p' S! V" k  P, \! t3 f' U( h& y
  63.             printf("\r\n");
    1 I+ b5 y, z: U+ G! [
  64.         }$ _" ^/ C2 {' v; @
  65.         else
    , e( B. T- g! Y: `/ s" q
  66.         {8 Y- Y( \1 s, x3 q
  67.             printf("ReadBlocks Failed\n");) Y4 N5 r/ d2 Z. J) |( R7 x: [
  68.         }' t6 a5 G$ x- Q
  69.         /*--------------------SD 擦除测试----------------------------------*/1 l* g3 E8 ~6 \8 Y
  70.         if (HAL_SD_Erase(&hsd, 0, 1) == HAL_OK )
    8 D, _( ?9 M: h$ G
  71.         {
    6 [" r1 O* o2 D9 p
  72.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);, \2 g6 @. h7 [1 B- l" E+ T, c
  73.             printf("SD_Erase Successfully\n");
    2 T" W" z; u0 H$ x6 u! a6 M
  74.         }
      ]' q0 e6 O& t; W! N$ _6 m
  75.         else! ?- J% ]' \/ X) |# ~, K
  76.         {; o$ ~, C: {2 ^4 E5 M$ a
  77.             printf("SD_Erase Failed\n");
    8 `% Y" L! `$ }+ M( O* P3 y
  78.         }' V+ Q' G5 z' g, P6 i
  79.         /*--------------------SD 卡读测试----------------------------------*/
    5 n! }3 s7 Z/ ?/ c% u
  80.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)' n. k: Y$ Y/ H$ I' k. Q% o) W; I
  81.         {
    + L0 y- S9 U2 l" J* V$ I" |3 {) N
  82.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);5 l# w( p* u" Y1 [' b. o) K
  83.             printf("ReadBlocks Successfully\n");2 k/ {  w5 B% `& a# ~% }2 v" p# @0 P" N
  84.             for(i=0; i<SDBUF_SIZE; i++)% x8 p4 p- e; a% {
  85.             {
    ; e7 N' e+ J. Q' p2 ^7 F. Y( K' R
  86.                 printf("%d ",SDBUF_RX[i]);
    + ~' t2 a- o: `0 P
  87.             }4 N$ G$ J. D* e4 T
  88.             printf("\r\n");
    , Z/ m2 t: F/ |. }
  89.             }
    - K! v; @. b" }2 I
  90.         else# j+ }4 A* R) \# Q0 d( a
  91.         {9 O4 q' d" s" Y# o6 i$ S+ P# S7 G
  92.             printf("ReadBlocks Failed\n");
    + k% V' `: V. n1 D
  93.         }
    3 e4 m5 x. Z; X$ I2 X% D
  94.         while (1)" u" z. w2 g+ B
  95.         {  O1 I; {( h! S1 ]
  96.         }  7 X$ U  W5 G- m' p
  97. }
复制代码
  1. //sdio.c
    : L* Y+ I$ M2 e- x
  2. //此代码为工程自动生成,非用户编写,这里只做分析# N) ?( I# M; N  o' g( u* H
  3. #include "sdio.h"
    4 x, f1 G8 Z# ]' p  \7 _, L+ Y5 F2 h: C
  4. #include "gpio.h"6 b) @" d- z3 o: Y3 ^0 v. j
  5. /* USER CODE BEGIN 0 */
    4 b; O& g3 T0 t
  6. /* USER CODE END 0 */% ?4 Y/ b7 V5 @' i7 ~2 I5 `
  7. SD_HandleTypeDef hsd;3 f3 G. T& }6 S" j
  8. /* SDIO init function */% a& W2 d- [4 b' B1 l
  9. void MX_SDIO_SD_Init(void)   //初始化配置: l# h, o' a. P  v2 C
  10. {, J3 p/ F- z. h, A6 P
  11.   hsd.Instance = SDIO;    //SDIO句柄,整个系统中只有这一个
    % k7 ~; A% i8 V+ ~
  12.   hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;* g& V4 P  Z! }
  13.   hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;2 j* C- T1 G- |+ I! k
  14.   hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;' n( `' c' I/ i  a. |8 w
  15.   hsd.Init.BusWide = SDIO_BUS_WIDE_1B;   //初始化时设置数据宽度为1位
    / I1 T+ ^! O# q* k% a& \
  16.   hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;2 Y: C5 J& u: m, o" \
  17.   hsd.Init.ClockDiv = 0;" _9 _& h# j! \
  18. //HAL_SD_Init()函数内部做了很多事去识别SD卡,具体的卡识别代码分析在下方; u: M+ C$ r9 d2 F0 K
  19. if (HAL_SD_Init(&hsd) != HAL_OK)
    * d' f4 u+ G0 e' D
  20.   {
    , j" J. w) o8 L& E( a
  21.     _Error_Handler(__FILE__, __LINE__);
    ! p2 [: x7 G2 f4 v; _2 \
  22.   }
    8 U+ |7 [$ D# c; E9 ~* T
  23. //初始化结束后重新配置数据宽度为4位/ I7 U$ a! Y8 R1 [4 L6 w8 ~
  24. if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) 1 N- u' {# O# r, I" k) T: V
  25.   {( N& j  ~" g4 t  ]" W; {, f- W0 _6 o
  26.     _Error_Handler(__FILE__, __LINE__);1 ^" b" z8 D$ i7 i: l( `
  27.   }
    : [" i1 W7 h7 J. [
  28. }
    5 E9 i: Z& m4 f* L* t( M
  29. //卡识别代码如下" ^2 Z/ U; I! L# n" j: g* ?
  30. HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd)
    + {  |( s/ _/ ]2 ^
  31. {! s3 }* _% u% A
  32.   uint32_t errorstate = HAL_SD_ERROR_NONE;/ a- E+ J' h1 u/ @1 X+ Z
  33.   SD_InitTypeDef Init;5 ^6 D5 A# h0 X- F. P9 }
  34. /* Default SDIO peripheral configuration for SD card initialization *// [% J2 F% \8 k
  35. //完成卡识别
    . w7 g! S3 x9 Z" _
  36.   Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;
    ( l9 x% P9 a# m* u
  37.   Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;/ _( ~7 S/ N) [; B, O3 r
  38.   Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;
    ! i8 v& f4 G9 B
  39.   Init.BusWide             = SDIO_BUS_WIDE_1B;
    & H: R4 y: O2 D$ I$ e3 h. H' u
  40.   Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
    ( u5 {% E" [7 J" A1 d; G+ a2 T8 g
  41.   Init.ClockDiv            = SDIO_INIT_CLK_DIV;
    3 e8 s9 k5 L& b" P4 k
  42. //SDIO_CK卡识别阶段要求时钟小于400KHz,宏定义
    $ y' z( Y3 i- @+ a4 N
  43. //SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV)  L( G9 X: n3 e( F7 a# o4 z/ j- W
  44. /* SDIO Initialization Frequency (400KHz max) */5 @6 Q0 _" z2 M) \
  45. /*#define SDIO_INIT_CLK_DIV     ((uint8_t)0x76)*/  h6 h. Q8 b$ E; \1 i# g5 g* A
  46. /* Initialize SDIO peripheral interface with default configuration */! P7 ~- `9 G+ v1 T
  47.   SDIO_Init(hsd->Instance, Init);
    " v. Q: \4 [" ^, ]2 P' S4 c
  48. /* Disable SDIO Clock */2 G5 y* B/ T( r( Y- U
  49.   __HAL_SD_DISABLE(hsd);
    8 g' p4 }$ l5 N6 o' l% ?6 B" t
  50. /* Set Power State to ON 对SD卡进行上电 */3 k" I; ?% [. B; G% O$ F2 M9 W. B
  51.   SDIO_PowerState_ON(hsd->Instance);
    ' w8 P: d1 x3 l$ L. B6 q
  52. /* Enable SDIO Clock */
    0 [- W5 @% w7 d. z1 o$ M* W  h
  53.   __HAL_SD_ENABLE(hsd);
    , Z0 k8 l: R! ~* Z6 y
  54. /* Required power up waiting time before starting the SD initialization
      R1 e! P3 U, P
  55.   sequence */
    " G, z; \6 i! C* X6 A4 @
  56.   HAL_Delay(2U);
    , A0 X. a! A# E! Q9 p
  57. /* Identify card operating voltage */9 P) S) x3 |- B  {$ r. }& z6 W! I
  58.   errorstate = SD_PowerON(hsd);
    7 @1 U2 X# D0 a& `/ H2 V+ I5 Z
  59. if(errorstate != HAL_SD_ERROR_NONE)! w( K/ z) Y6 e: G' n
  60.   {
    " m  A+ W1 z9 H# g; F' a4 O
  61.     hsd->State = HAL_SD_STATE_READY;7 V! O5 o0 A- {' m) ^0 r' l
  62.     hsd->ErrorCode |= errorstate;3 C. C- n! {  s# s: V; G# t
  63. return HAL_ERROR;2 ]) D, g( h, w7 d3 {
  64.   }
    . R( G# J! N# w" i% a0 o5 W
  65. /* Card initialization */
    % ~9 q( {4 s+ V5 s) C) K' F
  66.   errorstate = SD_InitCard(hsd);  X) N7 T( Q9 }3 Z) \
  67. if(errorstate != HAL_SD_ERROR_NONE), v5 M7 e# @3 `4 A8 P! F) `
  68.   {
    . G9 H" w. `0 D# B! \
  69.     hsd->State = HAL_SD_STATE_READY;
    + ?7 X  q( X( o; s: K" H
  70.     hsd->ErrorCode |= errorstate;
    4 \, P+ B( E( J" N7 z$ N1 Y; v  w, R! ]
  71.     return HAL_ERROR;, T, C' e. s" t8 A3 W7 i6 A/ B9 Q0 W
  72.   }# p" n8 o3 _; P3 b& {* W$ n
  73. return HAL_OK;5 ~5 [: e9 }) `! v  z
  74. }
复制代码
' L( s* E$ x1 H! u8 T' Y; e
  • SD卡DMA模式操作实例
    / j  F/ X1 Z) B0 f  C+ ?

    ( E0 c& M1 B2 a+ G
当SD卡中有大量的音视频需要读取时,整个过程需要CPU干预,这样CPU的利用率就会降低,因此大量数据的传输最好启用DMA。
( G( k- ?! ~. _% y* p9 G7 @
实验内容:向SD卡写入数据后读出。

2 t% a' j& ?1 w3 ]
实验步骤:工程的时钟配置和SDIO配置与普通模式相同,只有一点不同需要打开SDIO的全局中断,因为SDIO发送与接收完成后都需要中断去生成DMA请求。设置SDIO全局中断优先级更高一些,因为它内部还有很多其他中断,发生错误时更需要处理。
/ @+ u/ v7 M: R9 T  q. p$ I
微信图片_20230411192200.png
" T* P* Z% {7 ]8 X% v: `* n
配置DMA

, V: s  D1 N3 a- o
微信图片_20230411192156.png
4 ?( Q) z9 @' }! x. m
编写代码:
  1. //mian.c8 k" s* W+ \: m) J) ?# r

  2. 6 K; H( x: g) e2 s
  3. #include "main.h". _- _. e$ U+ X
  4. #include "stm32f4xx_hal.h"
    4 z1 b$ X3 J6 }3 P4 n# y5 i5 h
  5. #include "dma.h"
    , V9 {! g  A; e+ D' B
  6. #include "sdio.h"
    1 X0 p" E3 H# b9 \- i8 j
  7. #include "usart.h"" k1 O2 G# e7 y0 s8 B
  8. #include "gpio.h"
    ! @6 O" ~3 [6 c" e

  9. . c" i1 e# }( w6 d! ~# T. E! |
  10. #define SDBUF_SIZE   1024/ {! K' `0 h& u* N! g
  11. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收
    : A. t) V0 P* N) i) q( N
  12. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出9 `9 U: @- }3 O
  13. uint8_t DMA_SEND_OK, DMA_RCV_OK;5 v* L0 M. i$ \  G) R. o5 C2 q, i0 g- K
  14. 8 z6 ]% T0 x2 T, d6 A0 q3 ]
  15. int main(void)
    6 d* {6 F4 d1 U, \- N0 X
  16. {6 h5 \7 P6 L( ?6 b; _
  17.       uint32_t i;
    ! f$ F9 H/ _* U0 S5 c
  18.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
    8 Z% l4 v8 L. O* u9 ~2 H
  19.       HAL_Init();6 K, P& z8 O& k8 E& t
  20.       SystemClock_Config();
    4 R. @% j8 I. H8 }4 S# ?/ z
  21.       MX_GPIO_Init();
    # y3 D% u6 r" T; n5 x
  22.       MX_DMA_Init();& l. p9 {  S( O# g! ~$ i+ u
  23.       MX_SDIO_SD_Init();0 e  d7 q. G8 {5 L4 u' G" X! i$ i
  24.       MX_USART1_UART_Init();
    % l/ |/ w; P: F+ z% q
  25. & N3 |+ }# C, b  X6 U
  26.       printf("this is sd test\n");
    : K; M% l, v6 k& e$ [/ A7 }
  27.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印2 d" S- P6 w* x* q& q2 T# ~
  28.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);1 x( f/ O- l2 k# T$ x# y
  29.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);
    1 \) X( p( G* f- J* N
  30.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
    & @' S* Y/ t% [7 f
  31.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数; p8 x7 K  K9 m9 }$ G
  32.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
    ) Y6 D; ~: `$ Z: G' j+ S
  33.       /*------------------- SD DMA 写测试-------------------------------------*/
    1 ~5 U4 q3 u, o; l7 A3 E
  34.       memset(SDBUF_TX, 0x2, SDBUF_SIZE );  x8 ~+ S  B1 s9 E1 ]& R# l  R/ B. Q& P
  35.       DMA_SEND_OK = 0;  //设置发送完成标志位为0,完成时为1- s: B8 Z0 R: ~, E
  36.        /**% G5 B/ A) N$ @) M
  37.        //函数原型
    0 i' b$ {3 S% a1 P! s6 v- C
  38.        *HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, 4 \2 x  L8 }: c  O
  39.                                              uint32_t BlockAdd, uint32_t NumberOfBlocks)
    * b3 m# S" Y9 b& Z
  40.       4 y: l- i$ M. X; `  O5 G
  41.         * @param  hsd Pointer to SD handle# Q; K; D3 y! w1 g4 M
  42.         * @param  pData Pointer to the buffer that will contain the data to transmit" V( n' b  \' q3 P/ ]& h4 j
  43.         * @param  BlockAdd Block Address where data will be written  从哪个块开始写
    : o/ h/ |7 _5 V! |5 E
  44.         * @param  NumberOfBlocks Number of blocks to write  写几个块
    1 n2 t& F1 d  I
  45.         * @retval HAL status  d, O+ u5 `/ g% n0 L
  46.         */
    7 z+ [  ?. G* `; m" K
  47.       
    0 A! s" `/ [6 E
  48.       if( HAL_SD_WriteBlocks_DMA(&hsd, SDBUF_TX, 0, 1) == HAL_OK )! V% d# }: G! `, `9 B2 l
  49.       {+ s  S4 n; f0 X* w# e' [
  50.           //等待DMA传输完成,并且SD卡状态为传输状态6 @7 g* P4 s% X1 [# U  z
  51.           while( (DMA_SEND_OK ==0 ) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));  , j' b3 h. X4 S
  52.           printf("WriteBlocks Successfully\n");2 t; _5 L" e8 v2 J6 I( M
  53.       
    6 r6 c0 d+ U4 B, _* N6 {) D+ V4 G
  54.           for(i=0; i<SDBUF_SIZE; i++)
    # I* J$ ?; U/ U( U; ^! K
  55.           {5 Y& f/ ?2 F% Q, Y6 z0 ^3 `
  56.             printf("%d ",SDBUF_TX[i]);
    - b4 Q& J; ?0 s- N
  57.           }
    ! v8 Q) Y  [5 j  P/ O  ^8 \$ O
  58.           printf("\r\n"); 4 _* P2 B7 [6 @% F4 P8 o
  59.           }3 o; E6 g) ?* Z2 s* g( k0 T4 ]) @
  60.           else% Q! t9 K+ i7 T7 E
  61.           {
      f6 ~- \3 z' p* \0 g+ U
  62.              printf("WriteBlocks Failed\n");1 Q; v( @% N3 e! w. p
  63.           }, A+ n8 ^) y* U( @2 t0 P: w3 M
  64.       
    $ }5 m1 t7 g( J5 w8 S% C+ t
  65.           /*------------------- SD DMA 读测试-------------------------------------*/' _! a1 q, H7 _
  66.           DMA_RCV_OK = 0;   //设置读取完成标志位为0,完成时为1
    - F, ?( l& ~' V. P4 i/ R
  67.           if(HAL_SD_ReadBlocks_DMA(&hsd, SDBUF_RX, 0, 1) == HAL_OK)$ ^6 i0 A" r6 L. ~# g7 v9 n, l
  68.           {
    # a! c+ }' P' v$ J$ m* V
  69.               while( (DMA_RCV_OK ==0) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));         
    % k4 J" m; ~# A" }8 p
  70.               printf("ReadBlocks Successfully\n");         
    / Q5 o: {3 i9 T* ^; @
  71.               for(i=0; i<SDBUF_SIZE; i++)$ J$ \+ a/ R9 l! Q  V2 w+ x+ g
  72.               {
    & H+ A/ V0 p- F; t) k1 e2 X; U. V
  73.                 printf("%d ",SDBUF_RX[i]);
    9 S; r$ y) U% `, `' T
  74.               }
    . A, ]$ `, ]6 {
  75.               printf("\r\n");
    - K" R# w7 b" p* ?" D1 W, c" S
  76.            }" O9 ~7 y) o, W( H6 m# R* P
  77.             else
    - m6 _0 n1 v. G9 w2 d( U) L& Q
  78.             {
    - C0 r. J- o; H/ N# p2 X( l5 L
  79.               printf("ReadBlocks Failed\n");
    6 X* h9 P/ {1 {+ [
  80.             }# `# B4 ^/ p7 H% r) p
  81.             while (1){}+ G, O4 E! E( L8 @
  82. }
复制代码

$ u& K% S! a$ X6 X$ |7 X3 I
  1. 2 P  G# f# K! s) Q9 s0 `' Y
  2. //sdio.c
    : E/ h$ K# `; R$ l' y& u

  3. # o3 s# l1 ]  L4 i
  4. extern uint8_t DMA_SEND_OK, DMA_RCV_OK;
    " Z  X. l7 Z8 i7 D: k, _7 Q4 \) r, Z
  5. //发送完成中断处理" g% w/ _4 ]$ f. s6 U( X
  6. void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
    / M9 i0 |7 ]! q, b' [% O% K3 k, `/ ?6 ]
  7. {
    * J& p% I& X6 N7 j9 `( ~9 o6 F: d
  8.   DMA_SEND_OK = 1;2 R# A- J) ~3 U# Z+ Y" M
  9. }
    ' s2 z' W) Y4 M* Z2 J* Z( P
  10. //接收完成中断处理9 @$ o  D" _, a6 B! w$ b5 B
  11. void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)3 |0 l7 n- v1 `' z$ @8 @# |
  12. {
    " c5 |% x8 d0 W4 M+ E+ M
  13.   DMA_RCV_OK = 1;
    ! e9 s3 b8 Q7 e/ N
  14. }
复制代码
+ c9 `8 D* k. `3 [$ t! W8 M  `
至此,ARM开发进阶知识已全部更新完毕!下一系列介绍物联网操作系统开发!
( [, h" M! X4 v; P$ C1 a9 z  e/ H3 m' o! F

: F6 t! D  R% }1 J4 u! G. u转载自: 骆驼听海6 u1 K4 U& \6 G
如有侵权请联系删除  s/ D) ^! o* m; ~) P8 h- o
赞 收藏 评论0 发布时间:2023-4-11 19:23

举报

0个回答

所属标签

相似技术帖

官网相关资源

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