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

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

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

$ W5 M! u( S4 \5 `1 u) H4 B; s, ^9 Y, Y
SD卡(Secure Digital Memory Card)即:安全数字内存卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。
9 I  E+ @. y3 M6 Y( n! ?
  • SD卡简介

    ! ?, W6 |9 |6 N- Y0 G7 C
SD非常小巧,可以嵌入到设备中,在嵌入式开发中,产品需要存储一些容量叫大的文件,就可能会运用到SD卡。SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示。

) a7 v. Y8 K. U* t8 ]
微信图片_20230411192315.png
$ R  ^6 F2 A1 v! T7 ?/ V1 i0 O* z
容量大小不同的SD卡,其内部的操作标准是不同的。很多外设会明确告诉用户本设备最大能够支持容量多大的外扩,不能无限制的扩存,因为容量越大,操作不同,对控制器的要求比较高。当前设备的控制器不足以支持大容量,所以有外扩限制。
1 \8 c: [. h  T- s1 {2 b
微信图片_20230411192311.png

! @- k9 h& U" o7 j3 X) t" {
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。

2 {* N2 g/ D. R/ C! P
) Q" H! o) C& N4 h
微信图片_20230411192306.png ' Y: y1 n( r% V8 q  W
) f0 e$ y2 C* f$ o
  • SD卡的物理结构
    8 Y5 l0 B& P: E7 S1 t% m; Y

2 S6 J; x; ?  M# H' o& ^6 H; Z0 z
一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。
, g9 {! q% r. m* N& O2 @
微信图片_20230411192301.png

' u# H' `3 [) J
SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。
9 F; Y. A& q0 e  |5 C+ q( k6 Y3 [
5 ?' U" B7 F- T  I! U1 v# r
) Z  o4 Y: Z7 c$ W1 i. T; ~. X- V/ i
存储单元:是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;

. [7 W  a! w4 V( N! d- O4 P
电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;

& u% L5 E, Q7 h, f7 f/ l+ ~' r7 y
卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;

# P4 b1 `# R8 W) k
接口驱动器:控制SD卡引脚的输入输出。

3 c& D# z4 M4 J+ ?; g5 D

9 a+ ^% Y8 K5 n) J
  • SD卡内部寄存器
    6 J3 h$ I! j2 E* t' q! c: {
SD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。

8 d- |# E9 v% H, p2 R
微信图片_20230411192255.png

/ u6 I+ i1 a& ?) l7 _
  • SDIO接口

    4 i8 d# C' ?, p  w9 g1 V0 O) P
SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡都有SDIO接口。STM32F407系列控制器有一个SDIO主机接口,它可以与MMC卡、SD卡、SD I/O卡以及CE-ATA设备进行数据传输。也就是说SDIO接口不仅仅只会操作SD卡,凡是符合SDIO接口通信的设备都可以被操作。
. S% M( W. T8 d+ L. I5 ?' q2 x7 Y' z
微信图片_20230411192250.png

* u% Z6 \. i) S
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 的通信模式。
  ^/ l5 K  D/ U$ D6 K6 k) ]) j. k/ e
  • SDIO组成

    , e! w: V* R: _" A$ L, h  p, @
SDIO 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令和数据传输。

1 j7 ^1 Q( _  D/ Q- }
APB2 接口访问 SDIO 适配器寄存器,并且生成中断和 DMA 请求信号。
+ W8 e$ h: R, W4 d+ K6 n+ p9 D
微信图片_20230411192246.png

* H% E+ B" R& p' x7 c
注:
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 Q" l- I, q' U' n' Y
注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)。

8 T; E* j( ]9 z7 {
微信图片_20230411192243.png
, U1 z6 `- G( a& E2 m+ ?( L
该适配器由五个子单元组成:
1.适配器寄存器块 :适配器寄存器模块包含所有系统寄存器。
2.控制单元   : 控制单元包含电源管理功能和存储卡时钟的时钟分频器。
3.命令路径 :命令路径单元向卡发送命令并从卡接收响应。
4.数据路径 :数据路径子单元负责与卡相互传输数据。
5.数据 FIFO :数据 FIFO(先进先出)子单元是一个数据缓冲器,带发送和接收单元。FIFO 包含一个宽度为 32 位且深度为 32 字的数据缓冲器和发送/接收逻辑。(一共32 个单元,一个单元一个字)。所有的数据传输都要经过FIFO,便于管理。

5 w4 o; l$ C  Z: E5 C: h: G) ~! n
  • SDIO 命令简介
    . ~6 A' X) r% R* x
SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。

0 i! u0 D7 {' i5 g5 F
SD命令格式固定为48bit,都是通过CMD线连续传输的,数据线不参与。
" a. b) Q+ Y( h  O) J3 O% w' n
微信图片_20230411192239.png

8 r1 q% T% W. D: G( [' ]
SD命令的组成如下:
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。/ `; k) u! N* {  w

8 I4 S9 ]# x+ o/ e  w
传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。

; D* v) D: B7 P9 P
命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。

' Q8 H5 z3 }5 _
命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。

9 x: @) n) F" O" K) M+ w
地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。

, k6 a0 W. q  q5 ?! u. V
CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。

4 e0 l* Y6 y7 u
SD命令有4种类型:
1.无响应广播命令(bc),发送到所有卡,不返回任务响应;
2.带响应广播命令(bcr),发送到所有卡,同时接收来自所有卡响应;
3.寻址命令(ac),发送到选定卡,DAT线无数据传输;
5.寻址数据传输命令(adtc),发送到选定卡,DAT线有数据传输。

, V$ z6 e9 N' z9 M
在标准中定义了两种类型的通用命令:特定应用命令(ACMD)和常规命令(GEN_CMD),也就是说在64个命令作为常规命令的基础上加了特定的命令

. b3 Q8 x$ e7 t) M# ]1 y
要使用SD卡制造商特定的ACMD命令如ACMD6,需要在发送该命令之前无发送CMD55命令,告知SD卡接下来的命令为特定应用命令。CMD55命令只对紧接的第一个命令有效,SD卡如果检测到CMD55之后的第一条命令为ACMD则执行其特定应用功能,如果检测发现不是ACMD命令,则执行标准命令。

" d* n) h& z7 J8 N8 z* n: W/ L
SD命令响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:
1 Y) q& t- C" e+ }
lSDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。与命令一样,SD卡的响应也是通过CMD线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。

6 e! h$ _! _- W1 w: a3 d( u
SDIO读数据

& C# m' j" O* ~, j' c& S, U- e
微信图片_20230411192236.png

" N  F4 m, E8 G  E6 U
单个块读操作与多个块的读操作除命令不同外,还体现在读操作结束时,单个块读取一个块自动结束,而多个块还需要主机发送停止命令。写操作类似,也要多发一个结束命令,只不过写操作写数据前需要检查卡的状态是否为忙状态。
, ?( k/ s8 L* O/ k9 ~6 I
SDIO写数据

* `+ b( t" K  r9 J/ G* S! h
微信图片_20230411192231.png
2 L, {4 v. w7 g& k: O/ v; a
  • SD卡操作模式
    1 `1 s& i) ]$ l
SD卡有多个版本,STM32控制器目前最高支持《Physical Layer SimplifiedSpecification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。
9 o5 v. M2 o% \) h$ ^, V" z
SD卡系统定义了两种操作模式:卡识别模式和数据传输模式。
; c3 l2 W9 @0 m* e- m
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。
6 U& s& `5 H- ]1 F# |
每个不同的操作模式下,SD卡都有不同状态,通过命令控制实现卡状态的切换,在不同的状态下做不同的事,比如在发送数据前需要SD卡处于传输状态,发送数据时,SD卡处于接收状态。

/ v3 \5 L0 s& ?0 G# M5 k
微信图片_20230411192227.png

& D3 I! J8 ?7 v" B7 W
  • 卡识别模式

    " j9 C" U% i8 Y2 [) ]# D# X3 m% Y
微信图片_20230411192220.png
' w# I$ M! I' ~9 u% p& T
①上电后,主机发送CMD0让所有卡软复位从而进入空闲状态。
②主机发送CMD8确定卡的电压范围,并识别是否为2.0的卡
③主机发送ACMD41识别或拒绝不匹配它的电压范围的卡,SD卡需要工作在特定的电压范围之内
④主机发送CMD2来控制所有卡返回它们的卡识别号CID(128位)
⑤主机发送CMD3命令,让卡推荐一个RCA(16)地址作为以后通信的标识,之后都以RCA值作为身份标识进行信息交互。

2 n/ E. v; K2 r+ p2 a# \" J. n7 [
注:在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下,在SD卡初始化时,SDIO_CK不可以超过400Khz
0 O7 j  ?& ^8 A1 x$ t- ?4 X. D
  • 数据传输模式
    1 }" U) M2 y: |; {3 Z: C6 n
微信图片_20230411192214.png
/ Z; e4 H7 t. Q% d  E0 o/ J
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过CMD4命令来实现。通过CMD7命令加上RCA值来选定指定的卡,选中后SD卡进入数据传输状态,就可以发送CMD17读单个块,CMD18读多个块,读多个块时只有发送CMD12命令才会停止。SD卡再次进入传输状态,若不想对卡有任何操作可以再次发送CMD7命令加上RCA值来取消指定的卡,写操作与上述原理相同。
8 n/ D' Z# s" f$ d$ q
  • SD卡普通模式操作实例
    9 I7 s6 d" c' a
实验内容:向SD卡写入数据后读出
* l9 k* e& B& Q" j# C  z
实验步骤:

2 p7 x# P9 X* a' I
1.配置RCC,与以往不同的是SDIO适配器的时钟是单独配置的,需要专用的SDIOCLK,标准工作在48MHz

& H3 d  v* _9 T  B# u& D
微信图片_20230411192210.png
7 O2 e' z- ~1 g0 r/ n% u- Y8 L4 b
2.配置SDIO6 i( q& i& ]0 N. O. z

0 `" Z2 Z! K: y) H3 z8 ~2 ~
微信图片_20230411192207.png
1 j( I9 ]! A5 X: P6 T  |* Q# v' C: n
微信图片_20230411192203.png

6 X% P+ g$ @) E+ B) h
3.编写代码

  1. ! [( u' t0 H) K
  2. //mian.c
    - a. ~0 @% _7 Z
  3. #include "main.h"4 h2 x7 D% o& ?! u
  4. #include "stm32f4xx_hal.h"
    " f  C8 o+ j" n3 R; g9 c
  5. #include "sdio.h"- |3 E" ]4 ~) \% A8 P- a
  6. #include "usart.h"
    $ W, v7 N( x  T/ T
  7. #include "gpio.h"
    8 n3 m7 ?* d9 _. Q7 N# Y
  8. #define SDBUF_SIZE   1024) O8 r% b6 ]5 I# a( d, H
  9. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收5 H  R2 u* s, X/ b' S! f# O
  10. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出+ z$ j" x% e, O' \) O2 Y3 d
  11. int main(void)8 }4 q3 w: }8 T$ @9 n2 k; r
  12. {
    4 Y3 d1 j( Q) _( j
  13.       uint32_t i;
    $ [& [7 v0 K0 T  x
  14.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
    , F) q; @; s1 B& y; L- M+ k& M. X/ e* U
  15.       HAL_Init();/ S* ?* D; ?% W4 a% G. i
  16.       SystemClock_Config();
    6 I8 W! ?. b6 D& Z
  17.       MX_GPIO_Init();
    0 @4 [' J" J: T: A! R
  18.       MX_SDIO_SD_Init();4 @; v3 D' }1 f7 E  Y0 t
  19.       MX_USART1_UART_Init();# ~% c$ g9 `1 U: T0 C8 Q" V* g6 R$ D
  20.       printf("this is sd test\n");3 Z! ?) y6 J7 w( x
  21.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印; h# B7 r" G  L
  22.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
    8 \9 W4 I8 R  c3 u5 c
  23.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);) o! |) O4 ^* B  l8 W1 Z0 h1 {
  24.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本* m% @8 T6 h! Z* E0 E
  25.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
    8 y' \# F: r3 J( _
  26.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小) @. X8 P- a6 X, ~
  27.       /*--------------------SD卡写测试----------------------------------*/+ `! {: R4 j4 k: y* c
  28.       memset(SDBUF_TX, 0x8, SDBUF_SIZE);  /、填充TXbuf为0x81 Q$ G, b$ ^+ R" r" V4 v
  29.       /**
    0 c2 Z( x, l* N
  30.          //函数功能及参数描述                             
    ! I3 U) I5 z* K. l' f3 |5 r$ e! c
  31.         *HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, 0 a( Z+ z' l$ d
  32.                       *uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout); L4 n/ n6 ~: e/ X+ I4 V
  33.         * @param  hsd Pointer to SD handle
    + g- ~. M+ r* q+ D
  34.         * @param  pData pointer to the buffer that will contain the data to transmit
    . S% H' x) ~0 e2 k9 ~! X6 S
  35.         * @param  BlockAdd Block Address where data will be written,从哪一个块开始写  1 j. |& ^1 _/ d5 V+ z$ t- i
  36.         * @param  NumberOfBlocks Number of SD blocks to write   写几个块, m: I1 p. B6 [' _
  37.         * @param  Timeout Specify timeout value 超时时间
    4 D, l& c  y7 J5 t8 f
  38.         * @retval HAL status/ E5 s5 R) B5 L/ q' t
  39.         if( HAL_SD_WriteBlocks(&hsd, SDBUF_TX, 0 , 2, 1000) == HAL_OK)
    8 }3 [# ?& N- ]( d1 m) l# m
  40.         {
    : x# N4 c: S9 y* J9 D7 X
  41.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//处于传输状态退出
    & W4 \6 }  g  y  a
  42.             printf("WriteBlocks Successfully\n");
    - B6 U. E( ]8 N7 D- B+ M9 G
  43.             for(i=0; i<SDBUF_SIZE; i++)5 t( F5 f9 f5 \5 n+ A
  44.             {
    " ^  B# J1 U* U" R$ D
  45.                 printf("%d ",SDBUF_TX[i]);0 f3 ?/ ]+ [: G8 {8 j
  46.             }
    # B9 Y" v  ]& [
  47.             printf("\r\n");
    8 ]" y) e( M  k4 O5 N$ r
  48.         }
    * P& G5 N# p/ K- ?$ N
  49.         else
    ! d* M2 d0 ^7 i# v2 _% |( F. u
  50.         {6 k3 j8 J+ S% \# \& ?
  51.             printf("WriteBlocks Failed\n");6 C1 _2 ^7 S0 e4 t3 X
  52.         }
    ' R1 U5 S. S+ I+ o7 j
  53.         /*--------------------SD 卡读测试----------------------------------*/
    4 t  _" z# h1 `, h( g; u
  54.         //与HAL_SD_WriteBlocks函数的参数功能相同! h& o% f* u1 J$ \  E
  55.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)3 {: F$ Z& |$ Q# v
  56.         {: n8 s8 Q( ^( N
  57.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//返回到传输状态退出
    7 X* B' S8 G* _  U2 ?0 e
  58.             printf("ReadBlocks Successfully\n");, }( f* V5 c) P) m/ h
  59.             for(i=0; i<SDBUF_SIZE; i++)7 U* z) B* I: h7 S6 p& ^
  60.             {( }6 J* J1 y. d+ j0 R" x( l! R, b
  61.                 printf("%d ",SDBUF_RX[i]);
    : i1 [$ |  N8 D' s
  62.             }2 [7 G- D; k- k
  63.             printf("\r\n");; w  x- C7 S& {4 M
  64.         }
    5 |* j/ n! F2 N; }
  65.         else
    / B5 h6 h9 ^+ W0 d; |# C5 a! f
  66.         {
    ( j0 L8 o5 }; m2 c
  67.             printf("ReadBlocks Failed\n");
    . {* V$ [% N. ^7 W9 l- g: d/ O8 }+ k
  68.         }
    , S+ B8 _6 I3 R% w+ b: y& |! `
  69.         /*--------------------SD 擦除测试----------------------------------*/- H  i* d4 L) ?5 Y
  70.         if (HAL_SD_Erase(&hsd, 0, 1) == HAL_OK )
    3 o, j1 m9 ?# p$ _4 Y, }
  71.         {
      R4 q+ Y( o: S, s/ D& I
  72.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);$ V) T6 t3 F- s1 v$ p
  73.             printf("SD_Erase Successfully\n");# p3 x5 H" h5 X
  74.         }
    ; D" L3 A  F3 Y# T6 s
  75.         else
    & A* N" m( y$ D/ b
  76.         {( z7 s" Q% N8 R
  77.             printf("SD_Erase Failed\n");
    % ~2 u1 d8 I+ C! p6 h
  78.         }; K: v( n: _, M' R8 |: N
  79.         /*--------------------SD 卡读测试----------------------------------*/
    4 A8 e! _8 U3 F8 u
  80.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
    6 g" g6 h# X  |6 m7 B5 Z7 I9 m
  81.         {* r8 o# x/ c* D
  82.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
    7 d; E1 X9 d0 P( z7 b
  83.             printf("ReadBlocks Successfully\n");
    / h, u/ H" t6 b% P
  84.             for(i=0; i<SDBUF_SIZE; i++)$ m6 F8 I$ U( U3 u& M3 P
  85.             {
    ( D' u  y3 f; W! `+ |6 N# Q; b
  86.                 printf("%d ",SDBUF_RX[i]);1 [/ ]3 v: s% v+ b. a8 ^2 ~' [
  87.             }
    : \+ K; S" `8 n& ]. S3 B  l
  88.             printf("\r\n");
    . @: X0 C7 d5 v0 T: y- j- i
  89.             }. @+ {6 f* V% t6 `- Y) m2 E8 v
  90.         else' T4 y2 e4 m8 l* U; `
  91.         {
    / t' H2 c* O/ H
  92.             printf("ReadBlocks Failed\n");/ j/ y( I# n, M: |( a
  93.         }
    % X8 T. W/ k8 i# y- V8 i; d/ s
  94.         while (1)2 u: P6 ?0 k' Q
  95.         {
    9 l+ Y4 e6 z, q! n/ n+ P+ |2 V
  96.         }    H, l' ]; S+ b' @+ ~( m. j: g
  97. }
复制代码
  1. //sdio.c
    / l, y2 \4 j! |6 y4 c! ^
  2. //此代码为工程自动生成,非用户编写,这里只做分析7 Q: a- j# ?, y1 c! n5 j* F
  3. #include "sdio.h"' B: P6 G( j) E; L) S) }, M4 S
  4. #include "gpio.h"$ }+ K: z) d0 V6 w& m" n4 W
  5. /* USER CODE BEGIN 0 */! R3 A( j+ a: |3 u% B' x
  6. /* USER CODE END 0 */% L/ X' m2 ^! H0 U% l& \& ^) U$ i* e
  7. SD_HandleTypeDef hsd;
    . a4 I! o0 K; E+ k$ v. T; T; [
  8. /* SDIO init function */
    % A' w- `& f6 w3 \/ [
  9. void MX_SDIO_SD_Init(void)   //初始化配置+ z2 U  z$ V6 w' n; B: \' e' o1 N
  10. {( O5 q7 x9 m" i; @6 P" I
  11.   hsd.Instance = SDIO;    //SDIO句柄,整个系统中只有这一个8 N2 B% w: j  S# D
  12.   hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
    " j8 o8 I2 U1 O1 J. k
  13.   hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;* _6 \' O& O# i7 c6 s
  14.   hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;1 p! a$ r2 I* s& J6 I
  15.   hsd.Init.BusWide = SDIO_BUS_WIDE_1B;   //初始化时设置数据宽度为1位
    / N8 g% [( p, n0 H
  16.   hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;7 Q' ~; _$ L, M( N
  17.   hsd.Init.ClockDiv = 0;* @, f% c. I$ U
  18. //HAL_SD_Init()函数内部做了很多事去识别SD卡,具体的卡识别代码分析在下方' g; G. d+ B2 n
  19. if (HAL_SD_Init(&hsd) != HAL_OK)
    1 |0 Y; S0 c8 Q# D! H; }: q; ^
  20.   {
    ' Z; ?7 L+ _9 q& ?7 t! g4 C
  21.     _Error_Handler(__FILE__, __LINE__);+ \6 M/ o- y$ [, x+ s+ q; r% o
  22.   }  K$ w: l# ^/ q1 K  r* ?
  23. //初始化结束后重新配置数据宽度为4位; I3 M8 i& R. @. Y. u  k
  24. if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) 6 T2 P$ j+ t5 t  G
  25.   {
    * t: h( l  q4 F
  26.     _Error_Handler(__FILE__, __LINE__);
    ) H& K% E$ P: L& u& ^
  27.   }
    - H$ a4 v, M9 r# {2 b
  28. }- P" ^3 Q/ F# w& E0 k. g% H2 T
  29. //卡识别代码如下
    & P! G# b+ P* e7 `- O- I
  30. HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd)
    / `4 \2 ]: T/ u7 N: m5 w
  31. {
    8 y% e- ?. B3 I% T7 B# J
  32.   uint32_t errorstate = HAL_SD_ERROR_NONE;
    9 z/ n! B. H! o5 t6 [- [
  33.   SD_InitTypeDef Init;. X" ]1 V% y, U0 h
  34. /* Default SDIO peripheral configuration for SD card initialization */
    ' [2 H( o  _; }+ t1 u
  35. //完成卡识别
    ' e  j& Z+ r3 R& n5 j
  36.   Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;4 o  y5 s( k( e/ \3 f" X9 r# G4 E
  37.   Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;1 G6 F/ k1 s$ [* U0 J8 o7 V6 Q
  38.   Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;
    1 I# g0 k5 W/ B
  39.   Init.BusWide             = SDIO_BUS_WIDE_1B;* \; ?9 ]- q: Z! W
  40.   Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
    ! r6 X6 K" m: h3 R+ d1 G8 p
  41.   Init.ClockDiv            = SDIO_INIT_CLK_DIV;& Z$ X2 i' I9 E; z( _
  42. //SDIO_CK卡识别阶段要求时钟小于400KHz,宏定义0 ?8 E8 u- Z! \" X4 g4 G
  43. //SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV)
    3 y7 D. A, W8 f* b- s
  44. /* SDIO Initialization Frequency (400KHz max) */
    6 o. o$ v0 j& W5 b" t
  45. /*#define SDIO_INIT_CLK_DIV     ((uint8_t)0x76)*/! X8 g! Z: P: Y0 [. n
  46. /* Initialize SDIO peripheral interface with default configuration */& ~. i9 R( K4 [& [& e$ Z
  47.   SDIO_Init(hsd->Instance, Init);9 |: T- g$ w2 a% e6 w! O- f
  48. /* Disable SDIO Clock */8 M) ~7 P1 B5 C( p
  49.   __HAL_SD_DISABLE(hsd); & B" e( y% L) a: P" q: i5 u+ m" A
  50. /* Set Power State to ON 对SD卡进行上电 */
    0 [$ C; {9 R) _4 s% Q" e# A2 d
  51.   SDIO_PowerState_ON(hsd->Instance);0 Z3 F1 j( F9 c4 M0 {) V1 y( @
  52. /* Enable SDIO Clock */. q' ~! A: N: G% u7 a2 I& x% e6 U
  53.   __HAL_SD_ENABLE(hsd);
    . e- q( W9 ~, L- J" j6 Q  Q
  54. /* Required power up waiting time before starting the SD initialization 3 i! j% G8 k0 F! ^# ], E0 y) H
  55.   sequence */- y; w5 m1 T  Q1 N4 n, Q
  56.   HAL_Delay(2U);
      n) F' Y& e- j
  57. /* Identify card operating voltage */+ g% d" U  Q1 W. R
  58.   errorstate = SD_PowerON(hsd);+ c- x! V- R# E9 B& t
  59. if(errorstate != HAL_SD_ERROR_NONE)
    0 D% l1 F! u) x. R
  60.   {
    * `6 H3 P! Y) Q8 g
  61.     hsd->State = HAL_SD_STATE_READY;
    8 ^0 T4 g) g) H! n
  62.     hsd->ErrorCode |= errorstate;
    1 K; b" x  D; w+ d1 o' j; v8 w0 l
  63. return HAL_ERROR;5 u+ M" L' l! |8 h( E- ]
  64.   }% s  t8 I$ ]/ x
  65. /* Card initialization */
    8 i  v% M/ q- t, L
  66.   errorstate = SD_InitCard(hsd);
      h) a, m. d" [$ Q+ p4 O, {4 c
  67. if(errorstate != HAL_SD_ERROR_NONE)" g3 }; b: f# d
  68.   {
    & D5 I, F4 L! w4 b! a$ Y
  69.     hsd->State = HAL_SD_STATE_READY;
    3 n9 k& Q# H5 Y, |- ]$ f6 |5 m
  70.     hsd->ErrorCode |= errorstate;
    1 b% a/ g) n# [: N+ @7 D! x
  71.     return HAL_ERROR;
    1 B4 I  B4 G) v' [# ]% g
  72.   }! o, i. r9 }- {' y
  73. return HAL_OK;$ V7 y. [6 x3 g) S9 |% e7 }
  74. }
复制代码

$ f" ^% Y4 t5 h- _( A
  • SD卡DMA模式操作实例2 @  }' K* h3 N. O. l7 t
    , F8 h; S# |& B8 v8 z& o
当SD卡中有大量的音视频需要读取时,整个过程需要CPU干预,这样CPU的利用率就会降低,因此大量数据的传输最好启用DMA。
! s! C) @9 n6 _
实验内容:向SD卡写入数据后读出。
) `8 P! c( q1 D
实验步骤:工程的时钟配置和SDIO配置与普通模式相同,只有一点不同需要打开SDIO的全局中断,因为SDIO发送与接收完成后都需要中断去生成DMA请求。设置SDIO全局中断优先级更高一些,因为它内部还有很多其他中断,发生错误时更需要处理。
) m7 {9 V3 k  w7 {
微信图片_20230411192200.png

8 O1 w; X0 U" Z; |  m/ k
配置DMA
2 s( O9 C2 B; i4 }- s8 _6 x4 F
微信图片_20230411192156.png
0 Y0 y$ c' l& @9 ^2 R
编写代码:
  1. //mian.c5 e% y: x0 x6 f7 h% Q! u  N

  2. 1 u7 d: x3 k% D( W& ~
  3. #include "main.h"
    : k, Z6 |7 P, m' f
  4. #include "stm32f4xx_hal.h"
    7 P1 m: x+ f4 ]# T1 X$ Z) b! v
  5. #include "dma.h"9 I- R6 D7 B3 g, ]" h8 v4 E
  6. #include "sdio.h"
    8 i9 U% r2 n) D
  7. #include "usart.h"
    0 v: \- q% d1 ]+ S' @4 L
  8. #include "gpio.h"6 U( M: ]/ l' E1 P( w  z6 b# P9 j
  9. 8 m+ D9 ^( D) b. P. ~
  10. #define SDBUF_SIZE   1024* k5 U, W( w! d; j
  11. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收, G; N) [) A( p' J6 n1 H
  12. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出
    ) l; u: V0 I$ p: w+ I# H0 b8 n
  13. uint8_t DMA_SEND_OK, DMA_RCV_OK;
    2 q- O% n; s/ g4 j' y
  14. : `! S' \/ j& `8 s5 `- J
  15. int main(void)
    8 U! l/ p! I" Z3 f! _1 u. I
  16. {3 \7 m3 e3 Y, c
  17.       uint32_t i;
    % L; h; \4 ~/ Y4 c% u
  18.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息( p& U/ A- w# r& T; ~
  19.       HAL_Init();
    % f' A- V( b8 ^! I( C
  20.       SystemClock_Config();- j+ q: u$ I9 ^  Y# _9 n' i
  21.       MX_GPIO_Init();
    ; \8 K) B0 Z, ^3 v) Z
  22.       MX_DMA_Init();
    3 w9 u$ w/ p5 C, x7 ?
  23.       MX_SDIO_SD_Init();
    1 l5 y$ C8 S' C! U- S2 C6 N9 r
  24.       MX_USART1_UART_Init();5 ~4 x( y9 D9 g6 h
  25. & ?* B* c  q; R7 u' l. @6 s' ~
  26.       printf("this is sd test\n");; `/ F. Z3 g; o: S7 k
  27.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印: y8 \2 U9 D7 \# i
  28.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
    % i/ _4 m4 n* ?
  29.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);6 Z4 B# P3 T( }1 h& e
  30.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
    9 w' n( x% K2 F# T* F% ]* x4 c9 a
  31.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
    ! p$ e$ A8 j- a, s) N# r* \8 s+ H
  32.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小. y8 @5 X5 T& V$ h/ @! V+ X( h
  33.       /*------------------- SD DMA 写测试-------------------------------------*/- |, A! C" k2 O) q
  34.       memset(SDBUF_TX, 0x2, SDBUF_SIZE );
    # B0 r* K. J+ E- K7 b: x
  35.       DMA_SEND_OK = 0;  //设置发送完成标志位为0,完成时为1
    , Y, C+ P# w3 h9 B8 |6 j
  36.        /**
    % l; T/ M( S0 y  x$ h
  37.        //函数原型2 `  x" c& k) B3 M3 |( O! e
  38.        *HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData,
    8 ]* A* f; x/ t3 D2 b+ s# f
  39.                                              uint32_t BlockAdd, uint32_t NumberOfBlocks)- z9 _- g! `9 }( ^' G4 @" ?) f/ [
  40.       ) W( i6 k; Y2 a
  41.         * @param  hsd Pointer to SD handle) w, E: y1 P; x: t% C& [4 i4 {/ k
  42.         * @param  pData Pointer to the buffer that will contain the data to transmit
    % ~9 [5 z$ X6 _6 R7 j
  43.         * @param  BlockAdd Block Address where data will be written  从哪个块开始写
    7 ^' P& M3 l7 B3 g# D7 e' c
  44.         * @param  NumberOfBlocks Number of blocks to write  写几个块
    : H- x$ o& R& z% d4 x+ ]
  45.         * @retval HAL status, ^- s5 c9 o' v- Y4 `3 k4 ]
  46.         */
    2 Z" n4 Z/ y8 D( B
  47.       6 [8 o3 B! T! X* f: L
  48.       if( HAL_SD_WriteBlocks_DMA(&hsd, SDBUF_TX, 0, 1) == HAL_OK )( R  V! m' x7 J% x2 l
  49.       {3 t- S# H' m- U, A( m) R
  50.           //等待DMA传输完成,并且SD卡状态为传输状态9 `" ~2 v' T/ \
  51.           while( (DMA_SEND_OK ==0 ) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));  
    5 M( G9 ?$ X' b. t: q$ W
  52.           printf("WriteBlocks Successfully\n");1 S' E8 a  r7 i3 P
  53.       
    8 [) O  c4 `3 y" D. ~  V4 X
  54.           for(i=0; i<SDBUF_SIZE; i++)  T! w" [1 H3 z* c' a# y
  55.           {
    / U/ z# u' E7 n
  56.             printf("%d ",SDBUF_TX[i]);" B) o4 w5 A5 V7 r4 V
  57.           }: [3 p( Q( H  U0 H- M
  58.           printf("\r\n");
    ) W  ?$ M. D9 {; f5 ]' l( Z3 R' D
  59.           }
    " l4 m7 U) E/ Y4 {( l2 e
  60.           else+ W" P) U2 P. d* O* S( ^8 I
  61.           {
    7 J' y/ L- J% t8 R- D; w
  62.              printf("WriteBlocks Failed\n");
    ' c* `3 N& j  q. Y. T
  63.           }0 m! e4 i1 A5 y: G0 {) ~
  64.         ]' z* F5 o/ J
  65.           /*------------------- SD DMA 读测试-------------------------------------*/
    5 b+ z7 n) o# d* X8 D. ^& U" H+ Q
  66.           DMA_RCV_OK = 0;   //设置读取完成标志位为0,完成时为1
    . t6 V9 r7 l5 W6 V+ g4 a
  67.           if(HAL_SD_ReadBlocks_DMA(&hsd, SDBUF_RX, 0, 1) == HAL_OK)0 ^/ w% ~% H$ W. U
  68.           {
    9 x/ L' A5 V0 D+ M! d8 Z
  69.               while( (DMA_RCV_OK ==0) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));            C9 t6 m1 p; j  T' a$ M
  70.               printf("ReadBlocks Successfully\n");         
    1 K: P& N+ }) r7 N4 W; I
  71.               for(i=0; i<SDBUF_SIZE; i++), {* o8 Z* R* D6 _3 f8 x4 {
  72.               {% H/ w4 `' \1 M$ ~5 Q. u# W
  73.                 printf("%d ",SDBUF_RX[i]);% U- g% g% z4 E& O: p" n! z) t
  74.               }* E4 x9 K# F/ `! F+ O
  75.               printf("\r\n");) B9 w; A. I8 }( L3 x
  76.            }! k  W2 f" `9 o3 I5 u6 @( M) q  K2 \) w
  77.             else
    4 r5 p9 ^( Z# u6 \6 H0 M
  78.             {) w8 b( O; |) g9 l
  79.               printf("ReadBlocks Failed\n");
    * y$ H3 [% v- m8 d. j; P
  80.             }; U* }# ]9 i$ n, f6 C1 c- S/ f) ^
  81.             while (1){}
    ( Y# i3 D5 V# u* U5 q+ o
  82. }
复制代码

" J. Q$ g* B$ D
  1. 3 J/ z" W) d- E% W( ~# z
  2. //sdio.c
    % y; Q( T" Y+ m6 _; L
  3. $ J, Q5 f5 ?/ H4 y" \" N
  4. extern uint8_t DMA_SEND_OK, DMA_RCV_OK;9 Z$ P1 k* u, [# `2 Z7 F1 R
  5. //发送完成中断处理
    8 J6 W1 L1 H; u; s
  6. void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)1 v9 J) c' E, _' z, X& v7 c/ u$ o
  7. {
    9 x; U, I) ^- u( ^
  8.   DMA_SEND_OK = 1;5 D: c$ R, N) C* b) N4 ]: w4 m: A
  9. }) g3 E- Z7 H3 \3 z* F& F
  10. //接收完成中断处理0 ^4 r( C( _( w! M9 X* V
  11. void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)" ^1 h" @" z8 a7 C' K
  12. {. Q1 q( i! E  j9 I8 j
  13.   DMA_RCV_OK = 1;
    ( g: n* M0 m0 m4 K, p) J  F
  14. }
复制代码
$ {0 O" ]  ]# m" n/ H7 ^! ^
至此,ARM开发进阶知识已全部更新完毕!下一系列介绍物联网操作系统开发!
$ ~  L0 M5 H' F9 W% b3 v! Z+ T$ c' |/ S, W$ V& U4 W' o8 F

$ k( b' |. D% x6 a( X8 \% C转载自: 骆驼听海6 o/ ~8 q6 e  C
如有侵权请联系删除. V: n5 _2 U- H8 O* c- v
收藏 评论0 发布时间:2023-4-11 19:23

举报

0个回答

所属标签

相似分享

官网相关资源

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