導語“我們在前面章節(jié)中使用了SDIO接口對SD卡進行讀寫操作,使用的輪詢模式,這種模式效率低下,F(xiàn)103有SDIO接口的DMA模式,DMA模式在不需要CPU操作的情況下,自動的將數(shù)據(jù)進行讀取和寫入?!?/p>
第一節(jié) 系統(tǒng)要求
同第八章。
第二節(jié) CubeMx配置
SDIO配置為4位的總線模式。

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

在NVIC中斷配置中,設置SDIO的中斷優(yōu)先級比DMA的優(yōu)先級高。

玩成上述配置后,進行代碼生成。
第三節(jié) MDK代碼編寫
在stm32F103 中SDIO的DMA只有一個通道,因此讀寫是公用的,需要在讀寫之前進行方向配置,不能簡單的調(diào)用HALSDReadBlocksDMA()庫函數(shù)來完成讀,不能簡單的調(diào)用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           // 測試塊數(shù)量(小于15)
#define WRITE_READ_ADDRESS    0x00002000  // 測試讀寫地址
#define SDMMC                            hsd
/* 私有變量 ------------------------------------------------------------------*/
__align(4) uint32_t Buffer_Block_Tx[BLOCK_SIZE*NUMBER_OF_BLOCKS]; // 寫數(shù)據(jù)緩存
__align(4) uint32_t Buffer_Block_Rx[BLOCK_SIZE*NUMBER_OF_BLOCKS]; // 讀數(shù)據(jù)緩存
HAL_StatusTypeDef sd_status;    // HAL庫函數(shù)操作SD卡函數(shù)返回值:操作結果
TestStatus test_status;           // 數(shù)據(jù)測試結果
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函數(shù)中:
//申明測試函數(shù) 
       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個參數(shù)為SD卡句柄,第2個參數(shù)為擦除起始地址,第3個參數(shù)為擦除結束地址 */
  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)
  {       
    /* 讀取剛剛擦除的區(qū)域 */
    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);
    /* 把擦除區(qū)域讀出來對比 */
    test_status = eBuffercmp(Buffer_Block_Rx,BLOCK_SIZE*NUMBER_OF_BLOCKS);
    if(test_status == PASSED)
      printf("《SD》""除測試成功!rn" ); 
    else         
      printf("《SD》""擦除不成功,數(shù)據(jù)出錯!rn" );      
  }
  else
  {
    printf("《SD》""擦除測試失?。〔糠諷D不支持擦除,只要讀寫測試通過即可rn" );
  }
}
void Fill_Buffer(uint32_t *pBuffer, uint32_t BufferLength, uint32_t Offset)
{
  uint32_t index = 0;
  /* 填充數(shù)據(jù) */
  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 program may erase all the TF card 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;
  /* 填充數(shù)據(jù)到寫緩存 */
  Fill_Buffer(Buffer_Block_Tx,BLOCK_SIZE*NUMBER_OF_BLOCKS, 0x6666);
  /* 往SD卡寫入數(shù)據(jù) */
  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卡讀取數(shù)據(jù) */
  sd_status = SDIO_ReadBlocks_DMA(&SDMMC,(uint8_t *)Buffer_Block_Rx,WRITE_READ_ADDRESS,NUMBER_OF_BLOCKS);
  printf("《SD》""read status:%drn",sd_status);
  /* 比較數(shù)據(jù) */
  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
第四節(jié) 效果演示

可以看到能夠正確的使用DMA進行SD卡的讀寫操作。
第五節(jié) 補充DMA 補充
我們在上一節(jié)中使用DMA的使用讀寫過程中要改變DMA的方向,在每個讀寫函數(shù)中進行,可以單獨實現(xiàn):
在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;
}
                        電子發(fā)燒友App
                    
                
                
          
        
        























           
            
            
                
            
評論