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

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

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

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

  • SD卡简介

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

微信图片_20230411192315.png

容量大小不同的SD卡,其内部的操作标准是不同的。很多外设会明确告诉用户本设备最大能够支持容量多大的外扩,不能无限制的扩存,因为容量越大,操作不同,对控制器的要求比较高。当前设备的控制器不足以支持大容量,所以有外扩限制。

微信图片_20230411192311.png

SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。


微信图片_20230411192306.png

  • SD卡的物理结构


一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。

微信图片_20230411192301.png

SD卡由9个引脚与外部通信,支持SPI和SDIO两种模式,不同模式下,SD卡引脚功能描述如下表所示。



存储单元:是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;

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

卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;

接口驱动器:控制SD卡引脚的输入输出。


  • SD卡内部寄存器

SD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。

微信图片_20230411192255.png

  • SDIO接口

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

微信图片_20230411192250.png

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 的通信模式。

  • SDIO组成

SDIO 适配器块提供特定于 MMC/SD/SD I/O 卡的所有功能,如时钟生成单元、命令和数据传输。

APB2 接口访问 SDIO 适配器寄存器,并且生成中断和 DMA 请求信号。

微信图片_20230411192246.png

注:
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。

注意:在SD卡初始化时,SDIO_CK不可以超过400Khz,初始化完成后,可以设置为最大频率(但不可以超过SD卡最大操作频率)。

微信图片_20230411192243.png

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

  • SDIO 命令简介

SD命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与SD主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。

SD命令格式固定为48bit,都是通过CMD线连续传输的,数据线不参与。

微信图片_20230411192239.png

SD命令的组成如下:
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。

传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。

命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分。

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

地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。

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

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

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

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

SD命令响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:

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

SDIO读数据

微信图片_20230411192236.png

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

SDIO写数据

微信图片_20230411192231.png

  • SD卡操作模式

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

SD卡系统定义了两种操作模式:卡识别模式和数据传输模式。

在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。

每个不同的操作模式下,SD卡都有不同状态,通过命令控制实现卡状态的切换,在不同的状态下做不同的事,比如在发送数据前需要SD卡处于传输状态,发送数据时,SD卡处于接收状态。

微信图片_20230411192227.png

  • 卡识别模式

微信图片_20230411192220.png

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

注:在卡识别过程中,要求SD卡工作在识别时钟频率FOD的状态下,在SD卡初始化时,SDIO_CK不可以超过400Khz

  • 数据传输模式

微信图片_20230411192214.png

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

  • SD卡普通模式操作实例

实验内容:向SD卡写入数据后读出

实验步骤:

1.配置RCC,与以往不同的是SDIO适配器的时钟是单独配置的,需要专用的SDIOCLK,标准工作在48MHz

微信图片_20230411192210.png

2.配置SDIO

微信图片_20230411192207.png

微信图片_20230411192203.png

3.编写代码

  1. //mian.c
  2. #include "main.h"
  3. #include "stm32f4xx_hal.h"
  4. #include "sdio.h"
  5. #include "usart.h"
  6. #include "gpio.h"
  7. #define SDBUF_SIZE   1024
  8. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收
  9. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出
  10. int main(void)
  11. {
  12.       uint32_t i;
  13.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
  14.       HAL_Init();
  15.       SystemClock_Config();
  16.       MX_GPIO_Init();
  17.       MX_SDIO_SD_Init();
  18.       MX_USART1_UART_Init();
  19.       printf("this is sd test\n");
  20.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印
  21.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
  22.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);
  23.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
  24.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
  25.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
  26.       /*--------------------SD卡写测试----------------------------------*/
  27.       memset(SDBUF_TX, 0x8, SDBUF_SIZE);  /、填充TXbuf为0x8
  28.       /**
  29.          //函数功能及参数描述                             
  30.         *HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData,
  31.                       *uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
  32.         * @param  hsd Pointer to SD handle
  33.         * @param  pData pointer to the buffer that will contain the data to transmit
  34.         * @param  BlockAdd Block Address where data will be written,从哪一个块开始写  
  35.         * @param  NumberOfBlocks Number of SD blocks to write   写几个块
  36.         * @param  Timeout Specify timeout value 超时时间
  37.         * @retval HAL status
  38.         if( HAL_SD_WriteBlocks(&hsd, SDBUF_TX, 0 , 2, 1000) == HAL_OK)
  39.         {
  40.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//处于传输状态退出
  41.             printf("WriteBlocks Successfully\n");
  42.             for(i=0; i<SDBUF_SIZE; i++)
  43.             {
  44.                 printf("%d ",SDBUF_TX[i]);
  45.             }
  46.             printf("\r\n");
  47.         }
  48.         else
  49.         {
  50.             printf("WriteBlocks Failed\n");
  51.         }
  52.         /*--------------------SD 卡读测试----------------------------------*/
  53.         //与HAL_SD_WriteBlocks函数的参数功能相同
  54.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
  55.         {
  56.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);//返回到传输状态退出
  57.             printf("ReadBlocks Successfully\n");
  58.             for(i=0; i<SDBUF_SIZE; i++)
  59.             {
  60.                 printf("%d ",SDBUF_RX[i]);
  61.             }
  62.             printf("\r\n");
  63.         }
  64.         else
  65.         {
  66.             printf("ReadBlocks Failed\n");
  67.         }
  68.         /*--------------------SD 擦除测试----------------------------------*/
  69.         if (HAL_SD_Erase(&hsd, 0, 1) == HAL_OK )
  70.         {
  71.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
  72.             printf("SD_Erase Successfully\n");
  73.         }
  74.         else
  75.         {
  76.             printf("SD_Erase Failed\n");
  77.         }
  78.         /*--------------------SD 卡读测试----------------------------------*/
  79.         if( HAL_SD_ReadBlocks(&hsd, SDBUF_RX , 0, 2, 1000) == HAL_OK)
  80.         {
  81.             while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER);
  82.             printf("ReadBlocks Successfully\n");
  83.             for(i=0; i<SDBUF_SIZE; i++)
  84.             {
  85.                 printf("%d ",SDBUF_RX[i]);
  86.             }
  87.             printf("\r\n");
  88.             }
  89.         else
  90.         {
  91.             printf("ReadBlocks Failed\n");
  92.         }
  93.         while (1)
  94.         {
  95.         }  
  96. }
复制代码
  1. //sdio.c
  2. //此代码为工程自动生成,非用户编写,这里只做分析
  3. #include "sdio.h"
  4. #include "gpio.h"
  5. /* USER CODE BEGIN 0 */
  6. /* USER CODE END 0 */
  7. SD_HandleTypeDef hsd;
  8. /* SDIO init function */
  9. void MX_SDIO_SD_Init(void)   //初始化配置
  10. {
  11.   hsd.Instance = SDIO;    //SDIO句柄,整个系统中只有这一个
  12.   hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
  13.   hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
  14.   hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
  15.   hsd.Init.BusWide = SDIO_BUS_WIDE_1B;   //初始化时设置数据宽度为1位
  16.   hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  17.   hsd.Init.ClockDiv = 0;
  18. //HAL_SD_Init()函数内部做了很多事去识别SD卡,具体的卡识别代码分析在下方
  19. if (HAL_SD_Init(&hsd) != HAL_OK)
  20.   {
  21.     _Error_Handler(__FILE__, __LINE__);
  22.   }
  23. //初始化结束后重新配置数据宽度为4位
  24. if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
  25.   {
  26.     _Error_Handler(__FILE__, __LINE__);
  27.   }
  28. }
  29. //卡识别代码如下
  30. HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd)
  31. {
  32.   uint32_t errorstate = HAL_SD_ERROR_NONE;
  33.   SD_InitTypeDef Init;
  34. /* Default SDIO peripheral configuration for SD card initialization */
  35. //完成卡识别
  36.   Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;
  37.   Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;
  38.   Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;
  39.   Init.BusWide             = SDIO_BUS_WIDE_1B;
  40.   Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  41.   Init.ClockDiv            = SDIO_INIT_CLK_DIV;
  42. //SDIO_CK卡识别阶段要求时钟小于400KHz,宏定义
  43. //SDIO_CK计算公式:SDIO_CK=SDIOCLK/(2+CLKDIV)
  44. /* SDIO Initialization Frequency (400KHz max) */
  45. /*#define SDIO_INIT_CLK_DIV     ((uint8_t)0x76)*/
  46. /* Initialize SDIO peripheral interface with default configuration */
  47.   SDIO_Init(hsd->Instance, Init);
  48. /* Disable SDIO Clock */
  49.   __HAL_SD_DISABLE(hsd);
  50. /* Set Power State to ON 对SD卡进行上电 */
  51.   SDIO_PowerState_ON(hsd->Instance);
  52. /* Enable SDIO Clock */
  53.   __HAL_SD_ENABLE(hsd);
  54. /* Required power up waiting time before starting the SD initialization
  55.   sequence */
  56.   HAL_Delay(2U);
  57. /* Identify card operating voltage */
  58.   errorstate = SD_PowerON(hsd);
  59. if(errorstate != HAL_SD_ERROR_NONE)
  60.   {
  61.     hsd->State = HAL_SD_STATE_READY;
  62.     hsd->ErrorCode |= errorstate;
  63. return HAL_ERROR;
  64.   }
  65. /* Card initialization */
  66.   errorstate = SD_InitCard(hsd);
  67. if(errorstate != HAL_SD_ERROR_NONE)
  68.   {
  69.     hsd->State = HAL_SD_STATE_READY;
  70.     hsd->ErrorCode |= errorstate;
  71.     return HAL_ERROR;
  72.   }
  73. return HAL_OK;
  74. }
复制代码

  • SD卡DMA模式操作实例

当SD卡中有大量的音视频需要读取时,整个过程需要CPU干预,这样CPU的利用率就会降低,因此大量数据的传输最好启用DMA。

实验内容:向SD卡写入数据后读出。

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

微信图片_20230411192200.png

配置DMA

微信图片_20230411192156.png

编写代码:
  1. //mian.c

  2. #include "main.h"
  3. #include "stm32f4xx_hal.h"
  4. #include "dma.h"
  5. #include "sdio.h"
  6. #include "usart.h"
  7. #include "gpio.h"

  8. #define SDBUF_SIZE   1024
  9. uint8_t SDBUF_TX[SDBUF_SIZE],SDBUF_RX[SDBUF_SIZE];//数据传输的buf,一个用于传输,一个用于接收
  10. //定义全局变量,最好不定义局部变量,防止因过大造成栈溢出
  11. uint8_t DMA_SEND_OK, DMA_RCV_OK;

  12. int main(void)
  13. {
  14.       uint32_t i;
  15.       HAL_SD_CardInfoTypeDef pCardInfo;//定义结构体用来接收卡信息
  16.       HAL_Init();
  17.       SystemClock_Config();
  18.       MX_GPIO_Init();
  19.       MX_DMA_Init();
  20.       MX_SDIO_SD_Init();
  21.       MX_USART1_UART_Init();

  22.       printf("this is sd test\n");
  23.       //卡识别结束后就可以调用HAL_SD_GetCardInfo()函数获取卡信息并打印
  24.       HAL_SD_GetCardInfo(&hsd, &pCardInfo);
  25.       printf("pCardInfo.CardType = %u\n",pCardInfo.CardType);
  26.       printf("pCardInfo.CardVersion = %u\n",pCardInfo.CardVersion);//版本
  27.       printf("pCardInfo.BlockNbr = %u\n",pCardInfo.BlockNbr);//SD卡块数
  28.       printf("pCardInfo.BlockSize = %u\n",pCardInfo.BlockSize);//每一块大小
  29.       /*------------------- SD DMA 写测试-------------------------------------*/
  30.       memset(SDBUF_TX, 0x2, SDBUF_SIZE );
  31.       DMA_SEND_OK = 0;  //设置发送完成标志位为0,完成时为1
  32.        /**
  33.        //函数原型
  34.        *HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData,
  35.                                              uint32_t BlockAdd, uint32_t NumberOfBlocks)
  36.       
  37.         * @param  hsd Pointer to SD handle
  38.         * @param  pData Pointer to the buffer that will contain the data to transmit
  39.         * @param  BlockAdd Block Address where data will be written  从哪个块开始写
  40.         * @param  NumberOfBlocks Number of blocks to write  写几个块
  41.         * @retval HAL status
  42.         */
  43.       
  44.       if( HAL_SD_WriteBlocks_DMA(&hsd, SDBUF_TX, 0, 1) == HAL_OK )
  45.       {
  46.           //等待DMA传输完成,并且SD卡状态为传输状态
  47.           while( (DMA_SEND_OK ==0 ) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));  
  48.           printf("WriteBlocks Successfully\n");
  49.       
  50.           for(i=0; i<SDBUF_SIZE; i++)
  51.           {
  52.             printf("%d ",SDBUF_TX[i]);
  53.           }
  54.           printf("\r\n");
  55.           }
  56.           else
  57.           {
  58.              printf("WriteBlocks Failed\n");
  59.           }
  60.       
  61.           /*------------------- SD DMA 读测试-------------------------------------*/
  62.           DMA_RCV_OK = 0;   //设置读取完成标志位为0,完成时为1
  63.           if(HAL_SD_ReadBlocks_DMA(&hsd, SDBUF_RX, 0, 1) == HAL_OK)
  64.           {
  65.               while( (DMA_RCV_OK ==0) || (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER));         
  66.               printf("ReadBlocks Successfully\n");         
  67.               for(i=0; i<SDBUF_SIZE; i++)
  68.               {
  69.                 printf("%d ",SDBUF_RX[i]);
  70.               }
  71.               printf("\r\n");
  72.            }
  73.             else
  74.             {
  75.               printf("ReadBlocks Failed\n");
  76.             }
  77.             while (1){}
  78. }
复制代码


  1. //sdio.c

  2. extern uint8_t DMA_SEND_OK, DMA_RCV_OK;
  3. //发送完成中断处理
  4. void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
  5. {
  6.   DMA_SEND_OK = 1;
  7. }
  8. //接收完成中断处理
  9. void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
  10. {
  11.   DMA_RCV_OK = 1;
  12. }
复制代码

至此,ARM开发进阶知识已全部更新完毕!下一系列介绍物联网操作系统开发!


转载自: 骆驼听海
如有侵权请联系删除
收藏 评论0 发布时间:2023-4-11 19:23

举报

0个回答

所属标签

相似分享

官网相关资源

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