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

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

[复制链接]
攻城狮Melo 发布时间:2023-4-11 19:23
微信图片_20230411192319.jpg

4 K! D' b) `$ i
SD卡(Secure Digital Memory Card)即:安全数字内存卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。

& p( g( S# K6 }; y3 \
  • SD卡简介
    5 k% ?( f. Z. p# A# v* k
SD非常小巧,可以嵌入到设备中,在嵌入式开发中,产品需要存储一些容量叫大的文件,就可能会运用到SD卡。SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示。
: \& L, j5 r8 ^2 `
微信图片_20230411192315.png

1 _, k7 o3 x& @1 z1 B
容量大小不同的SD卡,其内部的操作标准是不同的。很多外设会明确告诉用户本设备最大能够支持容量多大的外扩,不能无限制的扩存,因为容量越大,操作不同,对控制器的要求比较高。当前设备的控制器不足以支持大容量,所以有外扩限制。
8 x0 J# y: v. H: t0 i
微信图片_20230411192311.png
7 {6 V& k$ B6 H% ~: A( I5 c+ g& S/ v
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。
: V9 w: ^  w. C+ E: u& R* f

1 \2 ^# Y% b% Z2 N8 e: w
微信图片_20230411192306.png ' P: z* V; _8 f  w% j

+ f# L! x6 z7 f' }
  • SD卡的物理结构
    % c$ t( j8 G. W& ?& d1 `) P
. [3 ~3 v' L  z& S( r" R( Q
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。

$ S/ K6 Y3 y/ T4 M/ d
微信图片_20230411192301.png
7 x+ r; ~% l$ |9 E+ f6 J- T
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。
3 I. f1 _2 r1 O, l

3 c, H) I* G! [$ M) B8 ]2 q! X
6 \  a, C" \6 M
存储单元:是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;
% y& q) n! O8 F2 n
电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;
- o8 u; h" L  n( J; X) |
卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;

. O* d& d; _# n+ G! c
接口驱动器:控制SD卡引脚的输入输出。

( s& P! C- v7 {6 D, M8 l
' w, t  ^; a" C* z8 k
  • SD卡内部寄存器

    $ ?; G6 T& |$ D2 W( k$ w" B$ G
SD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。
2 i5 H; n. I& ]3 \- @  d
微信图片_20230411192255.png

8 ?3 _0 c4 S  j) G+ v! o
  • SDIO接口

    1 r/ F1 `3 @$ c9 ]0 X& r
SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡都有SDIO接口。STM32F407系列控制器有一个SDIO主机接口,它可以与MMC卡、SD卡、SD I/O卡以及CE-ATA设备进行数据传输。也就是说SDIO接口不仅仅只会操作SD卡,凡是符合SDIO接口通信的设备都可以被操作。

" I* T' L3 q  _7 n; `* u
微信图片_20230411192250.png
: N. q( a7 ^& ?( 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 的通信模式。

* s6 R! S8 e* H" |  o
  • SDIO组成
    ( y4 ~% C8 {6 Q0 e
SDIO 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令和数据传输。
% D/ c, \) p3 z" a) [$ K
APB2 接口访问 SDIO 适配器寄存器,并且生成中断和 DMA 请求信号。

, K- _8 J7 C. F* @( N2 X! k
微信图片_20230411192246.png
6 D5 k2 R! b& D: @" u9 ~1 K* ^
注:
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 F% ~! w/ _0 O8 t
注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)。

# I" \! a# g; ]$ ]* n
微信图片_20230411192243.png

, ^" @. O; \3 K1 r
该适配器由五个子单元组成:
1.适配器寄存器块 :适配器寄存器模块包含所有系统寄存器。
2.控制单元   : 控制单元包含电源管理功能和存储卡时钟的时钟分频器。
3.命令路径 :命令路径单元向卡发送命令并从卡接收响应。
4.数据路径 :数据路径子单元负责与卡相互传输数据。
5.数据 FIFO :数据 FIFO(先进先出)子单元是一个数据缓冲器,带发送和接收单元。FIFO 包含一个宽度为 32 位且深度为 32 字的数据缓冲器和发送/接收逻辑。(一共32 个单元,一个单元一个字)。所有的数据传输都要经过FIFO,便于管理。
' {3 U1 L: M2 l: n; h
  • SDIO 命令简介

    $ M( H8 \/ l# J) L" c: s2 F  |% {
SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。
' C5 v5 W6 R4 V
SD命令格式固定为48bit,都是通过CMD线连续传输的,数据线不参与。

; k5 h' X9 ~8 i
微信图片_20230411192239.png

3 {5 x2 ~- t# X% Q' O
SD命令的组成如下:
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。
! e. j1 S: m$ ^/ C4 i9 B5 x& I
6 L) B5 |) L* e4 Z. C* l# Y( b
传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。

) M4 Z# D/ ?* ]) y+ H# a
命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。
9 R( G! I( L* w3 F7 ^/ J
命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。

6 P  C) ^; b" M& V4 \
地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。
/ u$ [2 x# S) F0 ?. H. ~0 w# X
CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。

9 q# M1 U) H& M9 i
SD命令有4种类型:
1.无响应广播命令(bc),发送到所有卡,不返回任务响应;
2.带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应;
3.寻址命令(ac),发送到选定卡,DAT线无数据传输;
5.寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。

! h( k- d! L" r$ g( H
在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD),也就是说在64个命令作为常规命令的基础上加了特定的命令
# |* q- `& S# O$ o4 L3 u3 g5 @" {
要使用SD卡制造商特定的ACMD命令如ACMD6,需要在发送该命令之前无发送CMD55命令,告知SD卡接下来的命令为特定应用命令。CMD55命令只对紧接的第一个命令有效,SD卡如果检测到CMD55之后的第一条命令为ACMD则执行其特定应用功能,如果检测发现不是ACMD命令,则执行标准命令。

' |) W# e! _& H( ^) f2 T
SD命令响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:

; J" [4 G: ]9 g6 \3 m' h
lSDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。

/ V& x' C7 k, g3 [6 W
SDIO读数据

. ?0 c3 I6 ]! }
微信图片_20230411192236.png
- v# n8 ?8 P. q  y: N( E! J
单个块读操作与多个块的读操作除命令不同外,还体现在读操作结束时,单个块读取一个块自动结束,而多个块还需要主机发送停止命令。写操作类似,也要多发一个结束命令,只不过写操作写数据前需要检查卡的状态是否为忙状态。
: a0 ?" u8 t+ Q3 O: O
SDIO写数据
) W1 u* T" t( E. L) L8 i& p
微信图片_20230411192231.png

: Y7 i  C9 @7 I
  • SD卡操作模式

    1 C' i: A" R, T6 P
SD卡有多个版本,STM32控制器目前最高支持《Physical Layer SimplifiedSpecification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。

$ M( u" ?# o3 h% [3 [, w* v3 i
SD卡系统定义了两种操作模式:卡识别模式和数据传输模式。
5 j; n; C8 R1 ?7 _3 G
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。

4 b2 b# v4 S( U2 I' w0 o; \
每个不同的操作模式下,SD卡都有不同状态,通过命令控制实现卡状态的切换,在不同的状态下做不同的事,比如在发送数据前需要SD卡处于传输状态,发送数据时,SD卡处于接收状态。
5 \- y4 L0 \6 n" I% [' z- n
微信图片_20230411192227.png
) w8 x8 ]: H3 J, q6 e
  • 卡识别模式

    3 P3 Q3 ?/ G# O: Q/ \6 X
微信图片_20230411192220.png

) C. k7 I, H* M
①上电后,主机发送CMD0让所有卡软复位从而进入空闲状态。
②主机发送CMD8确定卡的电压范围,并识别是否为2.0的卡
③主机发送ACMD41识别或拒绝不匹配它的电压范围的卡,SD卡需要工作在特定的电压范围之内
④主机发送CMD2来控制所有卡返回它们的卡识别号CID(128位)
⑤主机发送CMD3命令,让卡推荐一个RCA(16)地址作为以后通信的标识,之后都以RCA值作为身份标识进行信息交互。
3 ~2 W& B5 d6 f+ A8 ?# [
注:在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下,在SD卡初始化时,SDIO_CK不可以超过400Khz
. G$ k' D# _7 L8 b; @
  • 数据传输模式
    . @- q0 m0 X0 ?+ b) c3 D+ x
微信图片_20230411192214.png
4 y, X9 }$ p1 r: K& V; ?
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。通过CMD7命令加上RCA值来选定指定的卡,选中后SD卡进入数据传输状态,就可以发送CMD17读单个块,CMD18读多个块,读多个块时只有发送CMD12命令才会停止。SD卡再次进入传输状态,若不想对卡有任何操作可以再次发送CMD7命令加上RCA值来取消指定的卡,写操作与上述原理相同。
; U. ?3 a0 f3 V, k" V  F
  • SD卡普通模式操作实例

    7 a0 G4 [$ Z# r: C' F
实验内容:向SD卡写入数据后读出

* d. ?. b) B4 A& W
实验步骤:

7 ~, P( r/ S* t% P# W
1.配置RCC,与以往不同的是SDIO适配器的时钟是单独配置的,需要专用的SDIOCLK,标准工作在48MHz
1 ^1 R$ D3 T# |
微信图片_20230411192210.png
9 i" s; @: i" k4 d
2.配置SDIO
" R# h$ w9 l3 ^. _% I( Y! b2 V
* O& F' y' X8 R
微信图片_20230411192207.png

$ X7 |3 @0 t7 ]3 G
微信图片_20230411192203.png

& ]* w0 u0 l% W
3.编写代码

  1.   Z! C1 g5 _; @  q3 E1 I) @
  2. //mian.c
    * `0 O0 y3 C/ g
  3. #include "main.h"
    3 n* v" g* b+ v! ~6 ~/ w
  4. #include "stm32f4xx_hal.h"8 z$ W* S& w/ }1 A& u) L1 _6 [% \7 ~
  5. #include "sdio.h"
    " Z( }+ s1 @8 l  S; R: j% [2 U6 Z6 L$ o/ l
  6. #include "usart.h"0 I: N# p6 A5 i/ I3 b, y' f
  7. #include "gpio.h"
    . k! B' Z/ O5 Q( t6 B
  8. #define SDBUF_SIZE   10241 O. s7 j0 A: r, Q  @1 _" \# a
  9. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收; ^7 u' w& R/ n+ f; J2 Z7 z6 ^
  10. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出
    ) Y8 O7 T$ a, e, Z& A, b
  11. int main(void)
    ( G8 U% [2 U) V8 p6 R
  12. {
    8 H9 E3 o$ v: c
  13.       uint32_t i;
    7 t4 ]" F7 I% b8 K+ V$ t
  14.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息9 e  x* k6 R; p! r) u3 Y% [2 a% U4 E9 y
  15.       HAL_Init();
    : }) P! V$ g  i8 F0 E
  16.       SystemClock_Config();
    8 j1 ]; |$ f/ m  v: f5 b
  17.       MX_GPIO_Init();
    1 _; ^) H3 o; q; T1 ~+ U5 t
  18.       MX_SDIO_SD_Init();7 T3 _) b: n0 W8 B8 ]3 o" u8 U% j
  19.       MX_USART1_UART_Init();
    9 }6 Y2 }) f% G9 L+ l, y$ H6 I
  20.       printf("this is sd test\n");. ]* ?% Y- J: h. _( w7 O: B5 i# }
  21.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印
    . \/ O6 O- E, }6 v$ b$ S# ]) a7 _
  22.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);8 s2 N' N3 z% q0 k- W( V4 D
  23.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);  Y: f( M* p9 y6 L5 S+ B) B
  24.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
    ' C+ A! |4 E& r0 G  d8 r
  25.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
    , E" Y! T0 i' {: b
  26.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小4 x! \6 P) b3 R, o) h1 d3 W' H5 J
  27.       /*--------------------SD卡写测试----------------------------------*/
    8 t  \, I# T* y+ B9 f8 x
  28.       memset(SDBUF_TX, 0x8, SDBUF_SIZE);  /、填充TXbuf为0x8
    6 D) r6 N+ q1 _: W6 H
  29.       /**
    - \3 Z$ V" C3 ~7 n- ]/ \% p3 |/ W
  30.          //函数功能及参数描述                             
    . S" {% g% G5 p7 ^2 J
  31.         *HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, ' R! Z* k7 J# i) n9 f1 H/ P. \
  32.                       *uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)) o9 E3 [, T" r, S. e5 T
  33.         * @param  hsd Pointer to SD handle
    * }3 V  C: Y) H% N. L$ b
  34.         * @param  pData pointer to the buffer that will contain the data to transmit
    . O9 _; n: ?" ]/ E9 O
  35.         * @param  BlockAdd Block Address where data will be written,从哪一个块开始写  
    + ]' i: [- B9 w
  36.         * @param  NumberOfBlocks Number of SD blocks to write   写几个块. F+ K2 w" j5 C/ C9 A
  37.         * @param  Timeout Specify timeout value 超时时间! ]; t( Z% F/ l3 z' c& {1 D
  38.         * @retval HAL status- ^$ n* Z8 [2 H6 ~# Q
  39.         if( HAL_SD_WriteBlocks(&hsd, SDBUF_TX, 0 , 2, 1000) == HAL_OK)
    + \* f& C. @# R
  40.         {- r* }' K6 P$ q/ a4 v  E
  41.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//处于传输状态退出
    ! p2 P& \' p: J3 L2 V. ^
  42.             printf("WriteBlocks Successfully\n");" }8 z" ?  f' E! Z
  43.             for(i=0; i<SDBUF_SIZE; i++); B; ]+ m/ U2 `2 s2 G& a! ]
  44.             {
    & H, g: j& C7 `# ?6 G
  45.                 printf("%d ",SDBUF_TX[i]);
    3 q; @' H5 @6 j5 a; L( n4 ~  X; K) }
  46.             }% V8 N- A3 R) y. D" ?7 x- z# j
  47.             printf("\r\n");5 _( [8 T: S, {3 x' E9 A( k0 z! u
  48.         }; x$ L8 w/ K; a% A5 t* O
  49.         else# C, ?" A( Y2 Y/ I  x
  50.         {5 ?! G" L- [) g; w
  51.             printf("WriteBlocks Failed\n");
    , N4 n  ~  w3 l; y  D  R+ J  e
  52.         }% M: d( ?6 {0 c: g! J3 |+ S
  53.         /*--------------------SD 卡读测试----------------------------------*/. k) r1 Z$ L7 v6 k# T5 R" z
  54.         //与HAL_SD_WriteBlocks函数的参数功能相同
    - q6 i- `' ?5 [- l8 ^9 a2 p. _
  55.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
    ! y/ j6 P$ g; g2 c$ g" E
  56.         {
    , I( n& \5 R: y1 A
  57.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//返回到传输状态退出
    2 {! A/ }5 }5 _" \
  58.             printf("ReadBlocks Successfully\n");  x) j* Y. X: F3 u
  59.             for(i=0; i<SDBUF_SIZE; i++)
    6 t* _5 t0 ]9 Z" }. @, c( x$ E
  60.             {# \: _9 A3 i3 v* y
  61.                 printf("%d ",SDBUF_RX[i]);
      _" n7 Q7 h( R- y/ V) E4 v9 [
  62.             }; R8 R" c/ |1 I3 ~0 o, H& B
  63.             printf("\r\n");
    2 a3 [# D8 w8 R4 \3 k3 b
  64.         }3 Y6 f, x+ |, n9 V) r
  65.         else& l$ U* G9 @$ `3 `
  66.         {
    1 T2 }& [: E% _: g- b
  67.             printf("ReadBlocks Failed\n");
    * m5 D: j! e% J* c9 }: ?& q, z
  68.         }
    ! ^& |6 }- n% o6 b
  69.         /*--------------------SD 擦除测试----------------------------------*/
    * L. Q: H  S1 }$ Q
  70.         if (HAL_SD_Erase(&hsd, 0, 1) == HAL_OK )
    . [5 L5 Z) W% A2 N
  71.         {' Y0 ?9 D% v# A( M6 L: [
  72.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);+ t8 z1 P. e& `8 q* C2 d0 R# b$ \8 M  A
  73.             printf("SD_Erase Successfully\n");
    " ?# F3 O; {  p# m) ]: M
  74.         }
    & y4 C: f9 c8 T4 E) c$ H
  75.         else
    # V8 p! W1 w1 }0 K
  76.         {: R7 }* l. |8 m; W
  77.             printf("SD_Erase Failed\n");1 e& `2 E- @, N* u2 [! B, h" W* j
  78.         }
    + t( B3 v5 U3 H8 p: P, V+ ^
  79.         /*--------------------SD 卡读测试----------------------------------*/, E  K" w5 ]6 f& K. f" X
  80.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)* B  r) H: Y1 n: `* ]# T( W
  81.         {# l7 v: Y' `, i1 R3 p
  82.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);6 Q( G- u6 ?# N3 o5 V0 P
  83.             printf("ReadBlocks Successfully\n");2 j2 w' @8 g( ^2 O7 ?
  84.             for(i=0; i<SDBUF_SIZE; i++)# D3 f+ y' A- Y* [
  85.             {* y& }2 }9 ~- w8 S- r% w" G
  86.                 printf("%d ",SDBUF_RX[i]);; ^: z7 ]6 t! O. O9 J+ M' `* Q
  87.             }; {, V* h; s0 E- }/ E% _
  88.             printf("\r\n");
    4 r3 a+ v9 ~7 V
  89.             }: y0 ^) z2 S( B+ g. c! I; v3 o9 l
  90.         else  g6 Q$ u% D; u% v
  91.         {
    9 b! B4 K& z+ M8 G) g& s6 d
  92.             printf("ReadBlocks Failed\n");5 O. {$ K" r3 w" X
  93.         }3 B8 d# u" i+ ]: m! V
  94.         while (1)
    . i4 j4 T  l1 C, u
  95.         {
    ; X$ u. j/ ~( {# {7 E, ~" u7 L0 D
  96.         }  
    + W/ m4 W  C" A, [# E
  97. }
复制代码
  1. //sdio.c; z! j0 ?/ ~' k% G, q
  2. //此代码为工程自动生成,非用户编写,这里只做分析
    1 J; ^6 G* W$ Y" S/ r
  3. #include "sdio.h"6 ~( k% u( [1 M( l$ p
  4. #include "gpio.h"
    " x6 U; F. h7 a$ Y& l8 q9 @& h
  5. /* USER CODE BEGIN 0 */
    2 x3 O0 g/ W# v/ u: t5 A& w: a
  6. /* USER CODE END 0 */
    2 B( g* t$ t: a1 B$ X; @: f
  7. SD_HandleTypeDef hsd;$ J# u3 ~. u: r& R0 A
  8. /* SDIO init function */) ]. m. \: F; Y9 `1 m: A
  9. void MX_SDIO_SD_Init(void)   //初始化配置+ `( X/ J" O# ^  O0 x
  10. {
    . L) d8 b( K& ]
  11.   hsd.Instance = SDIO;    //SDIO句柄,整个系统中只有这一个
    2 Z; G1 C7 v# y  J: ~; e+ r
  12.   hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;& n# }9 T2 r) z7 C; g) P
  13.   hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
    8 b# p: N# {2 k! r
  14.   hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
    2 B' M$ O+ U( v5 S: |+ K
  15.   hsd.Init.BusWide = SDIO_BUS_WIDE_1B;   //初始化时设置数据宽度为1位, F$ L4 i$ I, ~1 O
  16.   hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;  p! q" K! J' e
  17.   hsd.Init.ClockDiv = 0;
    ) z+ r8 }( y( |  N" \; m  Y4 I
  18. //HAL_SD_Init()函数内部做了很多事去识别SD卡,具体的卡识别代码分析在下方  z9 m; U% L+ w4 _8 w) A
  19. if (HAL_SD_Init(&hsd) != HAL_OK) 7 D) W' W7 v0 K7 f# y) G
  20.   {
    8 _5 k* r! ~# e0 F$ g; f
  21.     _Error_Handler(__FILE__, __LINE__);+ A. Q/ u+ y, a6 t" @) o
  22.   }
    4 A1 _1 f0 ?- g9 H# ]* F1 Z- _* U3 `
  23. //初始化结束后重新配置数据宽度为4位
    4 I" ]1 B, N9 P2 {, F6 Q/ C) |
  24. if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
    ) _, j7 U: k. k: x4 e9 M4 E3 Z7 t
  25.   {' Z9 P8 w1 [) O- n* u6 o
  26.     _Error_Handler(__FILE__, __LINE__);
    " ]( i; `( t. E8 J
  27.   }, @9 {' k6 q' k$ ?* G. }
  28. }  N! L( g; k% T: w0 @
  29. //卡识别代码如下
    ! V- {0 w8 H/ J+ u  m! a& d
  30. HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd)
      \1 _& c4 Y; X
  31. {
    * U, s" W- T( x) K) `
  32.   uint32_t errorstate = HAL_SD_ERROR_NONE;6 w& }& W8 r* F) a, f1 n
  33.   SD_InitTypeDef Init;
    1 @$ q, A6 A1 f  s, v
  34. /* Default SDIO peripheral configuration for SD card initialization */" [+ s( a- G1 h, W8 s. G" z
  35. //完成卡识别4 A  k1 ?  g: I- L
  36.   Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;5 g: X0 r$ P  o5 F
  37.   Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;- {- c1 e  B2 v. |8 V' ^
  38.   Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;
    / b' N" z8 J$ [4 j* [
  39.   Init.BusWide             = SDIO_BUS_WIDE_1B;
    1 N: Q& I$ X% J/ T) n* i
  40.   Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;. E  `" m1 L+ i  u5 Z+ f. O- ?
  41.   Init.ClockDiv            = SDIO_INIT_CLK_DIV;- s3 |( l8 n6 M; Q! q1 u1 E+ h
  42. //SDIO_CK卡识别阶段要求时钟小于400KHz,宏定义: C4 M5 O. D5 t% |+ @) \! T
  43. //SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV)/ q8 ?, F, ]/ L6 H1 h
  44. /* SDIO Initialization Frequency (400KHz max) */; q2 v; h$ J; ?# C* N
  45. /*#define SDIO_INIT_CLK_DIV     ((uint8_t)0x76)*/$ a4 p" T# S# p0 a+ ~3 I& k/ E
  46. /* Initialize SDIO peripheral interface with default configuration */
    . B0 A$ D4 C1 V$ P+ M' j6 r9 C6 p5 T
  47.   SDIO_Init(hsd->Instance, Init);
    0 M. Y! _% j3 C, |- j
  48. /* Disable SDIO Clock */
    $ }' x. y' C' h. ]
  49.   __HAL_SD_DISABLE(hsd);
    ) }6 [, m4 B8 d. `
  50. /* Set Power State to ON 对SD卡进行上电 */
    0 k& F( I# C% X7 x9 [& F1 ^& L7 J2 I
  51.   SDIO_PowerState_ON(hsd->Instance);
    4 @9 K+ ~6 z: H& e+ i
  52. /* Enable SDIO Clock */
      Y9 {( o% Z; M2 m
  53.   __HAL_SD_ENABLE(hsd);
    7 b+ H, r1 e4 U% Z' F- l1 {
  54. /* Required power up waiting time before starting the SD initialization
    - e$ ~* b! Q# }1 S% e. X  j
  55.   sequence */
    9 e0 |5 S3 w6 t
  56.   HAL_Delay(2U);: K' a' N9 j3 Z
  57. /* Identify card operating voltage */( E+ u$ V) ]1 ~; {$ M. ]
  58.   errorstate = SD_PowerON(hsd);
    " _) _# |" S: I# y+ i
  59. if(errorstate != HAL_SD_ERROR_NONE)
    0 S: z- j- `2 ~) J! b! x, a
  60.   {% s8 _  k1 h% ~2 `+ @( I  ]
  61.     hsd->State = HAL_SD_STATE_READY;% T/ |. {/ \, B3 g7 Z
  62.     hsd->ErrorCode |= errorstate;4 l3 F( F# d2 a
  63. return HAL_ERROR;
    ' C1 O2 s, M5 T' _5 F. Z' ?) v
  64.   }
    ! [) F" I+ }+ ?  r  t/ c. A
  65. /* Card initialization */) k. E- A2 A8 @* s$ z/ |
  66.   errorstate = SD_InitCard(hsd);
    - p+ u0 ~- _+ E: x2 V3 u6 K& _# Q1 j
  67. if(errorstate != HAL_SD_ERROR_NONE)
    5 i+ o8 Z' X9 L
  68.   {- [2 n2 u+ W+ ^: D2 h
  69.     hsd->State = HAL_SD_STATE_READY;2 I& N( q3 T2 s2 D
  70.     hsd->ErrorCode |= errorstate;
    ; Y8 Y1 L7 B, B$ X- V: M
  71.     return HAL_ERROR;
    1 W& b( w: d  N, u2 @3 U
  72.   }
    8 v/ C$ B. F- e+ S
  73. return HAL_OK;
    , B% r: q" ^" j6 A4 A3 ~% o3 d
  74. }
复制代码

* E  i* C# h+ n9 u1 b
  • SD卡DMA模式操作实例
    * ]( }! `9 d- ^/ m! n4 a
    + L7 |1 B* e, _  G
当SD卡中有大量的音视频需要读取时,整个过程需要CPU干预,这样CPU的利用率就会降低,因此大量数据的传输最好启用DMA。
) S7 K( }& s6 T- x
实验内容:向SD卡写入数据后读出。
- a, r- G5 m& x% O  N" d" ?4 Q
实验步骤:工程的时钟配置和SDIO配置与普通模式相同,只有一点不同需要打开SDIO的全局中断,因为SDIO发送与接收完成后都需要中断去生成DMA请求。设置SDIO全局中断优先级更高一些,因为它内部还有很多其他中断,发生错误时更需要处理。

6 Z. P$ |- j/ L3 g
微信图片_20230411192200.png

0 A8 N& u2 y6 X% r) h3 A! m7 w
配置DMA
; P4 J9 p4 d% R5 u: i
微信图片_20230411192156.png

$ I) n( I- p2 E& \7 u: y: |# {  @' Z. Z
编写代码:
  1. //mian.c5 m% _1 M9 v2 \: g; O

  2. * w* i) {3 Z. f
  3. #include "main.h"
    2 X2 }9 Q- s% p/ y7 p; Z
  4. #include "stm32f4xx_hal.h"2 v0 T  z. R/ y/ ]
  5. #include "dma.h"/ \( Z2 c, K$ a& D6 Y% R
  6. #include "sdio.h"$ ?7 W/ ?/ q9 U5 `1 X% {2 M
  7. #include "usart.h"3 u: k7 ]0 Q6 `( n4 m, r
  8. #include "gpio.h"
    7 p6 `* q) {7 G+ P

  9. 5 f; O: u" |1 G) i+ e* s! U
  10. #define SDBUF_SIZE   1024
    % j# k3 S. Y- g+ W8 a; Z* {8 R
  11. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收, y% _8 }+ Y/ Q4 m) W0 u
  12. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出
    # ~2 z3 R1 T( W# r9 t. E* ~
  13. uint8_t DMA_SEND_OK, DMA_RCV_OK;
    ( @# N* q* Y3 d. ]( N  S, R+ ]/ j
  14. ' [6 T( }4 K: c/ y+ y. s6 ^6 \0 A
  15. int main(void)
    0 J# `7 O2 z. k
  16. {
    / }0 u3 r3 q1 h2 l: \% B6 A
  17.       uint32_t i;$ J: B" m# q. ?' s$ j
  18.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
    ; I4 C5 h7 ^( a9 T. [6 \
  19.       HAL_Init();
    3 P$ q& I4 c, r* `3 ?
  20.       SystemClock_Config();8 y+ x2 g* o/ D% l* `3 L8 h
  21.       MX_GPIO_Init();8 u2 M" Q: Q! v$ o, Y6 B1 F
  22.       MX_DMA_Init();: M$ E0 [+ d( P
  23.       MX_SDIO_SD_Init();+ U' Q/ L9 M& f' ~2 ~9 S
  24.       MX_USART1_UART_Init();8 n/ |- ]5 r9 _
  25. $ s4 {4 |$ R, U  d( w1 ]
  26.       printf("this is sd test\n");
    % O) ~' V" F" E7 m: ^2 X
  27.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印) M$ n" w1 a7 O; A$ E
  28.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
    ! |& K# T0 c7 d/ W: R
  29.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);* }5 V8 c' W3 K7 l8 @- b* y
  30.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本& q( y; o0 T; M8 x: X7 O% E7 m
  31.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
    1 {4 a/ k5 I: J+ I- \. a; E
  32.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
    + R5 m' z) W" n- q% X% D3 g: i
  33.       /*------------------- SD DMA 写测试-------------------------------------*/
    2 `- H& E/ x" M/ V
  34.       memset(SDBUF_TX, 0x2, SDBUF_SIZE );
    ; C& _6 A+ L, w+ s: R
  35.       DMA_SEND_OK = 0;  //设置发送完成标志位为0,完成时为1! P! w+ B- l9 z1 F
  36.        /**
    + J' Y3 }# I1 a4 x; [0 R7 }
  37.        //函数原型
    & @  i/ i) A8 I) Z% m5 e9 O# ^
  38.        *HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData,
    - h8 u0 r! l* W* c2 X+ k' |; G
  39.                                              uint32_t BlockAdd, uint32_t NumberOfBlocks)
    % j( l8 z: d5 ]7 r1 ~* h
  40.       
    . c) h: D6 `/ E+ H6 p" q/ S
  41.         * @param  hsd Pointer to SD handle
    * F5 H* N' C+ d1 D' }4 _) a
  42.         * @param  pData Pointer to the buffer that will contain the data to transmit* `  \% {" E/ h" k/ }
  43.         * @param  BlockAdd Block Address where data will be written  从哪个块开始写4 u; I& m& ?$ h- n
  44.         * @param  NumberOfBlocks Number of blocks to write  写几个块
    ) U0 y7 i' y: d7 h: r/ h' ^
  45.         * @retval HAL status0 i; M5 B" _& d4 V
  46.         */; q; \5 J* g: u
  47.       5 W; I9 J; z  p6 ^5 {0 r
  48.       if( HAL_SD_WriteBlocks_DMA(&hsd, SDBUF_TX, 0, 1) == HAL_OK )
    % o& X1 Y, r, `/ E$ q3 b& ?4 C3 r6 _
  49.       {
    ; w- I- ~1 Q  s) L" S
  50.           //等待DMA传输完成,并且SD卡状态为传输状态& @* Z, s& h3 O. W, k. D; o
  51.           while( (DMA_SEND_OK ==0 ) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));  3 R9 J! m1 K9 T' K% u
  52.           printf("WriteBlocks Successfully\n");, w. X5 E" Y  j) z& K8 c
  53.       
    + O1 e1 N9 j2 W" V# P
  54.           for(i=0; i<SDBUF_SIZE; i++)
    ; d3 L0 a0 u7 _# ]6 E! i! y
  55.           {' L' v4 b* h* u
  56.             printf("%d ",SDBUF_TX[i]);
    * [# k, G+ {: G' M: v
  57.           }. j: K9 ]& ?$ W
  58.           printf("\r\n"); . a; _% Y- P3 N! g/ ?. v
  59.           }/ C( c- F3 s. W( r4 h* T6 w
  60.           else
    7 L' l7 r4 h4 e0 @
  61.           {9 K8 Y0 A. f- \- r% {) t
  62.              printf("WriteBlocks Failed\n");6 N: T& q/ z' k' r; D+ z1 u
  63.           }) Y' P" x5 ~$ t% k# Z5 x0 Z
  64.       % X; |" z" D  I# K/ z( O6 T" ^0 e
  65.           /*------------------- SD DMA 读测试-------------------------------------*/7 d  }9 ^- _  ?; `2 t! H
  66.           DMA_RCV_OK = 0;   //设置读取完成标志位为0,完成时为15 [& N1 x* q6 H* Y
  67.           if(HAL_SD_ReadBlocks_DMA(&hsd, SDBUF_RX, 0, 1) == HAL_OK), d( Z2 [: |& J2 i$ K
  68.           {! d! ^7 V. m: S4 N
  69.               while( (DMA_RCV_OK ==0) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));         
    % l1 J  ]5 V$ j/ R7 g- Y9 s
  70.               printf("ReadBlocks Successfully\n");         
    " |. w: X  w, \$ f$ ?9 [
  71.               for(i=0; i<SDBUF_SIZE; i++)
    ( v! s' ?1 B4 G7 {5 R
  72.               {% R, V4 D8 D! M2 u
  73.                 printf("%d ",SDBUF_RX[i]);
    9 c3 B) e' S. K" [; v
  74.               }
    , |: k* t  O# z/ X" [+ @3 z5 S
  75.               printf("\r\n");
    ' q5 ]  z" a& U5 _4 {
  76.            }
    . f/ M, e$ d1 L6 `  o2 _
  77.             else( N5 q8 V( G5 c! A. y
  78.             {
    0 J# j8 e3 ]7 f2 a# q" ~% n
  79.               printf("ReadBlocks Failed\n");
    0 q! U- C6 r$ F  D" y4 g
  80.             }% u/ _6 ?: U- j3 p# C6 N' q
  81.             while (1){}& x5 W( P8 ?, {/ U( Y5 T
  82. }
复制代码

% [5 @) r' A* Z$ S- [3 G; F2 Y
  1. $ x8 [' a( M5 Y' c! o% g! r
  2. //sdio.c) _3 n- O& |2 a+ Y/ F* w: Z8 d' r. W
  3. , v/ w2 x/ [5 M- D
  4. extern uint8_t DMA_SEND_OK, DMA_RCV_OK;
    $ y/ ~4 D9 v2 X1 S6 o/ W! b
  5. //发送完成中断处理
    0 z. w0 k% R7 y
  6. void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
    % F3 `/ e) X. V. v# _1 n% `3 y
  7. {3 \$ }, e! b7 N/ X3 G! X, `! r) k" q
  8.   DMA_SEND_OK = 1;
      r5 j, D6 g+ A0 W7 ~# t
  9. }4 r% `. |$ f2 S
  10. //接收完成中断处理
    , c$ }$ D( v6 b1 j1 H
  11. void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)) q  h5 x4 D/ Y$ F0 z/ c
  12. {
    9 e; |% D  ?* P/ ?7 m
  13.   DMA_RCV_OK = 1;( _# i; W. Q, T. E
  14. }
复制代码
. e/ @. @2 d0 L  ^
至此,ARM开发进阶知识已全部更新完毕!下一系列介绍物联网操作系统开发!, Q! D% n( `; s+ Z5 `

; O3 n; H6 o8 g4 q5 j; o
' ]- b% o( y" l# t$ R
转载自: 骆驼听海
& U$ w4 L% v6 d' @6 ^如有侵权请联系删除2 ?1 ~% [$ I' h# s2 x( b9 f
收藏 评论0 发布时间:2023-4-11 19:23

举报

0个回答

所属标签

相似技术帖

官网相关资源

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