使用STM32F103ZET霸道主板實現SD卡的讀寫(非文件系統)

瞭解STM32F103ZET是高容量多管腳的芯片

瞭解SD讀寫線路圖

瞭解SD的基地址

閱讀STM32F10xx英文參考 SDIO那章,咱們編寫代碼邊看文檔解析

建工程,打開包含全部包括外設庫函數的樣本工程,而後留下以下的外設

  • 官方給的E:\2018-9-3\stm32-奮鬥者\STM32 官方庫3.5版本\stm32f10x_stdperiph_lib35\STM32F10x_StdPeriph_Lib_V3.5.0\Utilities\STM32_EVAL\Common下的文件只是用於他們的測試版,所以須要修改stm32_eval_sdio_sd.h中的include,由原來的#include "stm32_eval.h"改成#include "stm32f10x.h"

    數組

在stm32_eval_sdio_sd.c(我更名爲bsp_sdio_sdcard.c)添加新的功能函數

  • 宏定義:sd外設地址(stm32_eval_sdio_sd.h)
/** @defgroup STM32_EVAL_SDIO_SD_Exported_Constants
  * @{
  */ 

/*宏定義*/
#define SDIO_FIFO_ADDRESS                ((uint32_t)0x40018080)  //SDIO_FIOF地址=SDIO地址+0x80至 sdio地址+0xfc
/** 
  * @brief  SDIO Intialization Frequency (400KHz max)
  */
#define SDIO_INIT_CLK_DIV                ((uint8_t)0xB2)
/** 
  * @brief  SDIO Data Transfer Frequency (25MHz max) 
  */
/*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */
#define SDIO_TRANSFER_CLK_DIV            ((uint8_t)0x01)
  • GPIO 初始化
static void  GPIO_Configuration(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    //使能gpio時鐘,判斷APB仍是AHB,看System architecture圖(PDF搜)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC,ENABLE);
    //配置pc8,pc9,pc10,pc11,pc12爲D0,D0,D2,D3,D4,CLK,看電路線路圖
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 |GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;
    GPIO_Init(GPIOC,&GPIO_InitStructure)
    //配置PD2 CMD引腳
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
       GPIO_Init(GPIOD,&GPIO_InitStructure);
    //使能SDIO AHB時鐘
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO,ENABLE);
    //使能DMA
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2,ENABLE);
}
  • DMA 配置(爲啥選擇DMA2的channel4,DMA2的選擇是看System architecture,channel4看DMA2 request mapping)
/**
配置好dma2,一發現有中斷,就自動傳輸 Rx
**/
void SD_DMA_RxConfig(uint32_t *BufferDST, uint32_t BufferSize)
{
        DMA_InitTypeDef DMA_InitStructure;
    //清除標誌位
    DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 |DMA2_FLAG_HT4 | DMA2_FLAG_GL4);
    //禁止DMA
    DMA_Cmd(DMA2_Channel4,DISABLE);

    //傳輸配置
    //外設地址,fifo
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
    //目標地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferDST;
    //傳輸方向
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    //把字轉爲字節
    DMA_InitStructure.DMA_BufferSize = BufferSize / 4;
    //存儲地址自增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
       //不循環  When circular mode is activated, the number of data to be transferred is automatically reloaded with the initial value programmed during the channel configuration phase, and the DMA requests continue to be served.
    DMA_InitStructure.DMA_Mode =  DMA_Mode_Normal;
    //外設數據大小爲字, 32 位
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    //存儲數據大小爲字, 32 位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    //通道優先級高
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    //外設地址不自增
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    //非 存儲器至存儲器模式 The DMA channels can also work without being triggered by a request from a peripheral. This mode is called Memory to Memory mode.
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;    
    
    DMA_Init(DMA4_Channel4,&DMA_InitStructure); 
    /*!< 使能 DMA 通道 */
    DMA_Cmd(DMA2_Channel4, ENABLE);                              
}
/**
配置好dma2,一發現有中斷,就自動傳輸 Tx
**/
void SD_DMA_TxConfig(uint32_t *BufferSRC, uint32_t BufferSize)
{

  DMA_InitTypeDef DMA_InitStructure;

  DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4);

  /*!< DMA2 Channel4 disable */
  DMA_Cmd(DMA2_Channel4, DISABLE);

  /*!< DMA2 Channel4 Config */
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferSRC;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//外設爲寫入目標
  DMA_InitStructure.DMA_BufferSize = BufferSize / 4;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外設地址不自增
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA2_Channel4, &DMA_InitStructure);

  /*!< DMA2 Channel4 enable */
  DMA_Cmd(DMA2_Channel4, ENABLE);  
}
  • 打開stm32_eval_sdio_sd.h文件中發現不少枚舉定義,等
    - 枚舉:SD_Error、 SDTransferState 和 SDCardState
    - 結構體定義: SD_CSD、 SD_CID、 SD_CardStatus 以及 SD_CardInfo
    - 宏定義:命令號定義、 SDIO 傳輸方式、 SD 卡插入狀態以及 SD 卡類型定義。

接下來咱們就開始根據 SD 卡識別過程和數據傳輸過程理解 SD 卡驅動函數代碼。這部分代碼內容也是很是龐大,不可能所有在文檔中所有列出,對於部分函數只介紹其功能。

  • SD卡初始化
  • NVIC初始化
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Configure the NVIC Preemption Priority Bits */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  //SDIO的中斷請求 配置好NVIC的中斷控制器和中斷來,判斷誰的優先級高(假設啓動多箇中斷)。先配NVIC,在配外部中斷器來屏蔽--硬件或軟件(事件或中斷)
  NVIC_InitStructure.NVIC_IRQChannel =  SDIO_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_Init(&NVIC_InitStructure);
    //SDIO_IRQ不須要外部中斷/事件或軟件中斷/事件,所以不須要初始化EXIT控制器的所有寄存器,好比中斷屏蔽寄存器、事件屏蔽寄存器,看圖External interrupt/event controller block diagram
}
  • 初始化SD卡
/**
 描述  :初始化SD卡,使卡處於就緒狀態(準備傳輸數據)
 */
- 此函數原stm32_eval_sdio_sd.c有,不需添加,須要修改
SD_Error SD_Init(void)
{
    /*重置SD_Error狀態*/
  SD_Error errorstatus = SD_OK;
  
    NVIC_Configuration();
    
  /* SDIO 外設底層引腳初始化 */
  GPIO_Configuration();

  /*對SDIO的全部寄存器進行復位*/
  SDIO_DeInit();  

  /*上電並進行卡識別流程,確認卡的操做電壓  */
  errorstatus = SD_PowerON(); 

  /*若是上電,識別不成功,返回「響應超時」錯誤 */
  if (errorstatus != SD_OK)
  {
    /*!< CMD Response TimeOut (wait for CMDSENT flag) */
    return(errorstatus);    
  }

  /*卡識別成功,進行卡初始化    */
  errorstatus = SD_InitializeCards(); 

  if (errorstatus != SD_OK)   //失敗返回
  {
    /*!< CMD Response TimeOut (wait for CMDSENT flag) */
    return(errorstatus);
  }

 
  /*!< Configure the SDIO peripheral */
 
  /*!< on STM32F2xx devices, SDIOCLK is fixed to 48MHz */  
   /*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */
   //從新配置 SDIO 外設,提升時鐘頻率,由卡識別模式的400khz提高到數據傳輸模式小於25Mhz
  SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;
  //上升沿採集數據 
  SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
  // 不使能Bypass,使SDIO_CK通過SDIO_ClockDiv分頻
  SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
  //開啓的話,總線空閒時關閉SD_CLK 時鐘
  SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
  //暫時配置成lbit模式
  SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
  //硬件流。若開啓,在FIFO不能進行發送和接受數據是,數據暫停
  SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
  
  SDIO_Init(&SDIO_InitStructure);
  
  if (errorstatus == SD_OK)
  {
    /*----------------- Read CSD/CID MSD registers ------------------*/
    //用來讀取csd/cid寄存器
    /*調用 SD_GetCardInfo 函數獲取 SD 卡信息,它須要一個指向 SD_CardInfo 類型變
量地址的指針形參,這裏賦值爲 SDCardInfo 變量的地址。 SD 卡信息主要是 CID
和 CSD 寄存器內容,這兩個寄存器內容在 SD_InitializeCards 函數中都完成讀取過
程並將其分別存放在 CID_Tab 數組和 CSD_Tab 數組中,因此 SD_GetCardInfo 函
數只是簡單的把這兩個數組內容整合複製到 SDCardInfo 變量對應成員內。正確執
行 SD_GetCardInfo 函數後, SDCardInfo 變量就存放了 SD 卡的不少狀態信息,這
在以後應用中使用頻率是很高的。
    */
    errorstatus = SD_GetCardInfo(&SDCardInfo);
  }

  if (errorstatus == SD_OK)
  {
    /*----------------- Select Card --------------------------------*/
    //經過cm7,rca選擇要操做的卡
    errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));
  }

  if (errorstatus == SD_OK)
  {
    //提升讀寫,開啓4bit模式
    errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);
  }  

  return(errorstatus);
}
  • 調用 SD_PowerON 函數,它用於查詢卡的工做電壓和時鐘控制配置,並返回SD_Error 類型錯誤,該函數是整個 SD 識別精髓,
  • 此函數原stm32_eval_sdio_sd.c有,不需添加
//確保 SD 卡的工做電壓和配置控制時鐘
SD_Error SD_PowerON(void)
{
  SD_Error errorstatus = SD_OK;
  uint32_t response = 0, count = 0, validvoltage = 0;
  uint32_t SDType = SD_STD_CAPACITY;

  /*!< Power ON Sequence -----------------------------------------------------*/
  /*!< Configure the SDIO peripheral */
  /*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) */
  /*!< on STM32F2xx devices, SDIOCLK is fixed to 48MHz */
  /*!< SDIO_CK for initialization should not exceed 400 KHz */  
  SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;
  SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
  SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
  SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
  // 初始化的時候暫時把數據線配置成一根
  SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
  //禁止硬件流控制
  SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
  SDIO_Init(&SDIO_InitStructure);

  /*!< Set Power State to ON */
  //開啓外設電源
  SDIO_SetPowerState(SDIO_PowerState_ON);

  /*!< Enable SDIO Clock */
  //使能SDIO時鐘
  SDIO_ClockCmd(ENABLE);

  /*!< CMD0: GO_IDLE_STATE ---------------------------------------------------*/
  /*!< No CMD response required */
  //發送一系列命令。開始卡的識別流程
  SDIO_CmdInitStructure.SDIO_Argument = 0x0;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE;
  //設置具體的返回類型,
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;
  //SDIO是否開啓或關閉等待中斷
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  /* CPSM 在開始發送命令以前等待數據傳輸結束 */
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
   //檢測是否正確接收CM0 , CmdError 函數用於無需響應的命令發送
  errorstatus = CmdError();

  if (errorstatus != SD_OK)
  {
    /*!< CMD Response TimeOut (wait for CMDSENT flag) */
    //響應超時
    return(errorstatus);
  }

  /*!< CMD8: SEND_IF_COND ----------------------------------------------------*/
  /*!< Send CMD8 to verify SD card interface operating condition */
  /*!< Argument: - [31:12]: Reserved (shall be set to '0')
               - [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)
               - [7:0]: Check Pattern (recommended 0xAA) */
  /*!< CMD Response: R7 */
  //發送CMD8檢查SD卡電壓操做  
  //發送 CMD8 命令,檢測 SD 卡支持的操做條件,主要就是電壓匹配, CMD8 的響
  //應類型是 R7,使用 CmdResp7Error 函數可獲取獲得 R7 響應結果,它是經過檢測
  //SDIO_STA 寄 存 器 相 關 位 完 成 的 , 並 具 有 等 待 超 時 檢 測 功 能 。  
  SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
   //檢測是否正確接收 ,Checks for error conditions for R7 response. 搜索R7 (Card interface condition)
  errorstatus = CmdResp7Error();

  if (errorstatus == SD_OK)
  {
    CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 */
    SDType = SD_HIGH_CAPACITY;
  }
  else //無響應,說明1.x
  {
    /*!< CMD55 */
    SDIO_CmdInitStructure.SDIO_Argument = 0x00;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
  }
  /*!< CMD55 */
  //發送CMD55,用於檢測sd卡仍是mmc卡,或者不支持的卡
  SDIO_CmdInitStructure.SDIO_Argument = 0x00;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  //是否響應,沒響應就是mmc或不支持的卡
  errorstatus = CmdResp1Error(SD_CMD_APP_CMD);

  /*!< If errorstatus is Command TimeOut, it is a MMC card */
  /*!< If errorstatus is SD_OK it is a SD card: SD card 2.0 (voltage range mismatch)
     or SD card 1.x */
  if (errorstatus == SD_OK)   //響應cmd44.是sd卡,可能爲1.x也可能2.x
  {
   
   
    //下面,循環發送sdio支持的電壓範圍,
    /*!< SD CARD */
    /*!< Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */
    while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))
    {
      // 在發送 ACMD 命令前都要先向卡發送 CMD55   ,CMD55用於指示下一條指令是應用指令
      /*!< SEND CMD55 APP_CMD with RCA as 0 */
      SDIO_CmdInitStructure.SDIO_Argument = 0x00;
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
      SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
      SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
      SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
      SDIO_SendCommand(&SDIO_CmdInitStructure);

      errorstatus = CmdResp1Error(SD_CMD_APP_CMD);

      if (errorstatus != SD_OK)
      {
        return(errorstatus);
      }
      /* ACMD41  ,肯定卡是否是     SDSC 仍是 SDHC,返回R3(就是OCR寄存器),須要用CmdResp3Error返回狀態,主要從OCR寄存器31位0或1來判斷那種類型
 * 命令參數由支持的電壓範圍及 HCS 位組成, HCS 位置一來區分卡是 SDSC 仍是 SDHC
 * 0:SDSC
 * 1:SDHC
 * 響應: R3,對應的是 OCR 寄存器
 */
 /*使用 ACMD41 命令判斷卡的具體類型。由於是 A 類命令,因此在發送 ACMD41
以前必須先發送 CMD55, CMD55 命令的響應類型的 R1。若是 CMD55 命令都沒
有響應說明是 MMC 卡或不可用卡。在正確發送 CMD55 以後就能夠發送
ACMD41,並根據響應判斷卡類型, ACMD41 的響應號爲 R3, CmdResp3Error 函
數用於檢測命令正確發送並帶有超時檢測功能,但並不具有響應內容接收功能,
須要在斷定命令正確發送以後調用 SDIO_GetResponse 函數才能獲取響應的內容。
 */
      SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType;//0x80100000
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;
      SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
      SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
      SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
      SDIO_SendCommand(&SDIO_CmdInitStructure);

      errorstatus = CmdResp3Error();
      if (errorstatus != SD_OK)
      {
        return(errorstatus);
      }
     /* 若卡需求電壓在 SDIO 的供電電壓範圍內,會自動上電並標誌 pwr_up 位
 * 讀取卡寄存器,卡狀態
 */
      response = SDIO_GetResponse(SDIO_RESP1);
       /* 讀取卡的 ocr 寄存器的 pwr_up 位,看是否已工做在正常電壓 */
      validvoltage = (((response >> 31) == 1) ? 1 : 0);
      count++;  //計算循環
    }
    //結束循環


    // 循環檢測超過必定次數還沒上電
    if (count >= SD_MAX_VOLT_TRIAL)
    {
    // SDIO 不支持 card 的供電電壓
      errorstatus = SD_INVALID_VOLTRANGE;
      return(errorstatus);
    }

    /*檢查卡返回信息中的 HCS 位*/
 /* 判斷 ocr 中的 ccs 位 ,若是是 sdsc 卡則不執行下面的語句 */
    if (response &= SD_HIGH_CAPACITY)  //判斷30位是否爲1
    {
      CardType = SDIO_HIGH_CAPACITY_SD_CARD;
    }

  }/*!< else MMC Card */

  return(errorstatus);
}

到此,程序執行SD卡的SD模式流程圖執行到以下圖、

判斷執行 SD_PowerON 函數無錯誤後,執行下面的 SD_InitializeCards 函數進行與 SD 卡相關的初始化,使得卡進入數據傳輸模式下的待機模式。

  • 此函數原stm32_eval_sdio_sd.c有,不需添加
//描述 :初始化全部的卡或者單個卡進入就緒狀態
SD_Error SD_InitializeCards(void)
{
  SD_Error errorstatus = SD_OK;
  uint16_t rca = 0x01;

  if (SDIO_GetPowerState() == SDIO_PowerState_OFF)
  {
    errorstatus = SD_REQUEST_NOT_APPLICABLE;
    return(errorstatus);
  }
   //判斷卡的類型
  if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
  {
    /*!< Send CMD2 ALL_SEND_CID  響應: R2,對應 CID 寄存器*/
    SDIO_CmdInitStructure.SDIO_Argument = 0x0;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp2Error();

    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }
    /* 將返回的 CID 信息存儲起來 CID_Tab已經定義好了,不用咱們本身,直接用 */
    CID_Tab[0] = SDIO_GetRespon se(SDIO_RESP1);
    CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
    CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
    CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
  }
  if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) ||  (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) ||  (SDIO_SECURE_DIGITAL_IO_COMBO_CARD == CardType)
      ||  (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
  {
    /*!< Send CMD3 SET_REL_ADDR with argument 0* 要求各個 SD 卡返回自身的 RCA 地址. */
    /*!< SD Card publishes its RCA. */
    SDIO_CmdInitStructure.SDIO_Argument = 0x00;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
     /* 把接收到的卡相對地址存起來 */
    errorstatus = CmdResp6Error(SD_CMD_SET_REL_ADDR, &rca);

    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }
  }
 /*******************************************************************/
  if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
  {
    RCA = rca;

    /*!< Send CMD9 SEND_CSD with argument as card's RCA 
         響應:R2 對應寄存器 CSD(Card-Specific Data)*/
    SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16);
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp2Error();

    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }

    CSD_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
    CSD_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
    CSD_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
    CSD_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
  }
  /*所有卡初始化成功 */
  errorstatus = SD_OK; /*!< All cards get intialized */

  return(errorstatus);
}

;執行 SD_InitializeCards 函數無錯誤後 SD 卡就已經處於數據傳輸模式下的待機狀態,退出 SD_InitializeCards 後會返回前面的 SD_Init 函數,執行接下來代碼,如下是 SD_Init 函數的後續執行過程。執行以後,卡就從待機狀態轉變爲傳輸模式,能夠說數據傳輸已是萬事俱備了。

SD 卡數據操做:包括數據讀取、數據寫入以及存儲區擦除。數據讀取和寫入均可以分爲單塊操做和多塊操做。

  • 擦除函數
  • 此函數原stm32_eval_sdio_sd.c有,不需添加
SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr)
{
  SD_Error errorstatus = SD_OK;
  uint32_t delay = 0;
  __IO uint32_t maxdelay = 0;
  uint8_t cardstate = 0;

  /*!< Check if the card coomnd class supports erase command */
  if (((CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0)
  {
    errorstatus = SD_REQUEST_NOT_APPLICABLE;
    return(errorstatus);
  }

  maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2);

  if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)  //卡已上鎖
  {
    errorstatus = SD_LOCK_UNLOCK_FAILED;
    return(errorstatus);
  }

  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    //在 sdhc 卡中,地址參數爲塊地址,每塊 512 字節,而 sdsc 卡地址爲字節地址
   //因此如果 sdhc 卡要對地址/512 進行轉換
    startaddr /= 512;
    endaddr /= 512;
  }
  
  /*!< According to sd-card spec 1.0 ERASE_GROUP_START (CMD32) and erase_group_end(CMD33) */
  if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
  {
    /*!< Send CMD32 SD_ERASE_GRP_START with argument as addr  */
    SDIO_CmdInitStructure.SDIO_Argument = startaddr;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;  //R1
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_START);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }

    /*!< Send CMD33 SD_ERASE_GRP_END with argument as addr  */
    SDIO_CmdInitStructure.SDIO_Argument = endaddr;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_END);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }
  }

  /*!< Send CMD38 ERASE */
  SDIO_CmdInitStructure.SDIO_Argument = 0;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_ERASE);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }

  for (delay = 0; delay < maxdelay; delay++)
  {}

  /*!< Wait till the card is in programming state */
  errorstatus = IsCardProgramming(&cardstate);

  while ((errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate)))
  {
    errorstatus = IsCardProgramming(&cardstate);
  }

  return(errorstatus);
}

SD_WriteBlock 函數用於向指定的目標地址寫入一個塊的數據,它有三個形參,分別爲指向待寫入數據的首地址的指針變量、目標寫入地址和塊大小。塊大小通常都設置爲512 字節。 (函數不用本身添加,但須要修改)

SD_Error SD_WriteBlock(uint8_t *writebuff, uint64_t WriteAddr, uint16_t BlockSize)
{
  SD_Error errorstatus = SD_OK;

#if defined (SD_POLLING_MODE)
  uint32_t bytestransferred = 0, count = 0, restwords = 0;
  uint32_t *tempbuff = (uint32_t *)writebuff;
#endif

  TransferError = SD_OK;
  TransferEnd = 0;
  StopCondition = 0;
  
  SDIO->DCTRL = 0x0;


  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
    WriteAddr /= 512;
  }

    /*-------------- add , 沒有這一段容易卡死在DMA檢測中 -------------------*/
    /* Set Block Size for Card,cmd16,
     * 如果sdsc卡,能夠用來設置塊大小,
     * 如果sdhc卡,塊大小爲512字節,不受cmd16影響 
     */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;   
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);

  if (SD_OK != errorstatus)
  {
    return(errorstatus);
  }
 /*********************************************************************************/
  
  /*!< Send CMD24 WRITE_SINGLE_BLOCK */
  SDIO_CmdInitStructure.SDIO_Argument = WriteAddr;    //寫入地址
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_SINGLE_BLOCK;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;     //r1
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }
    
    //配置sdio的寫數據寄存器
  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
  SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;  //可用此參數代替SDIO_DataBlockSize_512b
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;//寫數據,
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;   //開啓數據通道狀態機
  SDIO_DataConfig(&SDIO_DataInitStructure);

  /*!< In case of single data block transfer no need of stop command at all */
#if defined (SD_POLLING_MODE) //普通模式
  while (!(SDIO->STA & (SDIO_FLAG_DBCKEND | SDIO_FLAG_TXUNDERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_STBITERR)))
  {
    if (SDIO_GetFlagStatus(SDIO_FLAG_TXFIFOHE) != RESET)
    {
      if ((512 - bytestransferred) < 32)
      {
        restwords = ((512 - bytestransferred) % 4 == 0) ? ((512 - bytestransferred) / 4) : (( 512 -  bytestransferred) / 4 + 1);
        for (count = 0; count < restwords; count++, tempbuff++, bytestransferred += 4)
        {
          SDIO_WriteData(*tempbuff);
        }
      }
      else
      {
        for (count = 0; count < 8; count++)
        {
          SDIO_WriteData(*(tempbuff + count));
        }
        tempbuff += 8;
        bytestransferred += 32;
      }
    }
  }
  if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
  {
    SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
    errorstatus = SD_DATA_TIMEOUT;
    return(errorstatus);
  }
  else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
  {
    SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
    errorstatus = SD_DATA_CRC_FAIL;
    return(errorstatus);
  }
  else if (SDIO_GetFlagStatus(SDIO_FLAG_TXUNDERR) != RESET)
  {
    SDIO_ClearFlag(SDIO_FLAG_TXUNDERR);
    errorstatus = SD_TX_UNDERRUN;
    return(errorstatus);
  }
  else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
  {
    SDIO_ClearFlag(SDIO_FLAG_STBITERR);
    errorstatus = SD_START_BIT_ERR;
    return(errorstatus);
  }
#elif defined (SD_DMA_MODE) //dma模式
  SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);  //數據傳輸結束中斷
  SD_DMA_TxConfig((uint32_t *)writebuff, BlockSize); //配置dma,跟rx相似
  SDIO_DMACmd(ENABLE);   // 使能sdio的dma請求
#endif

  return(errorstatus);
}

SD_WaitWriteOperation 函數用於檢測和等待數據寫入完成,在調用數據寫入函數以後通常都須要調用, SD_WaitWriteOperation 函數適用於單塊及多塊寫入函數。

/**
  * 上述代碼調用庫函數 SD_DMAEndOfTransferStatus 一直檢測 DMA 的傳輸完成標誌,
當 DMA 傳輸結束時,該函數會返回 SET 值。另外, while 循環中的判斷條件使用的
TransferEnd 和 TransferError 是全局變量,它們會在 SDIO 的中斷服務函數根據傳輸狀況被
設置, 傳輸結束後,根據 TransferError 的值來確認是否正確傳輸,若不正確則直接返回錯
誤代碼。 SD_WaitWriteOperation 函數最後是清除相關標誌位並返回錯誤。因爲這個函數裏
的 while 循環的存在, 它會確保 DMA 的傳輸結束。
  */
SD_Error SD_WaitWriteOperation(void)
{
  SD_Error errorstatus = SD_OK;
  //等待dma是否傳輸
  while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
  {}

  if (TransferError != SD_OK)
  {
    return(TransferError);
  }

  /*!< Clear all the static flags */
  SDIO_ClearFlag(SDIO_STATIC_FLAGS);

  return(errorstatus);
}

SD_ReadBlock函數:

SD_Error SD_ReadBlock(uint8_t *readbuff, uint64_t ReadAddr, uint16_t BlockSize)
{
  SD_Error errorstatus = SD_OK;
#if defined (SD_POLLING_MODE) 
  uint32_t count = 0, *tempbuff = (uint32_t *)readbuff;
#endif

  TransferError = SD_OK;
  TransferEnd = 0;   //傳輸結束標置位,在中斷服務置1
  StopCondition = 0;  
  
  SDIO->DCTRL = 0x0;

  
  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
    ReadAddr /= 512;
  }
  /*******************add,沒有這一段容易卡死在DMA檢測中*************************************/
  /* Set Block Size for Card,cmd16,
     * 如果sdsc卡,能夠用來設置塊大小,
     * 如果sdhc卡,塊大小爲512字節,不受cmd16影響 
     */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;   //r1
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);

  if (SD_OK != errorstatus)
  {
    return(errorstatus);
  }
 /*********************************************************************************/
  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
  SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
  SDIO_DataConfig(&SDIO_DataInitStructure);

  /*!< Send CMD17 READ_SINGLE_BLOCK */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_READ_SINGLE_BLOCK);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }

#if defined (SD_POLLING_MODE)  
  /*!< In case of single block transfer, no need of stop transfer at all.*/
  /*!< Polling mode */
  while (!(SDIO->STA &(SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR)))
  {
    if (SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET)
    {
      for (count = 0; count < 8; count++)
      {
        *(tempbuff + count) = SDIO_ReadData();
      }
      tempbuff += 8;
    }
  }

  if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
  {
    SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
    errorstatus = SD_DATA_TIMEOUT;
    return(errorstatus);
  }
  else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
  {
    SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
    errorstatus = SD_DATA_CRC_FAIL;
    return(errorstatus);
  }
  else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET)
  {
    SDIO_ClearFlag(SDIO_FLAG_RXOVERR);
    errorstatus = SD_RX_OVERRUN;
    return(errorstatus);
  }
  else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
  {
    SDIO_ClearFlag(SDIO_FLAG_STBITERR);
    errorstatus = SD_START_BIT_ERR;
    return(errorstatus);
  }
  while (SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET)
  {
    *tempbuff = SDIO_ReadData();
    tempbuff++;
  }
  
  /*!< Clear all the static flags */
  SDIO_ClearFlag(SDIO_STATIC_FLAGS);

#elif defined (SD_DMA_MODE)
    SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);
    SDIO_DMACmd(ENABLE);
    SD_DMA_RxConfig((uint32_t *)readbuff, BlockSize);
#endif

  return(errorstatus);
}

其餘讀寫操做函數雷同,不用修改。但須要把最後面4句話替換成:

到此,SD卡初始化完成了,以上只是介紹重要那部分的函數,其餘有些細微的修改,先引用,請點擊:SD讀寫測試

設置SDIO 中斷服務函數(stm32f10x_it.c)

// 在 SDIO_ITConfig()這個函數開啓了 sdio 中斷 ,
void SDIO_IRQHandler(void)
{
    //SDIO中斷相關處理
    SD_ProcessIRQSrc(); //定義在bsp_sdio_sdcard.c

}
/*
 * 函數名:SD_ProcessIRQSrc
 * 描述  :數據傳輸結束中斷
 * 輸入  :無        
 * 輸出  :SD錯誤類型
 */
SD_Error SD_ProcessIRQSrc(void)
{
  if (StopCondition == 1)  //發送讀取、多塊讀寫命令時置1
  {
    SDIO->ARG = 0x0;   //命令參數寄存器
    SDIO->CMD = 0x44C;    // 命令寄存器: 0100    01      001100
                        //                      [7:6]   [5:0]
                        //              CPSMEN  WAITRESP CMDINDEX
                        //      開啓命令狀態機 短響應   cmd12 STOP_ TRANSMISSION                      
    TransferError = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);
  }
  else
  {
    TransferError = SD_OK;
  }
  SDIO_ClearITPendingBit(SDIO_IT_DATAEND); //清中斷
  SDIO_ITConfig(SDIO_IT_DATAEND, DISABLE); //關閉sdio中斷使能
  TransferEnd = 1;
  return(TransferError);
}

至此,咱們已經介紹了 SD 卡初始化、 SD 卡數據操做的基礎功能函數以及 SDIO 相關中斷服務函數內容,利用這個 SDIO 驅動,能夠編寫一些簡單的 SD 卡讀寫測試程序。

測試 SD 卡部分的函數是咱們本身編寫的,存放在 sdio_test.c 文件等。

最後附上我編寫的程序代碼,主要實現串口輸入內容,而後保存到sd卡中。再讀取出來,輸出到串口那裏。

點擊下載:【sd卡讀取app

相關文章
相關標籤/搜索