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

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

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

2 p. f. m$ W3 }
SD卡(Secure Digital Memory Card)即:安全数字内存卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。
+ p; n/ I3 j; M, P) N+ z
  • SD卡简介
    % X( L& ^1 h! G- z: c
SD非常小巧,可以嵌入到设备中,在嵌入式开发中,产品需要存储一些容量叫大的文件,就可能会运用到SD卡。SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示。

7 ^+ u( ?% ]1 X! H1 `' N
微信图片_20230411192315.png

& [. k; i( l, v
容量大小不同的SD卡,其内部的操作标准是不同的。很多外设会明确告诉用户本设备最大能够支持容量多大的外扩,不能无限制的扩存,因为容量越大,操作不同,对控制器的要求比较高。当前设备的控制器不足以支持大容量,所以有外扩限制。
0 \+ q% U' J4 \, v0 P+ `
微信图片_20230411192311.png

0 g1 v0 B- m- e: {0 r/ U0 T
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。

' \- a9 G; Y3 W4 ]: ]
5 k4 d- L$ d  q' E" M8 e& c( x
微信图片_20230411192306.png
" C6 Y' J5 ^0 R6 m5 @( A" Q) P/ Q: }' o% ]
  • SD卡的物理结构

    5 a: h+ Q" m7 t0 v% c: M

2 L. w- X4 Q! j9 {$ ]  r
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。
- l  Z# ~6 l2 Q$ L" S
微信图片_20230411192301.png
8 Y9 ^, f8 s1 p2 [
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。
- L: t" m8 F9 Z3 Z+ c. o
6 P/ M7 p$ q- {# R# m! Z% `1 C/ x

+ J' _; Y7 X  `) [  n8 j
存储单元:是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;
9 G, H7 z) Y+ n0 i, B% }! ?
电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;

8 ], O, A  O+ _  L2 X6 q6 d7 g
卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;

1 x# \2 @( @0 i/ f+ ]/ @" x
接口驱动器:控制SD卡引脚的输入输出。
8 M7 I( t) J/ g. O: E. v0 O

& [7 T! @& L7 p+ X+ j: |& o
  • SD卡内部寄存器
    1 l/ p8 h( i* a& R9 Z  ]# [# O
SD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。

' B8 Y/ _( g0 t4 f/ n& E
微信图片_20230411192255.png
! \2 n& c$ D7 X2 i. B
  • SDIO接口

    1 P* S2 _. }! U7 z2 w
SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡都有SDIO接口。STM32F407系列控制器有一个SDIO主机接口,它可以与MMC卡、SD卡、SD I/O卡以及CE-ATA设备进行数据传输。也就是说SDIO接口不仅仅只会操作SD卡,凡是符合SDIO接口通信的设备都可以被操作。

; x- j7 R& Y3 @
微信图片_20230411192250.png
4 w& x; {7 u* ~2 ?
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 K, a$ m6 g. q2 I7 f2 A  M3 k
  • SDIO组成

    ; o3 F- i1 W5 Z4 k' F. C, L
SDIO 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令和数据传输。
. s5 ^. h: }% c/ u( S
APB2 接口访问 SDIO 适配器寄存器,并且生成中断和 DMA 请求信号。

4 s, [( I# @: ]
微信图片_20230411192246.png

0 X1 J1 q1 @! Z! F4 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。

& L3 z6 L/ J) R0 ~- e+ s% M4 x* m
注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)。

, H8 x5 \8 P! n/ x; j8 c; ~6 R
微信图片_20230411192243.png
1 I5 B2 ~0 X4 Z8 O; _
该适配器由五个子单元组成:
1.适配器寄存器块 :适配器寄存器模块包含所有系统寄存器。
2.控制单元   : 控制单元包含电源管理功能和存储卡时钟的时钟分频器。
3.命令路径 :命令路径单元向卡发送命令并从卡接收响应。
4.数据路径 :数据路径子单元负责与卡相互传输数据。
5.数据 FIFO :数据 FIFO(先进先出)子单元是一个数据缓冲器,带发送和接收单元。FIFO 包含一个宽度为 32 位且深度为 32 字的数据缓冲器和发送/接收逻辑。(一共32 个单元,一个单元一个字)。所有的数据传输都要经过FIFO,便于管理。

3 e4 e( n* e+ v2 T3 X
  • SDIO 命令简介
    $ s5 @- K/ }. x+ V! L! |
SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。

: |: {- A4 ~  b; G* H. Z
SD命令格式固定为48bit,都是通过CMD线连续传输的,数据线不参与。

& H# Y  E' ]1 h* _! y4 {1 f
微信图片_20230411192239.png
. l5 Y% D7 W0 [) I' z1 i# ]
SD命令的组成如下:
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。
, b( m5 I! {* t
. [8 b5 }9 a' Z+ B9 @% s
传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。

% ?  O; _0 h' X. j$ A. r3 m
命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。

* I9 q; h0 U1 p' P, ]. _1 [/ Q
命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。

' y: y, A5 r: _
地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。
1 i1 F' |) Q% b
CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。

4 Q6 P# p0 r: |
SD命令有4种类型:
1.无响应广播命令(bc),发送到所有卡,不返回任务响应;
2.带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应;
3.寻址命令(ac),发送到选定卡,DAT线无数据传输;
5.寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。
+ V1 _, k( a8 @7 l# m
在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD),也就是说在64个命令作为常规命令的基础上加了特定的命令
8 h& G. W( q: T& R% y8 a7 |. l
要使用SD卡制造商特定的ACMD命令如ACMD6,需要在发送该命令之前无发送CMD55命令,告知SD卡接下来的命令为特定应用命令。CMD55命令只对紧接的第一个命令有效,SD卡如果检测到CMD55之后的第一条命令为ACMD则执行其特定应用功能,如果检测发现不是ACMD命令,则执行标准命令。
8 G  l. a1 ]  C4 P
SD命令响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:
) g6 P) W% |, v$ z: b8 `
lSDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。

7 {' l5 X2 t& p& L9 D3 _
SDIO读数据
4 F/ S9 C  q! p- ~2 a
微信图片_20230411192236.png

& a- j( o9 X2 H' Q
单个块读操作与多个块的读操作除命令不同外,还体现在读操作结束时,单个块读取一个块自动结束,而多个块还需要主机发送停止命令。写操作类似,也要多发一个结束命令,只不过写操作写数据前需要检查卡的状态是否为忙状态。

; m5 R( Z; e- j" O- v* Y
SDIO写数据
3 |8 F0 \) X; }- q2 b; v3 c5 }  q  Y
微信图片_20230411192231.png

( q- h0 [5 Z' J
  • SD卡操作模式
    ; y0 l* _( K3 b% V3 g
SD卡有多个版本,STM32控制器目前最高支持《Physical Layer SimplifiedSpecification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。
* T8 y* O8 |# G& x
SD卡系统定义了两种操作模式:卡识别模式和数据传输模式。
" T* H' q/ n: X
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。

; [1 T: H( w+ N8 L9 W  G0 z
每个不同的操作模式下,SD卡都有不同状态,通过命令控制实现卡状态的切换,在不同的状态下做不同的事,比如在发送数据前需要SD卡处于传输状态,发送数据时,SD卡处于接收状态。
  }7 {1 X+ v; v6 o( E8 Q6 _
微信图片_20230411192227.png

# p# Y5 {5 R3 _2 e0 ^' a
  • 卡识别模式
    . \% r3 A( R/ K7 p1 V* W4 y, A. o2 D
微信图片_20230411192220.png

; L" r2 C5 Z! S# d) b/ H
①上电后,主机发送CMD0让所有卡软复位从而进入空闲状态。
②主机发送CMD8确定卡的电压范围,并识别是否为2.0的卡
③主机发送ACMD41识别或拒绝不匹配它的电压范围的卡,SD卡需要工作在特定的电压范围之内
④主机发送CMD2来控制所有卡返回它们的卡识别号CID(128位)
⑤主机发送CMD3命令,让卡推荐一个RCA(16)地址作为以后通信的标识,之后都以RCA值作为身份标识进行信息交互。

% u% X3 z6 Y7 s- x; J
注:在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下,在SD卡初始化时,SDIO_CK不可以超过400Khz

2 [' Y; H  I7 h
  • 数据传输模式
    0 p& D6 s, ~" R7 o5 g5 S) ]! k! f
微信图片_20230411192214.png
$ p( j4 M- c0 h4 M& T9 e. f
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。通过CMD7命令加上RCA值来选定指定的卡,选中后SD卡进入数据传输状态,就可以发送CMD17读单个块,CMD18读多个块,读多个块时只有发送CMD12命令才会停止。SD卡再次进入传输状态,若不想对卡有任何操作可以再次发送CMD7命令加上RCA值来取消指定的卡,写操作与上述原理相同。

. d: A; ^2 S0 C* z9 P$ ^1 d4 @
  • SD卡普通模式操作实例
    1 a# O9 D, N$ v7 Y* f. d
实验内容:向SD卡写入数据后读出
& L  y% j3 c) p! A
实验步骤:

" Y* e3 w; F  x; K& [- ]1 j' V
1.配置RCC,与以往不同的是SDIO适配器的时钟是单独配置的,需要专用的SDIOCLK,标准工作在48MHz

: I- I! u" n9 q. P$ J' j( O; s& }
微信图片_20230411192210.png
7 C% }# d# k8 j6 W" d
2.配置SDIO7 D+ Y5 R; C' F# J' J+ b# [
7 @1 p, K/ O" D7 F1 h  k4 x9 C
微信图片_20230411192207.png

! J. M, }. i" F* [' S' W# r
微信图片_20230411192203.png

3 Q0 Z5 S& b2 o
3.编写代码

  1. : K& c9 I6 m) T0 r  e+ p8 d8 z$ _
  2. //mian.c  n; b2 W+ r: M2 ?) Z
  3. #include "main.h"
    7 t8 C; E" K; \# j
  4. #include "stm32f4xx_hal.h"
    8 m5 Q: R/ j) h1 e$ V
  5. #include "sdio.h"% n% p% C8 {& ?9 t* V
  6. #include "usart.h"
    : W1 b* O' `) Q5 E* @! p
  7. #include "gpio.h"
    * F7 ]+ Y: [* x9 ?7 I: A+ s0 C
  8. #define SDBUF_SIZE   10249 i7 g* n  u& }' K/ n: I0 u1 ^  s
  9. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收
    / L+ n$ ^$ i1 v0 @: g& j' G
  10. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出. Z7 P3 z$ J6 V: G* ^: B
  11. int main(void)3 j3 Z9 c$ {2 T; V5 W5 M
  12. {* U4 h+ X# {" |8 L2 p2 G: J/ I
  13.       uint32_t i;, J0 g% k$ ^4 O  X4 r% \, X
  14.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
    ' i; N( P% I, E0 O6 d- r
  15.       HAL_Init();1 }8 F5 u( \$ j6 s3 H; L$ b9 v
  16.       SystemClock_Config();
    ! @) V% q. P2 l$ k2 c
  17.       MX_GPIO_Init();
    * Y/ r& \! Y& e
  18.       MX_SDIO_SD_Init();
    6 K) k: t: u  p) {
  19.       MX_USART1_UART_Init();
    2 n! U& m2 [& c! q. f4 P) [
  20.       printf("this is sd test\n");( E. L5 Y* ^7 a
  21.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印
    & _/ e1 t' Y$ `# b
  22.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);, q7 J. ~+ P3 d
  23.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);5 e1 y% q8 z9 ]/ `* Y
  24.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本) |. h5 ~, d2 c7 ]/ ^
  25.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
    $ i" S3 U  `% A5 U' Z% T, d
  26.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
    ( [5 T! p9 F7 a. @) J, i
  27.       /*--------------------SD卡写测试----------------------------------*/* |- h' |9 O# A. R( g
  28.       memset(SDBUF_TX, 0x8, SDBUF_SIZE);  /、填充TXbuf为0x8
    6 f$ l0 B2 Z6 f6 h( f4 R
  29.       /*** A% G$ e; P* s6 a4 |2 b- [5 p
  30.          //函数功能及参数描述                             
    % y, {3 O: \7 J% O
  31.         *HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData,
    $ w& I. g0 H& S( u6 E
  32.                       *uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
    1 M7 L% }, P' D5 T
  33.         * @param  hsd Pointer to SD handle6 |1 A2 Z' ]# {
  34.         * @param  pData pointer to the buffer that will contain the data to transmit
    * r4 v6 j4 g: j+ g$ Z7 D  j# A( p
  35.         * @param  BlockAdd Block Address where data will be written,从哪一个块开始写  ( n8 R. g7 Z7 }- z  s
  36.         * @param  NumberOfBlocks Number of SD blocks to write   写几个块
    ! X+ F. V7 K( ^- U9 x, P
  37.         * @param  Timeout Specify timeout value 超时时间6 m: t1 O- z+ p/ q- |/ G
  38.         * @retval HAL status
    % H0 w% U/ y, G4 H# Y+ J
  39.         if( HAL_SD_WriteBlocks(&hsd, SDBUF_TX, 0 , 2, 1000) == HAL_OK)
    3 x8 F! H" t5 I1 p6 O, f: C
  40.         {
    4 w# ]" I. m; d  b' |4 k+ ^/ S
  41.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//处于传输状态退出; T2 Y/ R9 M+ z! p/ b$ T7 D; B& S
  42.             printf("WriteBlocks Successfully\n");+ \) ^  Z$ ?0 P2 w) d
  43.             for(i=0; i<SDBUF_SIZE; i++)
    5 D5 w" @) A* @( C9 I8 k/ E' h
  44.             {3 b$ O3 n9 y' I; b9 b1 a& F
  45.                 printf("%d ",SDBUF_TX[i]);6 v  t! X* M  x+ u5 O/ r
  46.             }+ I, {1 V6 q' f  B
  47.             printf("\r\n");1 t5 n+ k, C3 S) F
  48.         }/ H; ]7 z+ v# B! Z- u  S4 M8 g
  49.         else7 y2 s0 s, s5 w; f& ]. E+ ?4 d
  50.         {! w1 e' `) i" |
  51.             printf("WriteBlocks Failed\n");
    , y' W* y$ d! Y/ Q5 j5 d2 M
  52.         }& i) ^  T/ l, [
  53.         /*--------------------SD 卡读测试----------------------------------*/4 C; t' m7 N  e' t7 P3 \7 ^
  54.         //与HAL_SD_WriteBlocks函数的参数功能相同
    $ U& ~% H" |& [+ y1 K' X8 J7 `9 j- Y
  55.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)/ T9 ?: M7 i& N% p. u+ U
  56.         {
    $ A) K3 J7 b8 A( u
  57.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//返回到传输状态退出
    3 @7 v$ u5 u; I, L
  58.             printf("ReadBlocks Successfully\n");) X" T  }* X( n; x2 g  O
  59.             for(i=0; i<SDBUF_SIZE; i++)
    " B; h( J) t7 G
  60.             {, E' y+ n% J' f' e2 H/ G. ~
  61.                 printf("%d ",SDBUF_RX[i]);
    1 f5 }) k# a! U. P$ d
  62.             }
    - u4 _% G% `' U/ }
  63.             printf("\r\n");
    8 Z8 l' I( Q5 j+ ?8 }3 j' [8 e
  64.         }
    ; Y- N6 P8 V( r' z) M' s$ _6 N5 p' Z
  65.         else
    * K$ S" r* H( _+ j( U& |5 N) y
  66.         {
    # w4 p2 B- k, ~7 z/ Y& }. T# m
  67.             printf("ReadBlocks Failed\n");
    4 C: n7 o, w* a7 f% t
  68.         }
    % c- o  y: s4 d$ q* Z% M
  69.         /*--------------------SD 擦除测试----------------------------------*/& n. G; y& @8 z$ X( {
  70.         if (HAL_SD_Erase(&hsd, 0, 1) == HAL_OK )% k, ~. h7 d( k8 v
  71.         {
    5 e! H( h" A3 U$ ~
  72.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
    * @+ ^3 E; z+ a1 Z* [
  73.             printf("SD_Erase Successfully\n");
    % I; j( b; X  [
  74.         }+ a2 y4 L, I7 m4 K
  75.         else
    + z- C* A4 Z  f& m; u7 Q, H9 [( i& Q7 \5 q
  76.         {! @. ?# q, E" |# C6 j7 Z4 ~$ n
  77.             printf("SD_Erase Failed\n");
    2 }' h) T( |- Z9 x
  78.         }
    ) E/ G# F& y4 F+ H  Z0 R6 Q
  79.         /*--------------------SD 卡读测试----------------------------------*/
    & H* u8 m5 M) u9 ^2 C6 v$ R. y
  80.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
    6 x+ Z2 e# n1 V) w
  81.         {
    1 v: s% P1 C0 |- B; m) v
  82.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);9 P, t; G& ~. O, C$ C
  83.             printf("ReadBlocks Successfully\n");6 l2 |& q# G* ^( V5 \
  84.             for(i=0; i<SDBUF_SIZE; i++)
    ) B0 F5 I" D7 ^
  85.             {' }) d( H6 l0 Y
  86.                 printf("%d ",SDBUF_RX[i]);
    % n8 \6 O* f% [" r
  87.             }
    ( l6 W3 S1 ~* J' y
  88.             printf("\r\n");8 v6 _& L/ w$ K9 c/ Q/ [: t
  89.             }
    : e5 i0 e* f% }! r; V3 S3 Z/ B! V! y
  90.         else
    5 F# M  f6 E5 U/ B% b1 w
  91.         {
    # K; Z; q6 t6 v+ T8 }
  92.             printf("ReadBlocks Failed\n");
      A2 V2 e8 R8 l2 ~& z! E
  93.         }( }( ]! E' s. P* E( l
  94.         while (1)
    8 {1 K" K1 p: Z. a& q& K% w
  95.         {% F$ Q( r  Y) W9 s7 ]
  96.         }  
    ( ?- h! o( Z! @' Y! \
  97. }
复制代码
  1. //sdio.c* H- G5 m, M+ T3 f8 k0 ]( i6 d; {
  2. //此代码为工程自动生成,非用户编写,这里只做分析
    6 Y% k8 |. f6 _# X. O
  3. #include "sdio.h"
    7 F# {( v: @# e; l
  4. #include "gpio.h"
    ; V# y  B4 Y# \4 _
  5. /* USER CODE BEGIN 0 */
    ) A1 |* F! [. s6 H+ k, y  \
  6. /* USER CODE END 0 */
    ( M. o- K! Q( k1 t  v; y" U
  7. SD_HandleTypeDef hsd;
    0 R$ z: I4 O8 d8 G6 J$ f) Z
  8. /* SDIO init function */
    2 b0 g- s. C  ?% [
  9. void MX_SDIO_SD_Init(void)   //初始化配置
    ) @+ J1 {3 j" m& X+ q- o
  10. {/ p$ F; p' q8 G" C7 m# o3 ~! A) W
  11.   hsd.Instance = SDIO;    //SDIO句柄,整个系统中只有这一个0 o& K5 w% v4 D$ f3 n* t4 o1 Z; J2 g
  12.   hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;  v# E$ t% u/ S4 J) A+ @! n
  13.   hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;( `; \0 g; h, E9 u% f% s
  14.   hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;- y4 ?3 f$ L% z4 Y  c+ v, R) v* H
  15.   hsd.Init.BusWide = SDIO_BUS_WIDE_1B;   //初始化时设置数据宽度为1位
    ' E# z+ g; k% e5 q  Y9 f
  16.   hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
    / O$ ^$ n* x+ ]  s: }; b0 |
  17.   hsd.Init.ClockDiv = 0;
    , L- q9 M% E% P+ ?) T
  18. //HAL_SD_Init()函数内部做了很多事去识别SD卡,具体的卡识别代码分析在下方
    + O( s# J9 M7 t5 d: D2 E( u
  19. if (HAL_SD_Init(&hsd) != HAL_OK) ( ~6 O9 J  Q/ k. Z& A
  20.   {
    7 x4 ?. \  ?1 m( }
  21.     _Error_Handler(__FILE__, __LINE__);" p1 }( y1 d' L' Z5 J
  22.   }4 {( y1 o# k& F( u" p. G
  23. //初始化结束后重新配置数据宽度为4位9 x) X, Q! k: |0 g* v: \
  24. if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) 4 V4 X/ \: O' f7 t, S4 U
  25.   {. T4 {; y+ N8 \5 `% Y
  26.     _Error_Handler(__FILE__, __LINE__);
    1 Y2 E( c* {5 b, U' d
  27.   }6 O6 w( ~- h( {. k, o+ T* T
  28. }
    1 m7 d: ^. o" T& I2 S2 A
  29. //卡识别代码如下
    ' P0 [1 D* w: x: X
  30. HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd)
    5 X" K7 l! k' M: b( {2 ^, P
  31. {. m1 i, C6 [+ H( O% |  a# W
  32.   uint32_t errorstate = HAL_SD_ERROR_NONE;8 V' p4 _% o5 r/ ^+ x
  33.   SD_InitTypeDef Init;
    4 F4 L) e$ V. r' i9 h) [# |; c% q5 ~
  34. /* Default SDIO peripheral configuration for SD card initialization */  \5 q6 J" h0 X3 `# {+ \
  35. //完成卡识别
    4 ~8 p% q' r- _2 |  c
  36.   Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;* W! t( d0 T7 D2 z5 i
  37.   Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;0 W! D2 T! n; I2 C' }
  38.   Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;& L. k: }6 J- k6 B/ |& T, a/ ?
  39.   Init.BusWide             = SDIO_BUS_WIDE_1B;
    0 M& L! H2 Z7 {0 c; h/ w: d
  40.   Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;7 ?& l7 p; G' H9 x+ P
  41.   Init.ClockDiv            = SDIO_INIT_CLK_DIV;3 @+ O5 N. J. p% l" \; {8 ?# ?. H
  42. //SDIO_CK卡识别阶段要求时钟小于400KHz,宏定义
    5 j4 r  B( Y2 S. @
  43. //SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV)
    7 L- c3 g1 [1 l  o6 C0 b; k/ e
  44. /* SDIO Initialization Frequency (400KHz max) */
    8 ~# X/ ?! V8 b! _+ ^3 [0 `
  45. /*#define SDIO_INIT_CLK_DIV     ((uint8_t)0x76)*/
    , D( M' x. ]3 M4 ]
  46. /* Initialize SDIO peripheral interface with default configuration */
    ' g6 x& p, L: B9 Z7 U
  47.   SDIO_Init(hsd->Instance, Init);  T* z* J. c& J9 q
  48. /* Disable SDIO Clock */$ ~8 L- R* L# W/ e3 c
  49.   __HAL_SD_DISABLE(hsd);
    ( T% z* D5 G0 I
  50. /* Set Power State to ON 对SD卡进行上电 */
    0 A& J; s4 N2 v2 U$ j4 ~7 f
  51.   SDIO_PowerState_ON(hsd->Instance);& U! |& j: P' w2 u- [6 F  ]
  52. /* Enable SDIO Clock */
    " _2 B# M  Q" a* T
  53.   __HAL_SD_ENABLE(hsd);( x; P, K' S0 x& N- T
  54. /* Required power up waiting time before starting the SD initialization & N! u+ Z, K7 i% [( L# A( _3 h  P
  55.   sequence */
    8 o7 B) z4 K2 X* K1 I* H' b( s0 L
  56.   HAL_Delay(2U);
    . }9 c0 O6 o1 b4 b4 {$ v. A# x" }
  57. /* Identify card operating voltage */
    ; k9 m7 i8 D4 f. A8 g
  58.   errorstate = SD_PowerON(hsd);
    , J6 {0 M& n1 x) \
  59. if(errorstate != HAL_SD_ERROR_NONE)
    : h/ M3 R# X- j2 @# e/ }
  60.   {1 n+ Z; e: N# N  o/ i+ k$ R* b( F3 K/ P
  61.     hsd->State = HAL_SD_STATE_READY;! E7 G2 M; o! D, z$ M% H" b
  62.     hsd->ErrorCode |= errorstate;
    5 F2 `6 N& ~+ E! ~
  63. return HAL_ERROR;) P; Y/ H! @5 b" e) O
  64.   }! j# n5 O; X" ?4 ]- _$ ~4 x5 A
  65. /* Card initialization */
    * r  A. \0 K" }0 c* h* {' l
  66.   errorstate = SD_InitCard(hsd);
    , f2 [# `9 X8 m. Z
  67. if(errorstate != HAL_SD_ERROR_NONE)" m, D7 R" J' Z2 j( l" x: }; y
  68.   {
    , |: ]' Q7 }5 L  w9 l  Y
  69.     hsd->State = HAL_SD_STATE_READY;
    4 N# l% n3 }) B* t% q9 ~+ o
  70.     hsd->ErrorCode |= errorstate;
    3 c8 P& Y% M- |. }3 b  d
  71.     return HAL_ERROR;
    2 Q# s; `$ K9 D! g# q, i
  72.   }- p  F. j9 r# r3 i0 G' h1 H
  73. return HAL_OK;
    / q7 [) K+ o+ ^8 T+ q
  74. }
复制代码

8 e8 {* `/ {7 Y7 C! p. X
  • SD卡DMA模式操作实例+ [  ~" R. u" h
      g* W3 W: R6 K
当SD卡中有大量的音视频需要读取时,整个过程需要CPU干预,这样CPU的利用率就会降低,因此大量数据的传输最好启用DMA。

  w3 V+ H0 O4 w  \* F3 K* A: h* k
实验内容:向SD卡写入数据后读出。

0 j  j) p4 V* W9 n5 S
实验步骤:工程的时钟配置和SDIO配置与普通模式相同,只有一点不同需要打开SDIO的全局中断,因为SDIO发送与接收完成后都需要中断去生成DMA请求。设置SDIO全局中断优先级更高一些,因为它内部还有很多其他中断,发生错误时更需要处理。

) c$ L+ r$ C- ~( U8 I
微信图片_20230411192200.png
1 ^3 e2 i3 T1 V8 O8 i! i
配置DMA

  [6 V4 v0 U' t! C3 z
微信图片_20230411192156.png
2 B& C8 e" i1 b% a( [- f7 X* @$ E
编写代码:
  1. //mian.c" a: A$ \: S& n5 W0 g+ t3 k
  2. : d" a. M1 a$ e- s
  3. #include "main.h"* a2 b/ f  i( y# e$ n1 ?5 t
  4. #include "stm32f4xx_hal.h"6 {7 Z( W1 R1 P- e- U
  5. #include "dma.h"
    * z' q* q# D) A8 C
  6. #include "sdio.h"
    + p, c: N9 e% k9 g" l) G3 m
  7. #include "usart.h"
    0 ?% ?& @' u0 o
  8. #include "gpio.h"
    * c. S, m* g7 C+ t/ {: f1 t( l/ ~
  9. 0 r/ e$ I  o' G2 i! Z
  10. #define SDBUF_SIZE   10243 A1 S# \$ n4 r' W% Z
  11. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收) D" Z  ~- N2 r6 y% s
  12. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出9 W' r' a+ h9 N# c
  13. uint8_t DMA_SEND_OK, DMA_RCV_OK;5 t  K' ^1 F4 L  z6 ?

  14. 8 U4 X/ X4 y- U7 M( C
  15. int main(void)1 R" l( K- @8 L2 s# ~/ G  l
  16. {# T$ g+ d$ ^. i3 Y0 Y5 o. h
  17.       uint32_t i;% t; ^4 u+ J9 W6 T2 g, H6 ]7 Y) k
  18.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
    , c( M, M! {1 a. c+ R2 o/ U
  19.       HAL_Init();3 ]0 X9 J+ O/ ?1 O! @# u8 p
  20.       SystemClock_Config();5 b6 l# q( Z: c( |7 C$ q( s
  21.       MX_GPIO_Init();
    , Q: N5 J; d" a' l
  22.       MX_DMA_Init();0 g" ^0 p. ^5 F: `6 `* r) P- y. c
  23.       MX_SDIO_SD_Init();+ R$ h) h* P5 p2 J4 T
  24.       MX_USART1_UART_Init();) G& e3 R' a, N* L& X

  25. " y+ K; O" }5 a* J8 y" ?0 T
  26.       printf("this is sd test\n");% U# v& e& G: q( U  g  a& a+ F
  27.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印
    5 ]# K/ p3 y, j) U
  28.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);. M) K, T1 F% r$ t! D( Y% B, ~
  29.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);- J5 J0 @' ^/ u$ h! V) i
  30.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本% O+ j6 K$ h0 `# E- v
  31.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
    6 {5 c; f/ M3 v0 G  G- h3 C
  32.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
    ! F0 x$ y9 n9 ~
  33.       /*------------------- SD DMA 写测试-------------------------------------*/
    $ i0 f- h4 h' f6 E3 O0 o
  34.       memset(SDBUF_TX, 0x2, SDBUF_SIZE );( ^/ h8 j! e4 J) l$ H5 |2 s: w$ ~* `  h
  35.       DMA_SEND_OK = 0;  //设置发送完成标志位为0,完成时为1
    3 T  D. t: {0 E
  36.        /**
    - P# e7 p. Q! U/ P3 G* i  I# o7 @: j
  37.        //函数原型& q. l. a5 p) b2 G0 r, u1 n
  38.        *HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, 0 S) J8 Y; {  T* N2 t4 h, q" Q$ T" o
  39.                                              uint32_t BlockAdd, uint32_t NumberOfBlocks)
    6 w; y$ P  t( l5 V+ N8 q7 l
  40.       
    * i; ?: D, r" u7 l, q) u& v  Y
  41.         * @param  hsd Pointer to SD handle
    1 }! @* q9 D9 c5 x* L# h
  42.         * @param  pData Pointer to the buffer that will contain the data to transmit
    , J2 u# T" T6 ]; E
  43.         * @param  BlockAdd Block Address where data will be written  从哪个块开始写
    . L7 P& W& i# D% S  g
  44.         * @param  NumberOfBlocks Number of blocks to write  写几个块7 i6 c/ O* q+ \
  45.         * @retval HAL status; p1 A1 F/ d5 C5 p8 o, B4 W! G# P, k  m5 v
  46.         */. {7 A! I  P# n) ^* S8 P& r/ [
  47.       
      t. n% t" Y4 T0 Q. S: z% v1 ]4 A
  48.       if( HAL_SD_WriteBlocks_DMA(&hsd, SDBUF_TX, 0, 1) == HAL_OK )( K+ Y0 O9 L" X& z& t5 w5 C' }
  49.       {
    # f, ^4 f: [4 i- ~$ g1 @1 k# O; F
  50.           //等待DMA传输完成,并且SD卡状态为传输状态
    2 ?9 x9 ], s9 b6 L  K
  51.           while( (DMA_SEND_OK ==0 ) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));  , t! l% O9 j: {
  52.           printf("WriteBlocks Successfully\n");/ F/ P" a# P* Z3 b% U
  53.       
    4 X* e8 I2 k5 h" D
  54.           for(i=0; i<SDBUF_SIZE; i++)2 Q" @0 A4 r& q* b( n  y1 ?
  55.           {+ p& O# e: F: X7 v1 b( K4 o
  56.             printf("%d ",SDBUF_TX[i]);
    6 W: f1 I9 D( ^" O; j  k
  57.           }8 P! K$ M0 S# l: v2 v& q
  58.           printf("\r\n"); ' d7 E1 d; X) _+ j
  59.           }2 e: ~6 J1 J! c# j
  60.           else  Z; q! _# m% Y. s( ~" U2 }7 N- \! r
  61.           {
    " X: L+ @9 ?% i6 Y7 x; {& s
  62.              printf("WriteBlocks Failed\n");
      T) `# Z& A' A6 x
  63.           }1 c* F4 E' h- \) N4 I% r
  64.       
    / L0 U1 Z  T& A3 K
  65.           /*------------------- SD DMA 读测试-------------------------------------*/" Z/ |, {8 b3 d
  66.           DMA_RCV_OK = 0;   //设置读取完成标志位为0,完成时为1* Z* }& H+ O1 l! c  |
  67.           if(HAL_SD_ReadBlocks_DMA(&hsd, SDBUF_RX, 0, 1) == HAL_OK)* `$ m2 d8 s6 d3 Z4 @7 g
  68.           {1 }0 G1 t& c5 h- g+ I! F
  69.               while( (DMA_RCV_OK ==0) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));          ) O! w# [* @! K# o, Z- t! l7 }. g  r2 k$ Q
  70.               printf("ReadBlocks Successfully\n");         
    . j: P" Y9 H3 i5 }/ Q- F" o8 j
  71.               for(i=0; i<SDBUF_SIZE; i++)
    4 v5 A3 o5 r' _+ S& V  G/ X
  72.               {
      {. P; |1 x, B  [5 p
  73.                 printf("%d ",SDBUF_RX[i]);! h: b$ ^4 O+ y+ N/ l9 V, q) L  \
  74.               }
    ; }& ?1 O0 f6 b1 `
  75.               printf("\r\n");
    ( c: e3 ]9 B0 ^! |' X7 c" \! A
  76.            }* {) R, B! T6 {9 _/ m
  77.             else
    4 q' ~: V2 S$ q
  78.             {
    5 r9 K  V' l& a8 {% t$ ?* G4 ^5 x
  79.               printf("ReadBlocks Failed\n");
    ! k5 c* E' k" j8 N& g: h' T
  80.             }
    $ D) I% w! b1 Q( C5 o2 s
  81.             while (1){}0 ]6 W( ~0 ~, o# i7 y. y
  82. }
复制代码

  |+ [% e, p. B4 s

  1. . z+ F4 e9 G8 M0 x0 ~* ]/ q( F9 h
  2. //sdio.c
    5 Y$ V9 F- V0 `
  3. - f# D5 q1 I5 o$ ]; J
  4. extern uint8_t DMA_SEND_OK, DMA_RCV_OK;, w; {, M, S, |; K
  5. //发送完成中断处理8 Q2 h- G6 k* `+ Z3 N2 [: }, ~) V
  6. void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
    " e( Q' X8 i/ Q
  7. {
    ! B! ]: X' e1 d- g( f% J' u; d
  8.   DMA_SEND_OK = 1;
    2 q1 T% w% {  h0 r- U
  9. }
    ; s, \2 ?( c4 h/ ?0 i
  10. //接收完成中断处理
    - Z$ R" w( o2 m: |0 f1 }9 D
  11. void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
    ! M- B# N8 l" k) k! X4 F4 O
  12. {: e5 [+ C. ?6 ~: u
  13.   DMA_RCV_OK = 1;
    ) Y, T$ Q& s8 v5 S
  14. }
复制代码

: Z2 {( ~, C% Z8 A$ r8 Q4 I5 m1 B至此,ARM开发进阶知识已全部更新完毕!下一系列介绍物联网操作系统开发!2 G9 {+ Y9 e& _% S1 M* _
) K3 G% |5 ]: l5 r1 `# b/ w

4 g4 U: ^2 E3 X. Q, {转载自: 骆驼听海
% n- M6 \' }: ~4 r/ @& g如有侵权请联系删除* C0 b  f6 w8 i) B0 L
收藏 评论0 发布时间:2023-4-11 19:23

举报

0个回答

所属标签

相似技术帖

官网相关资源

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