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

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

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

& F# w3 y& \4 w  y1 K: M. Y
SD卡(Secure Digital Memory Card)即:安全数字内存卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。
4 }4 u9 Y8 M6 p. G' Y0 a, y( _, e$ e
  • SD卡简介
    2 ~# B# l- ^. {2 u3 `
SD非常小巧,可以嵌入到设备中,在嵌入式开发中,产品需要存储一些容量叫大的文件,就可能会运用到SD卡。SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示。
+ z3 O! U: N: ^0 @2 g, g( V# W
微信图片_20230411192315.png
; N% d' S: ^' ~( b: m3 s
容量大小不同的SD卡,其内部的操作标准是不同的。很多外设会明确告诉用户本设备最大能够支持容量多大的外扩,不能无限制的扩存,因为容量越大,操作不同,对控制器的要求比较高。当前设备的控制器不足以支持大容量,所以有外扩限制。

4 O0 S6 x) m: [- s1 ?0 D! A( w# i
微信图片_20230411192311.png
/ R; K5 ~/ A. }# j" I0 \. U
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。
8 _7 r# ^) T) T. i$ L0 i

+ V* Q. v2 r- ^: V" n; t
微信图片_20230411192306.png
, b0 C# i& Q. Q; g' I/ d
, \8 U0 ^6 W3 k2 M4 t; T
  • SD卡的物理结构
    ' ~6 S1 q- E( S: h  ~. a' E
- x7 m# t! A$ B( P" V5 R
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。

; u9 ?+ V/ _* t# O
微信图片_20230411192301.png

+ F, p; Y; I! i# {, K. r
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。

4 ?4 ~; [7 Q+ y2 U9 A7 |4 }# Z, r" V7 J6 [% C- i3 {

/ E3 c3 H1 T$ J
存储单元:是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;

  b  }% _* g$ r$ R
电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;

) ?+ J$ L+ P, l! M& n: \
卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;
4 S8 j% l1 i3 |8 o7 H/ F6 c  ~) g
接口驱动器:控制SD卡引脚的输入输出。
+ g; `$ i  {+ S8 Y7 D+ }1 @0 [
- b! b8 Q: r7 C2 A6 S
  • SD卡内部寄存器
    7 W, ~' @/ E. I7 A3 G
SD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。

( m, u6 V! x& p' p7 a. S
微信图片_20230411192255.png

! R: M8 |4 p0 q
  • SDIO接口
    , `5 X  I! ]& y
SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡都有SDIO接口。STM32F407系列控制器有一个SDIO主机接口,它可以与MMC卡、SD卡、SD I/O卡以及CE-ATA设备进行数据传输。也就是说SDIO接口不仅仅只会操作SD卡,凡是符合SDIO接口通信的设备都可以被操作。
0 L( p1 s+ a$ K4 U
微信图片_20230411192250.png

. b9 h9 M0 I$ F) [: T
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# z/ ?) x! A# k1 q
  • SDIO组成

    4 j) f7 x3 F! l& c, u, |3 w! L' I1 _
SDIO 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令和数据传输。
$ n$ \% W+ y' s) T- b: z
APB2 接口访问 SDIO 适配器寄存器,并且生成中断和 DMA 请求信号。
: J8 X8 I+ y# L( ?; ?8 X
微信图片_20230411192246.png

8 f* m; [4 \1 }! I
注:
1、默认情况下,SDIO_D0用于数据传输。初始化后,主机可以更改数据总线宽度。
2、SD 卡连接到总线,主机可以将数据传输配置为SDIO_D[3:0]
3、SDIO使用两个时钟信号:SDIO适配器时钟(SDIOCLK= 48 MHz)和APB2总线时钟(PCLK2)。
4、卡时钟(SDIO_CK):每个时钟周期在命令和数据线上传输1位命令或数据。对于SD或SDI/O卡,时钟频率可以在0MHz至25MHz间变化,当数据正式稳定传输的时候配置为24MHz。SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV)
5、SDIO适配器时钟(SDIOCLK):该时钟用于驱动SDIO适配器,可用于产生SDIO_CK时钟。对F4来说,SDIOCLK来自PLL48CK(48Mhz)。
6、F4:APB2总线接口时钟(PCLK2):该时钟用于驱动SDIO的APB2总线接口,其频率为PCLK2=84Mhz。

2 |% o7 t5 j. T3 d# u& C3 o# V7 q3 @
注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)。
( y4 ^; r4 Z1 q
微信图片_20230411192243.png

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

# c0 p$ E/ T( r7 k
  • SDIO 命令简介
    : y; q; k0 ^' V' I; E
SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。
; ?; I' n- [/ a( z7 j. }6 e
SD命令格式固定为48bit,都是通过CMD线连续传输的,数据线不参与。

6 [. r" r6 T. m3 l3 \7 R+ l
微信图片_20230411192239.png
# Z1 {7 R# A' g7 a# a. d
SD命令的组成如下:
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。( j# ~1 V; i" A* S7 Y# c
6 E( e7 x: T0 }- a% F, a: j1 ^5 ]8 s
传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。
2 h) T3 V( K" I; j
命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。
) z  A! ^5 h4 H, _" X
命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。
) G- g! u3 y$ R+ [4 u8 v6 {! s
地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。
; ?/ E/ L+ w5 ~
CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。
% L% o  ^; s* J; b: B9 P1 B& v
SD命令有4种类型:
1.无响应广播命令(bc),发送到所有卡,不返回任务响应;
2.带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应;
3.寻址命令(ac),发送到选定卡,DAT线无数据传输;
5.寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。

6 |) C3 {: H) A# ?1 e4 ^- K
在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD),也就是说在64个命令作为常规命令的基础上加了特定的命令
5 a; Q4 M3 `3 G; r$ D- A, ?
要使用SD卡制造商特定的ACMD命令如ACMD6,需要在发送该命令之前无发送CMD55命令,告知SD卡接下来的命令为特定应用命令。CMD55命令只对紧接的第一个命令有效,SD卡如果检测到CMD55之后的第一条命令为ACMD则执行其特定应用功能,如果检测发现不是ACMD命令,则执行标准命令。
# M, K3 ~; x7 v' H0 \/ U% m! H
SD命令响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:

4 q: |- z) q' E( S# x) s* D
lSDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。

1 R" p, Q: T* o; t! S* V+ m
SDIO读数据

* D# a. t) a' h' f1 Z; k
微信图片_20230411192236.png
2 x7 D3 @2 Q* C' ?4 |2 X% P
单个块读操作与多个块的读操作除命令不同外,还体现在读操作结束时,单个块读取一个块自动结束,而多个块还需要主机发送停止命令。写操作类似,也要多发一个结束命令,只不过写操作写数据前需要检查卡的状态是否为忙状态。
/ k. E- D" W) e$ [0 x9 R
SDIO写数据

/ |2 g/ o1 c: ^: k9 v
微信图片_20230411192231.png

( g" s5 A  ^+ C4 o# G% E! _3 [: z
  • SD卡操作模式

    % D9 E7 u5 U  m% r1 p
SD卡有多个版本,STM32控制器目前最高支持《Physical Layer SimplifiedSpecification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。

" x. V, W# Y0 E4 J6 Z' {+ c
SD卡系统定义了两种操作模式:卡识别模式和数据传输模式。

- I) ~$ g$ Q! b- v
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。
; g  _. q! }& G$ J9 |/ Y
每个不同的操作模式下,SD卡都有不同状态,通过命令控制实现卡状态的切换,在不同的状态下做不同的事,比如在发送数据前需要SD卡处于传输状态,发送数据时,SD卡处于接收状态。
- i% h! S' t7 ^% G1 ~8 H6 G
微信图片_20230411192227.png
2 b& D4 u. v# G! I: g8 k* U( `
  • 卡识别模式
    / U% w  ?; Y( V) C* |0 Q8 ]4 {
微信图片_20230411192220.png

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

( y; O1 q. F0 D+ L0 O6 S" r: ?& A
注:在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下,在SD卡初始化时,SDIO_CK不可以超过400Khz
0 I7 [& H, u$ h2 O4 H
  • 数据传输模式
    3 }. @, p; ~3 M- l7 D6 j
微信图片_20230411192214.png

/ T- y' m( ~9 t' o& P' T! W
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。通过CMD7命令加上RCA值来选定指定的卡,选中后SD卡进入数据传输状态,就可以发送CMD17读单个块,CMD18读多个块,读多个块时只有发送CMD12命令才会停止。SD卡再次进入传输状态,若不想对卡有任何操作可以再次发送CMD7命令加上RCA值来取消指定的卡,写操作与上述原理相同。
7 h  B. w0 k8 X5 j+ c
  • SD卡普通模式操作实例
    3 B8 q9 ?- o$ U; x: A' Q1 e- s
实验内容:向SD卡写入数据后读出

' i! b1 q* {" {6 s5 d
实验步骤:
# P( u) Y- I/ z( C! F1 d; ?
1.配置RCC,与以往不同的是SDIO适配器的时钟是单独配置的,需要专用的SDIOCLK,标准工作在48MHz
- X* `6 V% c$ I5 K' j% h
微信图片_20230411192210.png
0 e. W" P7 Y7 p- E: Q
2.配置SDIO+ M" E8 j! K+ t
! g" N# M& ~2 B, W5 n% L6 f7 Q- p5 ]# I
微信图片_20230411192207.png

: Z7 ^4 P. }# Y! `
微信图片_20230411192203.png

4 ~& Q8 m; _" ]! o* H9 N6 C
3.编写代码
  1. 5 m0 S% ]* L0 m4 l" D
  2. //mian.c2 {7 D' M. o' u/ s6 V$ w$ u
  3. #include "main.h"
    / `  n: W/ m' i) h- Z6 Y2 \
  4. #include "stm32f4xx_hal.h"
    * H  G& c& z3 B0 j+ ~
  5. #include "sdio.h"
    4 ^, w8 @# o' K2 i/ {/ M
  6. #include "usart.h"! h# r( W" s1 F* Z0 Y5 N0 G, R
  7. #include "gpio.h"- ], b4 \( B2 [% `
  8. #define SDBUF_SIZE   1024, w5 Q( r' S# B3 K+ X6 L$ r% R
  9. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收
    0 @% W0 s3 y& a
  10. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出
    3 o0 v! z. y7 v
  11. int main(void)
    % |  G. p- C# {+ }
  12. {
    ' @0 W7 o- r$ y- O( J
  13.       uint32_t i;
    4 B/ i% q! q0 j% j! m
  14.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
      ]* O! w7 t! a& O; j) U1 ~9 A
  15.       HAL_Init();0 \* g$ Q5 l( I
  16.       SystemClock_Config();  Q# i$ y& C$ V; t$ {) r) u3 u
  17.       MX_GPIO_Init();
    9 Q' m% P) n; y- E: @
  18.       MX_SDIO_SD_Init();" ]; y! C- ~. w, w4 Y7 q
  19.       MX_USART1_UART_Init();
    ' b. A+ E! C; Y8 r, e: y, n( t
  20.       printf("this is sd test\n");5 ~" A; Y5 c  ]8 S2 s6 Z: }6 }
  21.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印0 Z' d! Q5 H5 o9 I4 h
  22.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
    ( @/ J: s" ~! _# o, y
  23.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);
    5 z9 a- Z, a, l
  24.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本$ ^1 S* p; _7 c6 C" C+ a$ h4 q
  25.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
    ! H# N1 R' B( w$ ]
  26.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小- k) H/ i' }) P" y6 t: _
  27.       /*--------------------SD卡写测试----------------------------------*/% i$ R8 u5 g. X5 x
  28.       memset(SDBUF_TX, 0x8, SDBUF_SIZE);  /、填充TXbuf为0x8
    , W0 r3 `% \) C, M
  29.       /**
    4 G* |3 k- [) [2 x1 Y
  30.          //函数功能及参数描述                             
    9 v( U  o! B) `: A2 y; n; q  r1 H- |
  31.         *HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData,
    ' p0 T! c. n* B( s4 I% o7 A
  32.                       *uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)' B- u9 R( P* }* w+ ~
  33.         * @param  hsd Pointer to SD handle! U# g) B4 N% d$ r: N) C, Y
  34.         * @param  pData pointer to the buffer that will contain the data to transmit; J8 u+ X. P- l) h
  35.         * @param  BlockAdd Block Address where data will be written,从哪一个块开始写  % H, w* m8 S+ L" q' G8 K
  36.         * @param  NumberOfBlocks Number of SD blocks to write   写几个块
    # s+ n: T4 W# z( B, ]2 Q  F
  37.         * @param  Timeout Specify timeout value 超时时间
    ! X9 y1 {& U8 `: a
  38.         * @retval HAL status
    - p- p. m! a0 s1 o2 X* d4 j1 J9 l4 j
  39.         if( HAL_SD_WriteBlocks(&hsd, SDBUF_TX, 0 , 2, 1000) == HAL_OK)  `8 `4 n$ Y8 e6 u8 m! c, m0 g
  40.         {
    0 [8 ]' [! B1 ^
  41.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//处于传输状态退出
    2 @! r- B3 I- K9 ?/ \5 [
  42.             printf("WriteBlocks Successfully\n");
    : e6 j$ h* z" u0 M! o' Q
  43.             for(i=0; i<SDBUF_SIZE; i++)
    9 }& t3 @# m% U
  44.             {
    6 n! L; v1 O! B) n- v4 o& C
  45.                 printf("%d ",SDBUF_TX[i]);
    % Y  {1 x2 F& s$ {% C9 z, \! x7 _2 Y
  46.             }1 s- b1 c1 P3 }& {4 X
  47.             printf("\r\n");
    ( q9 b+ O% N4 t3 L0 _3 c) x( \  ~
  48.         }
    3 X, [# n$ W6 }- g$ p  Q; u6 I) S
  49.         else
    0 ?6 |, j9 K& X4 Y) m# L
  50.         {
    7 J1 F( T7 q3 H% S3 V6 Y
  51.             printf("WriteBlocks Failed\n");# k8 W7 n1 R5 k; R; u/ ~( `
  52.         }
    % {- n% F+ }( U/ P& g! \; S
  53.         /*--------------------SD 卡读测试----------------------------------*/6 N% h7 Q4 G9 x# o9 D( z$ z5 k
  54.         //与HAL_SD_WriteBlocks函数的参数功能相同
      l( T) T& j$ k
  55.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
    5 C. D0 U5 j- W- f. x4 I+ ^, _6 b
  56.         {+ D9 z% k, ~8 i9 g* i" Q
  57.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//返回到传输状态退出  n9 ]+ Q1 a* Y' V' K: @6 ^
  58.             printf("ReadBlocks Successfully\n");
    + @+ k. J* M3 b/ k! Z0 Y
  59.             for(i=0; i<SDBUF_SIZE; i++)9 w' A/ C& A2 l/ f- k# i1 N3 T$ Q* C
  60.             {
    8 C7 I& s' P2 a* W# z& D' N2 G
  61.                 printf("%d ",SDBUF_RX[i]);
    6 v& Z8 O6 @8 |* o! R5 F  d
  62.             }' D9 V+ F- R) \
  63.             printf("\r\n");  A/ A. U& U, [& z: h2 H& A8 X/ O
  64.         }
    4 o5 b2 d0 N9 W+ h. L
  65.         else
    3 l  Q6 G# m# b, V+ g$ u5 }
  66.         {. T) v5 B3 ]# I
  67.             printf("ReadBlocks Failed\n");
    2 K! O, e! O0 R
  68.         }
    % {3 M; q6 ]) @3 z
  69.         /*--------------------SD 擦除测试----------------------------------*/3 C2 e8 Q% f5 }3 e& U6 R4 N
  70.         if (HAL_SD_Erase(&hsd, 0, 1) == HAL_OK )
    8 W0 ]1 \& A: z( e2 @
  71.         {2 P* o) F* d9 V" y* |
  72.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
    8 F# a8 v3 T4 K9 w; M
  73.             printf("SD_Erase Successfully\n");% u: |& w2 V3 k$ G4 R" w; J+ m: O
  74.         }# f0 ^0 N& G% t5 S
  75.         else
    ; \/ n' Z. h3 b! o" k
  76.         {8 D4 g; Q5 U2 a
  77.             printf("SD_Erase Failed\n");
      _" ]7 u2 b6 p" z8 D
  78.         }
    , w& j! m9 a  i; X4 A  ?8 a
  79.         /*--------------------SD 卡读测试----------------------------------*/
    ) A, y5 j+ V9 ^2 Y% d* n
  80.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)* @# o/ o8 M3 N0 k
  81.         {' q! n% s: s2 U5 c
  82.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);! n) f! J( ?& g- V9 }4 \7 {+ q& ]
  83.             printf("ReadBlocks Successfully\n");  G1 t5 ^+ `) P& K
  84.             for(i=0; i<SDBUF_SIZE; i++)
    % N( v% l, h, c- \) K7 u5 U
  85.             {! s$ |5 {* ?' v$ G
  86.                 printf("%d ",SDBUF_RX[i]);  U5 X$ P! j; h- N
  87.             }9 W( J: N! u" c5 I7 T- |* _$ W7 U
  88.             printf("\r\n");' T" i0 Z9 I/ t6 O
  89.             }  K) l5 J' }% v% W. ?, N/ b" b
  90.         else  D6 C+ z$ n1 [# M
  91.         {
    * V% f! H- J5 P
  92.             printf("ReadBlocks Failed\n");
    ) P' p9 v9 p5 Y6 ^3 |* w
  93.         }
    6 U. v0 l' i' O9 V1 i3 F% L) s
  94.         while (1)/ w- ?, [3 o' J* X; r4 R2 U0 K
  95.         {
    : j$ ^1 C8 [6 @0 h4 z
  96.         }  6 _; H( z5 H5 g
  97. }
复制代码
  1. //sdio.c
    2 T8 ~% @: U* d- Q9 t
  2. //此代码为工程自动生成,非用户编写,这里只做分析
    / f  a# x% ~% q" p
  3. #include "sdio.h"8 y) r/ r4 E' G; @) j
  4. #include "gpio.h"! R, R) M  ~) Y7 v
  5. /* USER CODE BEGIN 0 */
    + `' q! f* X7 A4 V/ ]' ?2 T0 K
  6. /* USER CODE END 0 */
    5 ]3 v  b/ f6 F5 x
  7. SD_HandleTypeDef hsd;4 y+ I' V, e) @$ n
  8. /* SDIO init function */
    6 H/ y1 O$ y6 s% f7 o8 G% ]
  9. void MX_SDIO_SD_Init(void)   //初始化配置
    & @, [% m1 `2 `) A6 q' m4 `- U
  10. {
    4 j( Q' J# |8 ]! D! w
  11.   hsd.Instance = SDIO;    //SDIO句柄,整个系统中只有这一个1 V3 b. T' M, j5 r) V# _; A- |
  12.   hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;- G) y& C( a1 V1 T, e$ U% W( ?# m
  13.   hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;% L* k# V7 z4 E  {% r9 u
  14.   hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;5 c: D/ B4 f4 v) r( @8 U' q
  15.   hsd.Init.BusWide = SDIO_BUS_WIDE_1B;   //初始化时设置数据宽度为1位5 U6 J' t& O  v; K# @4 t# t# P2 Z, p
  16.   hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;8 n/ m, R* E: T5 z- B, z) \
  17.   hsd.Init.ClockDiv = 0;7 {8 o- q# o' j, _3 P
  18. //HAL_SD_Init()函数内部做了很多事去识别SD卡,具体的卡识别代码分析在下方$ U. I6 n+ s4 i1 Y# @9 d+ V9 D3 h  x5 F
  19. if (HAL_SD_Init(&hsd) != HAL_OK)
    ' o: k, W0 ~' N. d8 S  Y, E
  20.   {
    5 {- T* u1 E0 v" ]2 c; G( Y7 z, ^
  21.     _Error_Handler(__FILE__, __LINE__);+ H" I$ a) z' k) j5 }# L
  22.   }
    / I, Z* k7 P9 ~
  23. //初始化结束后重新配置数据宽度为4位
    5 R; O+ I. d& X5 x1 L
  24. if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
    ) _) J0 u' l! t! K; \! J5 R- }
  25.   {: S: _9 T: ^8 C  i4 ]; U7 U6 X
  26.     _Error_Handler(__FILE__, __LINE__);
    ! i4 d/ f8 B3 C" z1 @
  27.   }+ _2 Y3 V% G( v; N) I
  28. }
    % D7 Q- Y6 L$ b5 \3 U7 f
  29. //卡识别代码如下
    / O, O2 Q# B& I. Q0 F& f
  30. HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd)/ R6 ~. ~( }0 V# T+ E
  31. {1 `% v6 C, Y* M1 S
  32.   uint32_t errorstate = HAL_SD_ERROR_NONE;
    ! M0 J% o( ]* I
  33.   SD_InitTypeDef Init;8 C; f, m  I7 A8 P9 L) ^' J0 [
  34. /* Default SDIO peripheral configuration for SD card initialization */+ k, D, K9 a, m/ t* R' d% I! [; D. B
  35. //完成卡识别0 u1 B, F) {" b9 Y5 Z
  36.   Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;& b: r. L0 K& B8 n1 \) w# B
  37.   Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;" J( V0 S$ ^3 L" B
  38.   Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;$ q, f) j* n& |* W
  39.   Init.BusWide             = SDIO_BUS_WIDE_1B;0 H1 }0 ^  |( A5 e
  40.   Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;( c! d$ q  A4 q, K
  41.   Init.ClockDiv            = SDIO_INIT_CLK_DIV;
    - I- h* O* k- {$ v6 {! o8 O+ H% ]
  42. //SDIO_CK卡识别阶段要求时钟小于400KHz,宏定义
    4 r. a6 w1 s, B* b: @9 n. a. C
  43. //SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV)
    4 ]0 }2 G  _9 z
  44. /* SDIO Initialization Frequency (400KHz max) */
    ! c3 {4 \  a" q1 W( L" [+ A
  45. /*#define SDIO_INIT_CLK_DIV     ((uint8_t)0x76)*/
    * J7 v$ p& B: `- A
  46. /* Initialize SDIO peripheral interface with default configuration */
    : q& Z) Y% N. m, h6 H9 _6 E
  47.   SDIO_Init(hsd->Instance, Init);
    % w3 K( r- C& b/ X, W
  48. /* Disable SDIO Clock */1 y, r: @) g4 d- U& k4 ?
  49.   __HAL_SD_DISABLE(hsd);
    ; O) K) `  w2 ]4 b
  50. /* Set Power State to ON 对SD卡进行上电 */* L% h, u7 L' `9 d
  51.   SDIO_PowerState_ON(hsd->Instance);
    , T7 b' `9 d' j' R2 g
  52. /* Enable SDIO Clock */
    9 Q" x2 k2 S, p$ Y3 W, Y( }7 k
  53.   __HAL_SD_ENABLE(hsd);
    1 u, ^8 E/ V* T, o5 C1 d6 i' ?
  54. /* Required power up waiting time before starting the SD initialization
    1 V1 r; _' _# d
  55.   sequence *// B" P) a# Z/ @* \  ^4 }! z
  56.   HAL_Delay(2U);. m' h8 ^0 T% M1 @
  57. /* Identify card operating voltage */
    - |6 i" }  i2 d/ @/ M) n
  58.   errorstate = SD_PowerON(hsd);
    ! ?8 d+ W5 C0 ?8 f
  59. if(errorstate != HAL_SD_ERROR_NONE)
    2 D3 P$ i6 ]- t6 G
  60.   {
    1 e6 b5 _% [' x  G' P+ }! u
  61.     hsd->State = HAL_SD_STATE_READY;' \0 H) M" _- e$ w" J: c
  62.     hsd->ErrorCode |= errorstate;
    ' R1 X, A$ Z/ w4 _$ u7 n# V  |
  63. return HAL_ERROR;
    ! `% c$ k6 z4 z
  64.   }& V: H; p- I3 q8 C2 l& \3 X
  65. /* Card initialization */! e& c8 W+ b$ `
  66.   errorstate = SD_InitCard(hsd);
    ; a5 F7 [" |/ b" Q4 i6 K6 M+ s
  67. if(errorstate != HAL_SD_ERROR_NONE)0 K" ^3 L& d' U0 B, n
  68.   {$ b" }0 ?5 C  D: z
  69.     hsd->State = HAL_SD_STATE_READY;1 [7 ?/ y) M  V) P, \* E5 K8 t! o5 y3 N7 n
  70.     hsd->ErrorCode |= errorstate;* k# v& N1 D6 ~. Z. |; d
  71.     return HAL_ERROR;
    5 p2 G9 i/ X! Z( [# A/ w9 k5 F
  72.   }1 V! t0 @9 L4 U  e4 E; w' |3 B
  73. return HAL_OK;% R0 K; r# N* [2 h
  74. }
复制代码
0 L5 C  u  [$ a
  • SD卡DMA模式操作实例$ @# L  J' i2 f

    , |1 T9 ], O% B9 v
当SD卡中有大量的音视频需要读取时,整个过程需要CPU干预,这样CPU的利用率就会降低,因此大量数据的传输最好启用DMA。
4 a8 X+ s; ?& u, E( S/ B% A7 C( y' ^
实验内容:向SD卡写入数据后读出。

( u* ?+ Z* @, `& l( f! d
实验步骤:工程的时钟配置和SDIO配置与普通模式相同,只有一点不同需要打开SDIO的全局中断,因为SDIO发送与接收完成后都需要中断去生成DMA请求。设置SDIO全局中断优先级更高一些,因为它内部还有很多其他中断,发生错误时更需要处理。

5 H- a& b+ L+ {! r
微信图片_20230411192200.png
, t. m: ?3 {6 K2 H
配置DMA
; p7 A' V* v: i2 x
微信图片_20230411192156.png
; H. e; c/ D5 V. ^* [9 q6 H: w
编写代码:
  1. //mian.c
    $ s4 b2 ^+ T' }6 U, Q, f1 b
  2. . D+ l  R3 ^9 V
  3. #include "main.h"; ]4 f* X, b) m% Y% I
  4. #include "stm32f4xx_hal.h"
    8 Z( [( c- k& c
  5. #include "dma.h"+ u, F0 e0 v7 f0 B2 l: }. \
  6. #include "sdio.h"
    ( G' ^; e: D0 }8 t! H0 N
  7. #include "usart.h"0 v# ^4 H# W2 T$ L/ B
  8. #include "gpio.h"
    5 |' P* W' C, ^& T$ L6 D% v

  9. 1 t3 y9 c5 O& b6 Z! b
  10. #define SDBUF_SIZE   1024, g! a! s( z* N" b
  11. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收" d0 y' n3 p& @# L
  12. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出  K( c6 V3 P# v+ d- H# \
  13. uint8_t DMA_SEND_OK, DMA_RCV_OK;
    ) {# Z  }% N: A2 @1 R9 p& U: W' X6 l
  14. * y7 F6 x* W/ q( d0 J/ N& V
  15. int main(void)
    ) w6 j) p3 ^' ^% V' B$ Y3 n3 \
  16. {+ `2 Y: _$ p; K" P- n
  17.       uint32_t i;/ e' n# e) f/ l: |% s
  18.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息6 i* F( A3 R# |) W; @
  19.       HAL_Init();  H- w- A- u' W  q; S
  20.       SystemClock_Config();
    " t( g: Z0 p7 D
  21.       MX_GPIO_Init();
    0 h/ G  h7 t7 l- J1 \
  22.       MX_DMA_Init();
    5 x2 M- b& s+ y0 H$ w/ t
  23.       MX_SDIO_SD_Init();
    9 o" m0 M  f; n# Y0 E  F
  24.       MX_USART1_UART_Init();, b7 k' y3 D4 C: S
  25. , o* z6 R  ?) D- ]
  26.       printf("this is sd test\n");
    # d; Y: L( ^; j8 |
  27.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印) B+ Y: y  E$ S
  28.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
    * j% a( R: n# F( _  U' b7 l
  29.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);  O  b! E4 L+ W- s) \
  30.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
    8 ]: a- ~$ R/ k7 _2 P
  31.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数# ]2 }; ^/ T0 w4 f. b5 u: ]* A
  32.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
    # r6 l8 N+ c2 Y, P' }# _
  33.       /*------------------- SD DMA 写测试-------------------------------------*/
    - ^4 g- d9 k* F4 c- r& n& @+ Q4 T
  34.       memset(SDBUF_TX, 0x2, SDBUF_SIZE );' s7 `5 [+ x2 U+ @+ g% d* p1 g
  35.       DMA_SEND_OK = 0;  //设置发送完成标志位为0,完成时为1. {1 z, O& S+ e6 B# S2 X
  36.        /**
    9 s/ M% D: y: {8 ]5 h( @( ^) Q
  37.        //函数原型
    0 K. o; [& ]  |4 ]) p  F( X6 J6 l
  38.        *HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData,
    & M$ Z& y+ ]+ [+ F3 n
  39.                                              uint32_t BlockAdd, uint32_t NumberOfBlocks)
    9 U+ G. M6 q8 V5 z. H+ C5 L
  40.       9 R$ c0 l" H; G7 J/ Z/ X" \6 |
  41.         * @param  hsd Pointer to SD handle
    3 f; C2 N* u  S2 ]3 s7 a6 J
  42.         * @param  pData Pointer to the buffer that will contain the data to transmit
    % c' r" [7 v: Y* e' F; }/ f
  43.         * @param  BlockAdd Block Address where data will be written  从哪个块开始写5 R/ e2 H6 d4 W
  44.         * @param  NumberOfBlocks Number of blocks to write  写几个块
    5 P# o2 E1 H8 d5 \9 P; g
  45.         * @retval HAL status) g7 G1 W; i" y9 D- b' N: W
  46.         */
      w2 p- g. U% p! m& @
  47.       ) E: m' c/ F- X4 P( {
  48.       if( HAL_SD_WriteBlocks_DMA(&hsd, SDBUF_TX, 0, 1) == HAL_OK )
    0 L9 ?6 r" y- P+ \1 Q  i+ {
  49.       {
    % `6 x8 c+ Z2 p3 Z1 g
  50.           //等待DMA传输完成,并且SD卡状态为传输状态
    ' ?( k  n6 I" A4 N
  51.           while( (DMA_SEND_OK ==0 ) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));  * [' u: g# @- }6 V( n4 }( @
  52.           printf("WriteBlocks Successfully\n");
    + `( ?& b, @2 Z$ j* T* Q( n4 g
  53.       . A- i' B1 u( l, j
  54.           for(i=0; i<SDBUF_SIZE; i++)
    % @* d' c/ J& g$ y( \
  55.           {
    : m# h* C+ U+ _# ]
  56.             printf("%d ",SDBUF_TX[i]);
    5 V0 g, y1 d, d- C7 r9 t- v$ i
  57.           }
    , F- ]% x# Y" y/ ?
  58.           printf("\r\n"); 5 a+ @8 ?9 M; k; h8 m
  59.           }2 H" ~; x# b. D9 u4 B
  60.           else3 X: P2 g( i* x1 G8 x
  61.           {
    1 j8 n/ A$ m! \( ?2 R$ Y
  62.              printf("WriteBlocks Failed\n");
    4 f- a# e& u2 a
  63.           }
    . L8 Z" o. v0 L; a0 W6 e
  64.       
    7 _( P+ J9 s% M2 o+ I
  65.           /*------------------- SD DMA 读测试-------------------------------------*/
    2 R  b, ~  h. R0 D; a
  66.           DMA_RCV_OK = 0;   //设置读取完成标志位为0,完成时为1' d! Q) |: I& M* v4 |. a
  67.           if(HAL_SD_ReadBlocks_DMA(&hsd, SDBUF_RX, 0, 1) == HAL_OK): j) q: J% A) ?0 s# i
  68.           {; r7 [, y9 X5 {) H# v6 Q
  69.               while( (DMA_RCV_OK ==0) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));         
    9 U$ O0 G7 W; T9 H- M' G6 W& t
  70.               printf("ReadBlocks Successfully\n");         
    * Q' L/ p! x5 _* Q9 q% r) X
  71.               for(i=0; i<SDBUF_SIZE; i++): G3 A( @* [; {' F5 u
  72.               {5 c$ j( v: H% Y1 c( g2 f
  73.                 printf("%d ",SDBUF_RX[i]);- ]2 h% u# o1 P# t0 I; R; b
  74.               }! t, r$ j& p1 e& q! P) e
  75.               printf("\r\n");+ E' M5 b" E/ n8 A  F2 _) N
  76.            }
    ' K* A3 t5 n# b/ d/ J: B' i
  77.             else7 Z* H, V+ E* i' M- Q7 q$ a
  78.             {: c- b- S9 c  d* m
  79.               printf("ReadBlocks Failed\n");) c0 c4 E# R4 \! x& h/ i- n6 b
  80.             }. [  |# n! w8 I! d& h
  81.             while (1){}& [4 o4 @; {# b: }: I" A
  82. }
复制代码

$ @3 N3 n# E9 o- V9 P  g

  1. ! d7 G" ?5 s1 ]% K
  2. //sdio.c
    1 M: g0 @; F  W: {
  3. ! d9 W' Y" Z- H- G5 j1 Q
  4. extern uint8_t DMA_SEND_OK, DMA_RCV_OK;
    ( H, Z. }3 v% c6 {1 m5 i  d5 o
  5. //发送完成中断处理
    , Z1 n3 [! Y, ?% D( l+ u( P5 [/ K7 T
  6. void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)7 B, v! A9 K$ C7 q, l+ ~
  7. {: ?1 j/ }0 C/ y
  8.   DMA_SEND_OK = 1;
    , D+ {& q6 ~& X3 U% z" `: i! J
  9. }
    # Z/ s  m9 q, a1 v
  10. //接收完成中断处理' i: [. \: D" s  ^0 R) \2 W  i
  11. void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
    0 w) R; |* ~+ I( S1 g& V) f
  12. {* N9 q: s; P% D+ I
  13.   DMA_RCV_OK = 1;
    : N5 n+ E- d5 g/ T  N# {
  14. }
复制代码
( v0 X7 g5 ^5 [, g4 F8 W2 D) G
至此,ARM开发进阶知识已全部更新完毕!下一系列介绍物联网操作系统开发!
- K2 K% t2 c# H8 |& f1 A) X& k: c2 w; t% W3 f+ D2 w
& [$ n8 X+ i& ]
转载自: 骆驼听海
; {+ Q+ p8 a) R如有侵权请联系删除+ d1 v6 Z4 ?/ x4 h5 e' ]" ^
收藏 评论0 发布时间:2023-4-11 19:23

举报

0个回答

所属标签

相似分享

官网相关资源

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