| 
 
| 阅读本文需要的基本知识可以参考本文作者发布的文章: 
 细说STM32单片机使用轮询模式直接访问SD卡的方法及其应用-CSDN博客  https://wenchm.blog.csdn.net/article/details/149218456?spm=1011.2415.3001.5331
 
 SD的HAL驱动程序提供了DMA方式读写SD卡的函数,即HAL_SD_WriteBlocks_DMA()和HAL_SD_ReadBlocks_DMA()。当SD卡读写数据量比较大时,使用DMA方式可以减少处理器负荷,提高运行效率。
 
 本文用一个示例演示DMA方式读写SD卡的操作。
 
 一、示例:以DMA方式读写SD卡
 1、示例功能与CubeMX项目设置
 通过串口在串口助手上互动,使用DMA模式测试SD卡的信息、擦除、读写。
 
 (1)RCC、GPIO、USART6、SYS、CodeGenrator
 与参考文章相同。
 
 (2)SDIO
 
 
   
 
   
 
   
 对SDIO进行DMA设置,SDIO有SDIO_RX(接收)和SDIO_TX(发送)两个DMA请求,需要分别关联DMA流。两个DMA流的模式都自动设置为PeripheralFlow Control(外设流程控制),都会自动使用FIFO,数据宽度自动设置为Word,Memory端开启地址自增功能。
 
 对SDIO进行DMA配置后,还必须打开SDIO的全局中断。如果不打开SDIO的全局中断,在使用DMA方式进行读写时,有时会出现失败的情况。SDIO全局中断的抢占优先级应高于或等于DMA流的抢占优先级。
 
 
   
 (3)NVIC
 
 
   
 
 2、主程序与外设初始化
 (1)初始主程序
 完成设置后,CubeMX会自动生成代码。初始的主程序代码如下,代码中增加了DMA初始化函数MX_DMA_Init():
 
 //main.c
 
 /* USER CODE END Header */
 /* Includes ------------------------------------------------------------------*/
 #include "main.h"
 #include "dma.h"
 #include "sdio.h"
 #include "usart.h"
 #include "gpio.h"
 
 /* Private includes ----------------------------------------------------------*/
 /* USER CODE BEGIN Includes */
 #include "keyled.h"
 #include <stdio.h>
 /* USER CODE END Includes */
 
 /* USER CODE BEGIN PV */
 uint8_t SDBuf_TX[BLOCKSIZE]; //Data Sending Cache, BLOCKSIZE=512
 uint8_t SDBuf_RX[BLOCKSIZE]; //Data Received Cache
 /* USER CODE END PV */
 
 /* Private function prototypes -----------------------------------------------*/
 void SystemClock_Config(void);
 
 /* USER CODE BEGIN PFP */
 
 /* USER CODE END PFP */
 
 /**
 * @brief The application entry point.
 * @retval int
 */
 int main(void)
 {
 //此处及之后的内容见下文
 }
 
 
 
 上述程序定义了两个uint8_t类型的全局数组SDBuf_TX和SDBuf_RX,其大小都是BLOCKSIZE,也就是512字节。因为以DMA方式读写数据是非阻塞式的,需要使用全局变量作为存储数据的缓冲区。
 
 (2)DMA初始化
 函数MX_DMA_Init()用于DMA初始化,在自动生成的文件dma.h和dma.c中定义和实现。这个函数的源代码如下,其功能就是使能DMA2控制器时钟,配置两个DMA流的中断优先级。
 
 /* USER CODE BEGIN Header */
 /**
 ******************************************************************************
 * @file    dma.c
 * @brief   This file provides code for the configuration
 *          of all the requested memory to memory DMA transfers.
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
 /* USER CODE END Header */
 
 /* Includes ------------------------------------------------------------------*/
 #include "dma.h"
 
 /* USER CODE BEGIN 0 */
 
 /* USER CODE END 0 */
 
 /*----------------------------------------------------------------------------*/
 /* Configure DMA                                                              */
 /*----------------------------------------------------------------------------*/
 
 /* USER CODE BEGIN 1 */
 
 /* USER CODE END 1 */
 
 /**
 * Enable DMA controller clock
 */
 void MX_DMA_Init(void)
 {
 
 /* DMA controller clock enable */
 __HAL_RCC_DMA2_CLK_ENABLE();
 
 /* DMA interrupt init */
 /* DMA2_Stream3_IRQn interrupt configuration */
 HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 2, 0);
 HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
 /* DMA2_Stream6_IRQn interrupt configuration */
 HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 2, 0);
 HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
 
 }
 
 
 
 (3)SDIO接口和SD卡初始化
 文件sdio.c中的初始化函数MX_SDIO_SD_Init()以及相关变量和函数的代码如下:
 
 /* USER CODE END Header */
 /* Includes ------------------------------------------------------------------*/
 #include "sdio.h"
 
 /* USER CODE BEGIN 0 */
 
 /* USER CODE END 0 */
 
 SD_HandleTypeDef hsd;
 DMA_HandleTypeDef hdma_sdio_rx;
 DMA_HandleTypeDef hdma_sdio_tx;
 
 /* SDIO init function */
 
 void MX_SDIO_SD_Init(void)
 {
 
 /* USER CODE BEGIN SDIO_Init 0 */
 
 /* USER CODE END SDIO_Init 0 */
 
 /* USER CODE BEGIN SDIO_Init 1 */
 
 /* USER CODE END SDIO_Init 1 */
 hsd.Instance = SDIO;
 hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
 hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
 hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
 hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
 hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
 hsd.Init.ClockDiv = 0;
 if (HAL_SD_Init(&hsd) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN SDIO_Init 2 */
 
 /* USER CODE END SDIO_Init 2 */
 
 }
 
 void HAL_SD_MspInit(SD_HandleTypeDef* sdHandle)
 {
 
 GPIO_InitTypeDef GPIO_InitStruct = {0};
 if(sdHandle->Instance==SDIO)
 {
 /* USER CODE BEGIN SDIO_MspInit 0 */
 
 /* USER CODE END SDIO_MspInit 0 */
 /* SDIO clock enable */
 __HAL_RCC_SDIO_CLK_ENABLE();
 
 __HAL_RCC_GPIOC_CLK_ENABLE();
 __HAL_RCC_GPIOD_CLK_ENABLE();
 /**SDIO GPIO Configuration
 PC8     ------> SDIO_D0
 PC12     ------> SDIO_CK
 PD2     ------> SDIO_CMD
 */
 GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_12;
 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
 GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
 HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
 GPIO_InitStruct.Pin = GPIO_PIN_2;
 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
 GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
 HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
 
 /* SDIO DMA Init */
 /* SDIO_RX Init */
 hdma_sdio_rx.Instance = DMA2_Stream3;
 hdma_sdio_rx.Init.Channel = DMA_CHANNEL_4;
 hdma_sdio_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
 hdma_sdio_rx.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_sdio_rx.Init.MemInc = DMA_MINC_ENABLE;
 hdma_sdio_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
 hdma_sdio_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
 hdma_sdio_rx.Init.Mode = DMA_PFCTRL;
 hdma_sdio_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
 hdma_sdio_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
 hdma_sdio_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
 hdma_sdio_rx.Init.MemBurst = DMA_MBURST_INC4;
 hdma_sdio_rx.Init.PeriphBurst = DMA_PBURST_INC4;
 if (HAL_DMA_Init(&hdma_sdio_rx) != HAL_OK)
 {
 Error_Handler();
 }
 
 __HAL_LINKDMA(sdHandle,hdmarx,hdma_sdio_rx);
 
 /* SDIO_TX Init */
 hdma_sdio_tx.Instance = DMA2_Stream6;
 hdma_sdio_tx.Init.Channel = DMA_CHANNEL_4;
 hdma_sdio_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
 hdma_sdio_tx.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_sdio_tx.Init.MemInc = DMA_MINC_ENABLE;
 hdma_sdio_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
 hdma_sdio_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
 hdma_sdio_tx.Init.Mode = DMA_PFCTRL;
 hdma_sdio_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
 hdma_sdio_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
 hdma_sdio_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
 hdma_sdio_tx.Init.MemBurst = DMA_MBURST_INC4;
 hdma_sdio_tx.Init.PeriphBurst = DMA_PBURST_INC4;
 if (HAL_DMA_Init(&hdma_sdio_tx) != HAL_OK)
 {
 Error_Handler();
 }
 
 __HAL_LINKDMA(sdHandle,hdmatx,hdma_sdio_tx);
 
 /* SDIO interrupt Init */
 HAL_NVIC_SetPriority(SDIO_IRQn, 1, 0);
 HAL_NVIC_EnableIRQ(SDIO_IRQn);
 /* USER CODE BEGIN SDIO_MspInit 1 */
 
 /* USER CODE END SDIO_MspInit 1 */
 }
 }
 
 void HAL_SD_MspDeInit(SD_HandleTypeDef* sdHandle)
 {
 
 if(sdHandle->Instance==SDIO)
 {
 /* USER CODE BEGIN SDIO_MspDeInit 0 */
 
 /* USER CODE END SDIO_MspDeInit 0 */
 /* Peripheral clock disable */
 __HAL_RCC_SDIO_CLK_DISABLE();
 
 /**SDIO GPIO Configuration
 PC8     ------> SDIO_D0
 PC12     ------> SDIO_CK
 PD2     ------> SDIO_CMD
 */
 HAL_GPIO_DeInit(GPIOC, GPIO_PIN_8|GPIO_PIN_12);
 
 HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2);
 
 /* SDIO DMA DeInit */
 HAL_DMA_DeInit(sdHandle->hdmarx);
 HAL_DMA_DeInit(sdHandle->hdmatx);
 
 /* SDIO interrupt Deinit */
 HAL_NVIC_DisableIRQ(SDIO_IRQn);
 /* USER CODE BEGIN SDIO_MspDeInit 1 */
 
 /* USER CODE END SDIO_MspDeInit 1 */
 }
 }
 
 
 
 MSP初始化函数HAL_SD_MspInit()中增加了DMA初始化功能,上述程序定义了两个DMA流对象与SDIO的两个DMA请求关联。
 
 3、程序功能实现
 (1)主程序
 在主程序中修改和添加代码,完成的主程序代码如下:
 
 // 继续上面的main.c代码
 
 /**
 * @brief  The application entry point.
 * @retval int
 */
 int main(void)
 {
 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();
 
 /* Configure the system clock */
 SystemClock_Config();
 
 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_DMA_Init();
 MX_SDIO_SD_Init();
 MX_USART6_UART_Init();
 
 /* USER CODE BEGIN 2 */
 
 uint8_t startstr[] = "Demo13_2: SD card R/W-DMA. \r\n";
 HAL_UART_Transmit(&huart6,startstr,sizeof(startstr),0xFFFF);
 
 uint8_t startstr1[] = "Read/write SD card via DMA. \r\n\r\n";
 HAL_UART_Transmit(&huart6,startstr1,sizeof(startstr1),0xFFFF);
 
 //show menu
 printf("[S2]KeyUp   =SD card info. \r\n");
 printf("[S3]KeyDown =Erase 0-10 blocks. \r\n");
 printf("[S4]KeyLeft =Write block. \r\n");
 printf("[S1]KeyRight=Read block. \r\n\r\n");
 
 while(1)
 {
 KEYS waitKey=ScanPressedKey(KEY_WAIT_ALWAYS);        //Waiting for the key
 
 if (waitKey==KEY_UP)
 {
 SDCard_ShowInfo();
 printf("Reselect menu item or reset. \r\n");
 }
 else if (waitKey== KEY_DOWN)
 {
 SDCard_EraseBlocks();                        //EraseBlocks 0-10
 printf("Reselect menu item or reset. \r\n");
 }
 else if (waitKey== KEY_LEFT)
 SDCard_TestWrite_DMA();
 else if (waitKey== KEY_RIGHT)
 SDCard_TestRead_DMA();
 
 HAL_Delay(500);
 }
 /* USER CODE END 2 */
 
 //以下的代码内容在下文接续
 
 
 
 主程序显示了4个菜单项,通过4个按键选择操作。SDCard_ShowInfo()和SDCard_EraseBlocks()的代码与前一示例的完全相同,就不再显示和解释了。函数SDCard_TestWrite_DMA()以DMA方式向SD卡写入数据,函数SDCard_TestRead_DMA()以DMA方式从SD卡读取数据。
 
 (2)中断ISR和回调函数
 在示例中,SDIO的全局中断开启了,两个DMA流中断也是自动打开的,在文件stm32f4xx_it.c中,自动生成了这3个中断的ISR,代码如下:
 
 //stm32f4xx_it.h
 
 /* Exported functions prototypes ---------------------------------------------*/
 // 此处的函数声明省略
 
 void SDIO_IRQHandler(void);
 void DMA2_Stream3_IRQHandler(void);
 void DMA2_Stream6_IRQHandler(void);
 
 
 /**
 * @brief This function handles SDIO global interrupt.
 */
 void SDIO_IRQHandler(void)
 {
 /* USER CODE BEGIN SDIO_IRQn 0 */
 
 /* USER CODE END SDIO_IRQn 0 */
 HAL_SD_IRQHandler(&hsd);
 /* USER CODE BEGIN SDIO_IRQn 1 */
 
 /* USER CODE END SDIO_IRQn 1 */
 }
 
 /**
 * @brief This function handles DMA2 stream3 global interrupt.
 */
 void DMA2_Stream3_IRQHandler(void)
 {
 /* USER CODE BEGIN DMA2_Stream3_IRQn 0 */
 
 /* USER CODE END DMA2_Stream3_IRQn 0 */
 HAL_DMA_IRQHandler(&hdma_sdio_rx);
 /* USER CODE BEGIN DMA2_Stream3_IRQn 1 */
 
 /* USER CODE END DMA2_Stream3_IRQn 1 */
 }
 
 /**
 * @brief This function handles DMA2 stream6 global interrupt.
 */
 void DMA2_Stream6_IRQHandler(void)
 {
 /* USER CODE BEGIN DMA2_Stream6_IRQn 0 */
 
 /* USER CODE END DMA2_Stream6_IRQn 0 */
 HAL_DMA_IRQHandler(&hdma_sdio_tx);
 /* USER CODE BEGIN DMA2_Stream6_IRQn 1 */
 
 /* USER CODE END DMA2_Stream6_IRQn 1 */
 }
 
 
 
 这些代码都是由IDE自动生成的。
 
 SDIO有两个最主要的回调函数,HAL_SD_TxCpltCallback()是在中断方式或DMA方式发送数据完成时执行的回调函数,HAL_SD_RxCpltCallback()是在中断方式或DMA方式接收数据完成时执行的回调函数。本示例需要重新实现这两个回调函数,为了便于使用全局数组SDBuf_TX和SDBuf_RX,这两个回调函数放在文件main.c里实现。
 
 (3)DMA方式写数据
 函数HAL_SD_WriteBlocks_DMA()以DMA方式向SD卡写入数据。在文件main.c中自定义的函数SDCard_TestWrite_DMA()就用这个函数测试向SD卡写入数据,同时,还需要实现回调函数HAL_SD_TxCpltCallback()。
 
 // 继续上面的main.c的代码
 // 此处代码在/* USER CODE BEGIN 4/END 4 */代码对内
 
 /* USER CODE BEGIN 4 */
 /* HAL_SD_GetCardInfo(), Display SD card information */
 void        SDCard_ShowInfo()
 {
 HAL_SD_CardInfoTypeDef cardInfo;  //SD card information structure
 HAL_StatusTypeDef res=HAL_SD_GetCardInfo(&hsd, &cardInfo);
 
 if (res!=HAL_OK)
 {
 printf("HAL_SD_GetCardInfo() error. \r\n");
 return;
 }
 
 printf("*** HAL_SD_GetCardInfo() info *** \r\n\r\n");
 
 printf("Card Type= %ld \r\n", cardInfo.CardType);
 printf("Card Version= %ld \r\n", cardInfo.CardVersion);
 printf("Card Class= %ld \r\n", cardInfo.Class);
 printf("Relative Card Address= %ld \r\n", cardInfo.RelCardAdd);
 printf("Block Count= %ld \r\n", cardInfo.BlockNbr);
 printf("Block Size(Bytes)= %ld \r\n", cardInfo.BlockSize);
 printf("LogiBlockCount= %ld \r\n", cardInfo.LogBlockNbr);
 printf("LogiBlockSize(Bytes)= %ld \r\n", cardInfo.LogBlockSize);
 
 uint32_t cap= (cardInfo.BlockNbr*cardInfo.BlockSize);                //bytes
 cap = cap/(1e6);                                                                                        //MB
 
 printf("SD Card Capacity(MB)= %ld \r\n", cap);
 }
 
 /* HAL_SD_Erase(), Erase SD card Blocks*/
 void        SDCard_EraseBlocks()
 {
 uint32_t BlockAddrStart=0;         // Block 0,Addresses using block number
 uint32_t BlockAddrEnd=10;         // Block 10
 
 printf("\r\n*** Erasing blocks *** \r\n\r\n");
 
 if (HAL_SD_Erase(&hsd,BlockAddrStart, BlockAddrEnd)==HAL_OK)
 {
 printf("Erasing blocks,OK. \r\n");
 printf("Blocks 0-10 is erased. \r\n");
 }
 else
 printf("Erasing blocks,fail. \r\n");
 
 HAL_SD_CardStateTypeDef cardState=HAL_SD_GetCardState(&hsd);
 printf("GetCardState()= %ld \r\n", cardState);
 
 // The following code has nothing to do with erasure and can be deleted.[wen]
 while(cardState != HAL_SD_CARD_TRANSFER)   //Wait for return to transmission status
 {
 HAL_Delay(1);
 cardState=HAL_SD_GetCardState(&hsd);
 return;                        //Otherwise, it will not come out after entering the loop
 }
 }
 
 /* HAL_SD_WriteBlocks_DMA(), Write SD card */
 void        SDCard_TestWrite_DMA()        //DMA mode, test write
 {
 printf("\r\n*** DMA Writing blocks *** \r\n\r\n");
 
 for(uint16_t i=0;i<BLOCKSIZE; i++)
 SDBuf_TX=i;                         //generate data
 
 printf("Writing block 6. \r\n");
 printf("Data in [10:15] is: %d ",SDBuf_TX[10]);
 
 for (uint16_t j=11; j<=15;j++)
 {
 printf(", %d", SDBuf_TX[j]);
 }
 printf("\r\nHAL_SD_WriteBlocks_DMA() is to be called. \r\n");
 
 uint32_t BlockAddr=6;                //Block Address
 uint32_t BlockCount=1;                //Block Count
 HAL_SD_WriteBlocks_DMA(&hsd,SDBuf_TX,BlockAddr,BlockCount);  //can erase block automatically
 }
 
 /* HAL_SD_ReadBlocks_DMA(), Read SD card */
 void        SDCard_TestRead_DMA()  //test read
 {
 printf("\r\n*** DMA Reading blocks *** \r\n\r\n");
 printf("\r\nHAL_SD_ReadBlocks_DMA() is to be called. \r\n");
 
 uint32_t BlockAddr=6;                //Block Address
 uint32_t BlockCount=1;                //Block Count
 HAL_SD_ReadBlocks_DMA(&hsd,SDBuf_RX,BlockAddr,BlockCount);
 }
 
 /* SD write callback func */
 void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
 {
 printf("DMA write complete. \r\n");
 printf("HAL_SD_TxCpltCallback() is called. \r\n");
 printf("Reselect menu item or reset. \r\n");
 }
 
 /* SD read callback func */
 void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
 {
 printf("DMA read complete. \r\n");
 printf("HAL_SD_RxCpltCallback() is called. \r\n");
 printf("Data in [10:15] is: %d\r\n",SDBuf_RX[10]);
 
 for (uint16_t j=11; j<=15;j++)
 {
 printf(", %d", SDBuf_RX[j]);
 }
 printf("\r\nReselect menu item or reset. \r\n");
 }
 
 
 
 上述程序首先将数组SDBuf_TX填满数据,然后调用函数HAL_SD_WriteBlocks_DMA()将数组SDBuf_TX里的数据写入SD卡的Block6。函数HAL_SD_WriteBlocks_DMA()可以一次写入多个数据块,写入之前无须擦除块,函数里会自动擦除。
 
 函数HAL_SD_WriteBlocks_DMA()的原型定义详见参考文章。
 
 函数HAL_SD_WriteBlocks_DMA()返回HAL_OK只表示DMA操作正确,并不表示数据写入完成了。以DMA方式向SD卡写入数据完成后,会执行回调函数HAL_SD_TxCpltCallback(),重新实现的这个回调函数的代码很简单,就是显示信息,表示DMA写入已完成。运行时会发现,此回调函数能被正常调用,表示DMA方式写入数据成功。
 
 (4)DMA方式读取数据
 函数HAL_SD_ReadBlocks_DMA()以DMA方式读取SD卡的数据,SDCard_TestRead_DMA()使用这个函数测试读取SD卡数据,同时,还需要实现回调函数HAL_SD_RxCpltCallback()。
 
 函数SDCard_TestRead_DMA()调用函数HAL_SD_ReadBlocks_DMA()读取Block6的数据,保存到全局数组SDBuf_RX里。同样地,函数HAL_SD_ReadBlocks_DMA()返回HAL_OK只是表示DMA操作正确,并不表示数据读取完成了。
 
 以DMA方式读取SD卡数据完成后,会执行回调函数HAL_SD_RxCpltCallback(),重新实现的这个回调函数显示了数组SDBuf_RX里的部分内容。在开发板上运行测试,如果Block 6被写入了数据,会发现读出的数据与写入的数据一致;如果Block 6被擦除后没有写入数据,读出的数组内容全部是0。
 
 SD卡被擦除后,存储单元的内容可能是0,也可能是0xFF,会因SD卡的不同而不一样。
 
 (5)其它代码
 串口:
 
 // 继续上面的main.c的代码
 // 此处代码在/* USER CODE BEGIN 4/END 4 */代码对内
 
 int __io_putchar(int ch)
 {
 HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);
 return ch;
 }
 /* USER CODE END 4 */
 
 
 main.h中声明私有函数:
 
 /* USER CODE BEGIN EFP */
 void SDCard_ShowInfo();
 void SDCard_EraseBlocks();
 void SDCard_TestWrite_DMA();
 void SDCard_TestRead_DMA();
 /* USER CODE END EFP */
 
 
 二、运行与调试
 下载并运行,本示例的项目仍然使用1线数据线,下载后在串口助手上不能显示开始菜单(这个现象不正常),按下开发板的复位键后显示开始菜单:
 
 
   
 S2:
 
 
   
 S3:
 
 
   
 S4:
 
 
   
 S1:
 
 
   
 
 ————————————————
 
 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
 
 原文链接:https://blog.csdn.net/wenchm/article/details/149242997
 
 
 | 
 |