了解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);//使能DMARCC_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);//禁止DMADMA_Cmd(DMA2_Channel4,DISABLE);//传输配置//外设地址,fifoDMA_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提升到数据传输模式小于25MhzSDIO_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;//0x80100000SDIO_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;  //R1SDIO_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;
#endifTransferError = 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;     //r1SDIO_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_512bSDIO_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请求
#endifreturn(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;
#endifTransferError = SD_OK;TransferEnd = 0;   //传输结束标置位,在中断服务置1StopCondition = 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;   //r1SDIO_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);
#endifreturn(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卡读取】

转载于:https://www.cnblogs.com/guguobao/p/10123755.html

使用STM32F103ZET霸道主板实现SD卡的读写(非文件系统)相关推荐

  1. SD卡SPI读写模式,基于51单片机的讲解

    经过验证测试,sd卡使用的spi模式为模式3(mode3),即CPOL和CPHA均为1,时钟线高电平空闲,第二个边沿(上升沿)进行采样. 下面为51单片机软spi读写一个字节时序(模式3)的代码示例, ...

  2. 使用vscode + gcc进行 STM32 单片机开发(三)DMA读写SD卡,移植FATFS文件系统

    背景 在本系列的前两篇文章( 使用vscode + gcc进行 STM32 单片机开发(一)编译及调试 使用vscode + gcc进行 STM32 单片机开发(二)gcc环境 移植rtthread) ...

  3. 嵌入式Linux磁盘(硬盘、SD卡)读写性能测试

    背景 在Windows下有一些磁盘基准测试工具,用于测试硬盘/SD卡的读写速度,如ATTO Disk Benchmark(注:单词「benchmark」就是基准检查的意思). 上一篇文章「市面常见存储 ...

  4. 【STM32Cube_20】在SD卡上移植FATFS文件系统

    本篇详细的记录了如何使用STM32CubeMX移植FATFS文件系统到SD卡上. 1. 准备工作 硬件准备 开发板 首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi): Mi ...

  5. zynq7000 SD卡的读写

    参考博客: https://blog.csdn.net/husipeng86/article/details/52262070 参考上述博客中的程序,报错:ERROR : f_write return ...

  6. fafts sd卡开启中断_使用FreeRTOS在SD卡驱动使用非系统延时导致上电重启不工作的情况...

    一.问题描述 在一个使用FreeRTOS的工程中,只做了SD卡的驱动,由于RTOS使用了Systick,故非系统延时函数使用的是 DWT中的时钟周期(CYCCNT)计数功能,但是在SD卡驱动中使用了这 ...

  7. 第十一天: SD卡原理分析及SD卡启动详解

    主流的外存设备 内存和外存的区别: 一般是把这种(random access memory,随机访问存储器,特点是任意字节读写,掉电丢失)叫内存,把ROM(read only memory,只读存储器 ...

  8. 单片AT89C2051 + SD卡 + 3310LCD = 音乐播放器

    http://www.amobbs.com/thread-4503884-1-1.html 这个小玩意,采用 ATMEL 的传统51MCU作主控制芯片,加上SD卡和显示屏,就可以作简单的音乐播放器了, ...

  9. 解决安卓系统写入SD卡权限问题

    1.需要用户手动赋予的权限( Dangerous Permissions) 所属权限组 权限 日历 READ_CALENDAR 日历 WRITE_CALENDAR 相机 CAMERA 联系人 READ ...

最新文章

  1. 机器学习性能优化全解
  2. OpenCV中的全景拼接例程
  3. 完整java开发中JDBC连接数据库代码和步骤
  4. python中高阶函数改写学生信息管理程序_python利用高阶函数实现剪枝函数
  5. SAP Kyma的Lambda Function describe命令输出
  6. python引用传递_python 是值传递还是引用传递 知乎
  7. Android 进程生命周期 Process Lifecycle
  8. oracle的共享内存段,oracle共享内存段手工清理
  9. mysql练习_MySQL基础知识—习题练习
  10. php区分字符串数字,php如何判断字符串是不是数字
  11. GeoTools操作shapefile
  12. 1946电子计算机诞生什么影响,自1946年世界上第一台电子计算机诞生至今.doc
  13. 小分子PEG的循环节可以做到1-36个,DSPE-PEG4-Mal
  14. 什么是云数据库RDS?
  15. Typora:Typora快捷键
  16. c语言编程 遍历字符串,请教大家一个C语言面试的编程题目 C语言:循环执行让用户输入一串字符串,如123456789......
  17. Android应用开发之所有动画使用详解
  18. SQL数据库修复例子
  19. web前端常见面试题总结
  20. yarn 报错 文件名、目录名或卷标语法不正确

热门文章

  1. 调用startActivityForResult后,onActivityResult立刻回调
  2. Java类的基本运行顺序
  3. Android 破解之道 (二)
  4. java爬虫框架动态_java爬虫框架webmagic
  5. GDOI2017 旅游记
  6. 【GDOI2014模拟】旅行 题解代码
  7. asp从后台调出的公式怎么参与运算_吴望一《流体力学》第一章中微分运算公式的初等证明...
  8. 数学之美系列五 -- 简单之美:布尔代数和搜索引擎的索引
  9. 数学之美系列之一:统计语言模型 (Statistical Language Models)
  10. JZOJ__Day 4:【普及模拟】火柴