STM32CubeMx入門教程(9):SDIO接口DMA模式應用

2023-07-12 12:03:32 來源:嵌入式開發始站

導語“我們在前面章節中使用了SDIO接口SD卡進行讀寫操作,使用的輪詢模式,這種模式效率低下,F103有SDIO接口的DMA模式,DMA模式在不需要CPU操作的情況下,自動的將數據進行讀取和寫入。”


(資料圖片僅供參考)

第一節 系統要求

同第八章。

第二節 CubeMx配置

SDIO配置為4位的總線模式。

在DMA的配置中,SDIO的DMA通道只有一個,所以讀和寫之間需要進行方向改變。地址增長選擇內存,這是因為我們把SDIO外設的數據發送到內存中,或從內存中讀入數據。

在NVIC中斷配置中,設置SDIO的中斷優先級比DMA的優先級高。

玩成上述配置后,進行代碼生成。

第三節 MDK代碼編寫

stm32F103 中SDIO的DMA只有一個通道,因此讀寫是公用的,需要在讀寫之前進行方向配置,不能簡單的調用HALSDReadBlocksDMA()庫函數來完成讀,不能簡單的調用HALSDWriteBlocksDMA()來完成寫操作。我們編寫DIOReadBlocksDMA()、SDIOWriteBlocksDMA()來使用DMA模式。

(1)sdio.h

/* USER CODE BEGIN Private defines */extern DMA_HandleTypeDef hdma_sdio;/* USER CODE END Private defines *//* USER CODE BEGIN Prototypes */ HAL_StatusTypeDef SDIO_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks); HAL_StatusTypeDef SDIO_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks); /* USER CODE END Prototypes */在sdio.c中/* USER CODE BEGIN 1 */ HAL_StatusTypeDef SDIO_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks) {        HAL_StatusTypeDef Return_Status;        HAL_SD_CardStateTypeDef SD_Card_Status;        do        {               SD_Card_Status = HAL_SD_GetCardState(hsd);        }while(SD_Card_Status != HAL_SD_CARD_TRANSFER );        /* SDIO DMA DeInit */        /* SDIO DeInit */        HAL_DMA_DeInit(&hdma_sdio);        /* 改變DMA的方向,重新初始化 */        hdma_sdio.Instance = DMA2_Channel4;        hdma_sdio.Init.Direction = DMA_PERIPH_TO_MEMORY;        hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;        hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;        hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;        hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;        hdma_sdio.Init.Mode = DMA_NORMAL;        hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;        if (HAL_DMA_Init(&hdma_sdio) != HAL_OK)        {               Error_Handler();        }        __HAL_LINKDMA( hsd,hdmarx,hdma_sdio);        Return_Status = HAL_SD_ReadBlocks_DMA( hsd,pData, BlockAdd, NumberOfBlocks);        return Return_Status; } HAL_StatusTypeDef SDIO_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks) {        HAL_StatusTypeDef Return_Status;        HAL_SD_CardStateTypeDef SD_Card_Status;        do        {               SD_Card_Status = HAL_SD_GetCardState(hsd);        }while(SD_Card_Status != HAL_SD_CARD_TRANSFER );        /* SDIO DMA DeInit */        /* SDIO DeInit */        HAL_DMA_DeInit(&hdma_sdio);        /* 改變DMA的方向,重新初始化 */        hdma_sdio.Instance = DMA2_Channel4;        hdma_sdio.Init.Direction = DMA_MEMORY_TO_PERIPH;        hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;        hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;        hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;        hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;        hdma_sdio.Init.Mode = DMA_NORMAL;        hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;        if (HAL_DMA_Init(&hdma_sdio) != HAL_OK)        {               Error_Handler();        }        __HAL_LINKDMA(hsd,hdmatx,hdma_sdio);              Return_Status = HAL_SD_WriteBlocks_DMA(hsd,pData, BlockAdd, NumberOfBlocks);        return Return_Status; }

在min.c中

/*SD 操作*/typedef enum {FAILED= 0, PASSED = !FAILED} TestStatus;/* 私有宏定義 ----------------------------------------------------------------*/#define BLOCK_SIZE            512         // SD卡塊大小     #define NUMBER_OF_BLOCKS      8           // 測試塊數量(小于15)#define WRITE_READ_ADDRESS    0x00002000  // 測試讀寫地址#define SDMMC                            hsd/* 私有變量 ------------------------------------------------------------------*/__align(4) uint32_t Buffer_Block_Tx[BLOCK_SIZE*NUMBER_OF_BLOCKS]; // 寫數據緩存__align(4) uint32_t Buffer_Block_Rx[BLOCK_SIZE*NUMBER_OF_BLOCKS]; // 讀數據緩存HAL_StatusTypeDef sd_status;    // HAL庫函數操作SD卡函數返回值:操作結果TestStatus test_status;           // 數據測試結果void        SD_EraseTest_DMA();void  SD_Write_Read_Test_DMA();HAL_StatusTypeDef Return_Status;HAL_SD_CardStateTypeDef SD_Card_Status;HAL_DMA_StateTypeDef DMA_Status;

在mian函數中:

//申明測試函數        SD_EraseTest_DMA();        SD_Write_Read_Test_DMA();

在main.c

/*************************************/TestStatus eBuffercmp(uint32_t* pBuffer, uint32_t BufferLength){  while (BufferLength--)  {    /* SD卡擦除后的可能值為0xff或0 */    if ((*pBuffer != 0xFFFFFFFF) && (*pBuffer != 0))    {      return FAILED;    }    pBuffer++;  }  return PASSED;}void SD_EraseTest_DMA(void){       /* 第1個參數為SD卡句柄,第2個參數為擦除起始地址,第3個參數為擦除結束地址 */  sd_status=HAL_SD_Erase(&SDMMC,WRITE_READ_ADDRESS,WRITE_READ_ADDRESS+NUMBER_OF_BLOCKS*4);   printf("《SD》""erase status:%drn",sd_status);       HAL_Delay(500);  if (sd_status == HAL_OK)  {           /* 讀取剛剛擦除的區域 */    sd_status = SDIO_ReadBlocks_DMA(&SDMMC,(uint8_t *)Buffer_Block_Rx,WRITE_READ_ADDRESS,NUMBER_OF_BLOCKS);    printf("《SD》""erase read status:%drn",sd_status);    /* 把擦除區域讀出來對比 */    test_status = eBuffercmp(Buffer_Block_Rx,BLOCK_SIZE*NUMBER_OF_BLOCKS);    if(test_status == PASSED)      printf("《SD》""除測試成功!rn" );     else               printf("《SD》""擦除不成功,數據出錯!rn" );        }  else  {    printf("《SD》""擦除測試失敗!部分SD不支持擦除,只要讀寫測試通過即可rn" );  }}void Fill_Buffer(uint32_t *pBuffer, uint32_t BufferLength, uint32_t Offset){  uint32_t index = 0;  /* 填充數據 */  for (index = 0; index < BufferLength; index++ )  {    pBuffer[index] = index + Offset;  }}TestStatus Buffercmp(uint32_t* pBuffer1, uint32_t* pBuffer2, uint32_t BufferLength){  while (BufferLength--)  {    if(BufferLength%50==0)    {      printf("buf:0x%08X - 0x%08Xrn",*pBuffer1,*pBuffer2);    }    if (*pBuffer1 != *pBuffer2)    {      return FAILED;    }    pBuffer1++;    pBuffer2++;  }  return PASSED;}void SD_Write_Read_Test_DMA(void){         printf(" Warning: this programmay erase all the TFcard data. rn");       printf("rn Initialize SD card successfully!rnrn");       printf(" SD card information! rn");       printf(" CardCapacity  : %llu rn",((unsigned long long)SDMMC.SdCard.BlockSize*hsd.SdCard.BlockNbr));       printf(" CardBlockSize : %d rn",SDMMC.SdCard.BlockSize);       printf(" RCA           : %d rn",SDMMC.SdCard.RelCardAdd);       printf(" CardType      : %d rn",SDMMC.SdCard.CardType);       int i,j = 0;  /* 填充數據到寫緩存 */  Fill_Buffer(Buffer_Block_Tx,BLOCK_SIZE*NUMBER_OF_BLOCKS, 0x6666);  /* 往SD卡寫入數據 */  sd_status = SDIO_WriteBlocks_DMA(&SDMMC,(uint8_t *)Buffer_Block_Tx,WRITE_READ_ADDRESS,NUMBER_OF_BLOCKS);  printf("《SD》""write status:%drn",sd_status);  HAL_Delay(600);  /* 從SD卡讀取數據 */  sd_status = SDIO_ReadBlocks_DMA(&SDMMC,(uint8_t *)Buffer_Block_Rx,WRITE_READ_ADDRESS,NUMBER_OF_BLOCKS);  printf("《SD》""read status:%drn",sd_status);  /* 比較數據 */  test_status = Buffercmp(Buffer_Block_Tx, Buffer_Block_Rx, BLOCK_SIZE*NUMBER_OF_BLOCKS/4);       //比較  if(test_status == PASSED)       {    printf("《SD》""》讀寫測試成功!rn" );              for(i=0;i

第四節 效果演示

可以看到能夠正確的使用DMA進行SD卡的讀寫操作。

第五節 補充DMA 補充

我們在上一節中使用DMA的使用讀寫過程中要改變DMA的方向,在每個讀寫函數中進行,可以單獨實現:

在sdio.h

HAL_StatusTypeDef SD_DMAConfigRX(SD_HandleTypeDef *hsd);HAL_StatusTypeDef SD_DMAConfigTX(SD_HandleTypeDef *hsd);HAL_StatusTypeDef SDIO_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);HAL_StatusTypeDef SDIO_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);

在sdio.c 中

HAL_StatusTypeDef SDIO_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks){       HAL_StatusTypeDef Return_Status;       HAL_SD_CardStateTypeDef SD_Card_Status;       do       {              SD_Card_Status = HAL_SD_GetCardState(hsd);       }while(SD_Card_Status != HAL_SD_CARD_TRANSFER );       if (SD_DMAConfigRX(hsd) != HAL_OK)       {              return HAL_ERROR;       }       else       {              Return_Status = HAL_SD_ReadBlocks_DMA( hsd,pData, BlockAdd, NumberOfBlocks);              return Return_Status;       }}HAL_StatusTypeDef SDIO_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks){       HAL_StatusTypeDef Return_Status;       HAL_SD_CardStateTypeDef SD_Card_Status;       do       {              SD_Card_Status = HAL_SD_GetCardState(hsd);       }while(SD_Card_Status != HAL_SD_CARD_TRANSFER );       if (SD_DMAConfigTX(hsd) != HAL_OK)       {              return HAL_ERROR;       }       else       {              Return_Status = HAL_SD_WriteBlocks_DMA(hsd,pData, BlockAdd, NumberOfBlocks);              return Return_Status;       }}HAL_StatusTypeDef SD_DMAConfigRX(SD_HandleTypeDef *hsd){       HAL_StatusTypeDef status = HAL_ERROR;       hdma_sdio.Instance = DMA2_Channel4;       hdma_sdio.Init.Direction = DMA_PERIPH_TO_MEMORY;       hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;       hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;       hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;       hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;       hdma_sdio.Init.Mode = DMA_NORMAL;       hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;       __HAL_LINKDMA( hsd,hdmarx,hdma_sdio);       HAL_DMA_Abort(&hdma_sdio);       HAL_DMA_DeInit(&hdma_sdio);       status = HAL_DMA_Init(&hdma_sdio);       return status;}HAL_StatusTypeDef SD_DMAConfigTX(SD_HandleTypeDef *hsd){       HAL_StatusTypeDef status = HAL_ERROR;       hdma_sdio.Instance = DMA2_Channel4;       hdma_sdio.Init.Direction = DMA_MEMORY_TO_PERIPH;       hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;       hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;       hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;       hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;       hdma_sdio.Init.Mode = DMA_NORMAL;       hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;       __HAL_LINKDMA( hsd,hdmarx,hdma_sdio);       HAL_DMA_Abort(&hdma_sdio);       HAL_DMA_DeInit(&hdma_sdio);       status = HAL_DMA_Init(&hdma_sdio);       return status;}

標簽:

上一篇:STM32CubeMx入門教程(10):Fatfs文件系統的應用
下一篇:最后一頁