本文主要用于快速了解SD卡及SDIO接口,如需更详细的信息,请下载SD卡2.0协议文档,AT32 MCU的RM文档,AT32 MCU的BSP进行了解。
一.SD卡简介
1. SD卡分类
2. SD卡引脚定义
注意:TF卡加卡套也可以当作SD卡使用,但很大一部分不支持SPI访问模式
3. SD卡寄存器
二.SDIO简介
以AT32F435芯片的SDIO接口为例:
1. 功能
#与 SD 储存卡 2.0 规格版本全兼容
#与 SDI/O 卡 2.0 规格版本全兼容并支持 1 位和 4 位数据总线模式
#与多媒体卡 4.2 规格版本全兼容并支持 1 位、4 位和 8 位数据总线模式
#与较早的多媒体卡规格版本全兼容
#支持 DMA 传输
#8位总线模式下数据传输速率可达 50 MHz
#中断请求
2. 通信
#命令:命令是启动操作的令牌。 命令从主机发送到单个卡(寻址命令)或所有连接 的卡(广播命令),命令在CMD总线串行传输。
#响应:响应是从卡发送到主机,作为对先前命令的答复,响应在CMD总线串行传输。
#数据:数据可以从卡传送到主机或是主机传送至卡端,数据通过SDIO_D数据总线进 行传送。
举个例子,以下是SDIO多数据块读操作:
3. 寄存器
SDIO电源控制寄存器:控制电源开关,相当于外设使能开关
SDIO时钟控制寄存器:
SDIO命令寄存器:
SDIO命令响应寄存器:包含最后收到的命令响应中的命令索引
SDIO响应1..4寄存器:包含卡的状态,即收到响应的部分信息
SDIO数据定时器寄存器:包含以卡总线时钟周期为单位的数据超时时间
SDIO数据长度寄存器:)包含需要传输的数据字节长度
SDIO数据控制寄存器:
三.SD卡2.0协议简介
1. 初始化流程:
2. 命令(CMDx):
3. 响应(Rx):
4. 卡状态:
四.源码解读
1. 源码下载
到雅特力官网下载最新AT32F425这款MCU的最新BSP,链接如下:
打开如下工程:
AT32F435_437_Firmware_Library_V2.1.9\project\at_start_f435\examples\sdio\sdio_fatfs。
2. 初始化代码解读
对照第三章第1节图片,并结合以下代码注释:
/**
**************************************************************************
* @file at32_sdio.c
* @brief this file provides a set of functions needed to manage the
* sdio/mmc card memory.
**************************************************************************
* Copyright notice & Disclaimer
*
* The software Board Support Package (BSP) that is made available to
* download from Artery official website is the copyrighted work of Artery.
* Artery authorizes customers to use, copy, and distribute the BSP
* software and its related documentation for the purpose of design and
* development in conjunction with Artery microcontrollers. Use of the
* software is governed by this copyright notice and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
* GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
* TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
* STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
* INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
*
**************************************************************************
*/
#include <string.h>
#include "at32_sdio.h"
#include "at32f435_437_board.h"
/** @addtogroup AT32F435_periph_examples
* @{
*/
/** @addtogroup 435_SDIO_fatfs
* @{
*/
sdio_command_struct_type sdio_command_init_struct;
sdio_data_struct_type sdio_data_init_struct;
static sd_memory_card_type card_type = SDIO_STD_CAPACITY_SD_CARD_V1_1; /* sd card type */
static uint32_t csd_table[4], cid_table[4], rca = 0; /* csd, sid, rca */
static sd_data_transfer_mode_type device_mode = SD_TRANSFER_POLLING_MODE; /* working mode */
static uint8_t stop_flag = 0; /* transmit stop flag */
volatile sd_error_status_type transfer_error = SD_OK; /* transmit error flag */
volatile uint8_t transfer_end = 0; /* transmit end flag */
sd_card_info_struct_type sd_card_info; /* sd card information */
sd_error_status_type command_error(void);
sd_error_status_type command_rsp1_error(uint8_t cmd);
sd_error_status_type command_rsp2_error(void);
sd_error_status_type command_rsp3_error(void);
sd_error_status_type command_rsp4_error(uint8_t cmd);
sd_error_status_type command_rsp5_error(uint8_t cmd);
sd_error_status_type command_rsp6_error(uint8_t cmd, uint16_t *p_rca);
sd_error_status_type command_rsp7_error(void);
sd_error_status_type sd_bus_wide_enable(confirm_state new_state);
sd_error_status_type mmc_switch(uint8_t set, uint8_t index, uint8_t value);
sd_error_status_type sd_switch(uint32_t mode, uint32_t group, uint8_t value, uint8_t *rsp);
sd_error_status_type check_card_programming(uint8_t *p_status);
sd_error_status_type speed_change(uint8_t speed);
sd_error_status_type scr_find(void);
uint8_t convert_from_bytes_to_power_of_two(uint16_t number_of_bytes);
/**
* @brief initializes the sd card and put it into standby state (ready for data
* transfer).
* @param none
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_init(void)
{
uint16_t clkdiv = 0;
sd_error_status_type status = SD_OK;
gpio_init_type gpio_init_struct = {0};
uint8_t retry = 0;
/* gpioc and gpiod periph clock enable */
crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE);
/* sdio periph clock enable */
crm_periph_clock_enable(CRM_SDIO1_PERIPH_CLOCK, TRUE);
/* configure pc.08, pc.09, pc.10, pc.11, pc.12 pin: d0, d1, d2, d3, clk pin */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_pins = GPIO_PINS_8 | GPIO_PINS_9 | GPIO_PINS_10 | GPIO_PINS_11 | GPIO_PINS_12;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOC, &gpio_init_struct);
/* configure pd.02 cmd line */
gpio_init_struct.gpio_pins = GPIO_PINS_2;
gpio_init(GPIOD, &gpio_init_struct);
gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE8, GPIO_MUX_12);
gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE9, GPIO_MUX_12);
gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE10, GPIO_MUX_12);
gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE11, GPIO_MUX_12);
gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE12, GPIO_MUX_12);
gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE2, GPIO_MUX_12);
retry = 3;
while(retry--){
/* reset sdio */
sdio_reset(SDIOx);//复位所有寄存器,清楚所有中断
/* power on */
status = sd_power_on();//SD卡上电过程
if(status == SD_OK)
break;
}
if(status == SD_OK)
{
/* sdio card initialize */
status = sd_card_init();//SD卡初始化
}
if(status == SD_OK)
{
/* get card information*/
status = sd_card_info_get(&sd_card_info);//将获取的SD卡CID与CSD寄存器内容存入结构体
}
if((SDIO_MULTIMEDIA_CARD == card_type) && (sd_card_info.sd_csd_reg.spec_version >= 4))
{
card_type = SDIO_HIGH_SPEED_MULTIMEDIA_CARD;
}
if(status == SD_OK)
{
/* select card */
status = sd_deselect_select((uint32_t)(sd_card_info.rca << 16));//通过发送CMD7选择卡片
}
if(status == SD_OK && ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == card_type) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == card_type) || \
(SDIO_SECURE_DIGITAL_IO_COMBO_CARD == card_type) || (SDIO_HIGH_CAPACITY_SD_CARD == card_type)))
{
status = scr_find();//获取SCR
}
if(status == SD_OK)
{
/* set normal speed */
status = speed_change(0);//切换到正常速度
}
if((status == SD_OK) || (card_type == SDIO_MULTIMEDIA_CARD))
{
if(sd_card_info.card_type == SDIO_STD_CAPACITY_SD_CARD_V1_1 || sd_card_info.card_type == SDIO_STD_CAPACITY_SD_CARD_V2_0)
{
/* set sdio_ck to 4mhz */
clkdiv = system_core_clock / 4000000;
if(clkdiv >= 2)
{
clkdiv -= 2;
}
}
else
{
/* set sdio_ck to 4mhz */
clkdiv = system_core_clock / 4000000;
if(clkdiv >= 2)
{
clkdiv -= 2;
}
}
/* set sdio clock divider */
sdio_clock_set(clkdiv);
/* set transfer mode */
status = sd_device_mode_set(SD_TRANSFER_DMA_MODE);
}
if(status == SD_OK)
{
/* Set data width */
status = sd_wide_bus_operation_config(SDIO_BUS_WIDTH_D4);
}
return status;
}
/**
* @brief set sdio clock devision factor.
* @param clkdiv: sdio_ck = ahbclk / (clkdiv+2)
* @retval none
*/
void sdio_clock_set(uint32_t clk_div)
{
/* config clock divide [7:0] */
SDIOx->clkctrl_bit.clkdiv_l = (clk_div & 0xFF);
/* config clock divide [9:8] */
SDIOx->clkctrl_bit.clkdiv_h = ((clk_div & 0x300) >> 8);
}
/**
* @brief enquires cards about their operating voltage and configures
* clock controls.
* @param none
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_power_on(void)
{
uint8_t retry = 0;
sd_error_status_type status = SD_OK;
uint32_t response = 0, count = 0, valid_voltage = 0, clk_psc;
uint32_t sd_type = SD_STD_CAPACITY;//默认SD卡(标准的)
/* sdio_ck is less than 400KHz in initialization stage, set to 200KHz */
clk_psc = (system_core_clock / 200000) - 2;
if(clk_psc > 0x3FF)
{
clk_psc = 0x3FF;
}
/* config sdio clock divide and edge phase */
sdio_clock_config(SDIOx, clk_psc, SDIO_CLOCK_EDGE_FALLING);//卡识别阶段SDIO_CK分频到200KHz,以及产生时钟的边沿
/* config sdio bus width */
sdio_bus_width_config(SDIOx, SDIO_BUS_WIDTH_D1);//为了通用性配置1位数据总线
/* disable flow control */
sdio_flow_control_enable(SDIOx, FALSE);//关闭硬件流控
/* disable clock bypass */
sdio_clock_bypass(SDIOx, FALSE);//时钟要分频
/* disable power saving mode */
sdio_power_saving_mode_enable(SDIOx, FALSE);//始终输出SDIO_CK
/* sdio power on */
sdio_power_set(SDIOx, SDIO_POWER_ON);//电源开关位,开启后卡时钟开启
/* enable to output sdio_ck */
sdio_clock_enable(SDIOx, TRUE);//时钟输出使能
for(retry = 0; retry < 5; retry++)
{
/* send cmd0, get in idle stage */
sdio_command_init_struct.argument = 0x0;//命令参数
sdio_command_init_struct.cmd_index = SD_CMD_GO_IDLE_STATE;//命令索引
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_NO;//响应类型
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;//等待请求
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);//将上述配置写到寄存器
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);//命令通道状态机使能
/* get command status */
status = command_error();//判断命令已发送(CMDCMPL)标志
}
/* send cmd8, check card interface feature */
sdio_command_init_struct.argument = SD_CHECK_PATTERN;//0x1AA VHS=0001b表示主机供电2.7V-3.6V 0xAA是固定检查位
sdio_command_init_struct.cmd_index = SDIO_SEND_IF_COND;//发送CMD8
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;//短响应
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;//无等待
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
/* waiting R7 */
status = command_rsp7_error();//若SD卡支持此电压,参数原本返回,否则不响应,这里没判断???
if(status == SD_OK)
{
/* set card type and sd type */
card_type = SDIO_STD_CAPACITY_SD_CARD_V2_0;//收到R7响应说明是V2.0的卡,否则的话就是V1.0或MMC卡
sd_type = SD_HIGH_CAPACITY;//假设为SDHC
}
/* send cmd55, check sd version */
sdio_command_init_struct.argument = 0x00;
sdio_command_init_struct.cmd_index = SD_CMD_APP_CMD;//发送ACMD41前先发送CMD55
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;//短响应
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
/* waiting R1 */
status = command_rsp1_error(SD_CMD_APP_CMD);//判断是否接收到R1响应???
/* check sd card or mmc card */
if(SD_OK == status)//若响应CMD55则V2.0的卡
{
/* send acmd41, check voltage operation range */
while((!valid_voltage) && (count < SD_MAX_VOLT_TRIAL))
{
delay_ms(10);
/* send cmd55 before acmd41 */
sdio_command_init_struct.argument = 0x00;
sdio_command_init_struct.cmd_index = SD_CMD_APP_CMD;//发送ACMD41前先发送CMD55
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
/* waiting R1 */
status = command_rsp1_error(SD_CMD_APP_CMD);
/* if any errors occured, return status */
if(status != SD_OK)
{
return status;
}
/* send acmd41 */
sdio_command_init_struct.argument = SD_VOLTAGE_WINDOW_SD | sd_type;//3.2-3.3V CCS
sdio_command_init_struct.cmd_index = SD_CMD_SD_APP_OP_COND;//发送ACMD41
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;//短响应
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
/* waiting R3 */
status = command_rsp3_error();//等待R3响应
/* if any errors occured, return status */
if(status != SD_OK)
{
return status;
}
/* get response1 */
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
/* check sd card power on stage is success or not */
valid_voltage = (((response >> 31) == 1) ? 1 : 0);//循环判断R3参数的bit31是否为1(BUSY),若为1证明卡片上电程序完成???
count++;
}//循环结束若count<0xFF说明卡片ready
if(count >= SD_MAX_VOLT_TRIAL)//若循环0xFF次仍不成功
{
status = SD_INVALID_VOLTRANGE;//电压范围无效
return status;
}
if(response &= SD_HIGH_CAPACITY)//判断R3的bit30(CCS)看是否为SDHC
{
card_type = SDIO_HIGH_CAPACITY_SD_CARD;
}
}
/* mmc card */
else//若不响应CMD55则可能是MMC卡
{
/* send cmd1 */
while((!valid_voltage) && (count < SD_MAX_VOLT_TRIAL))
{
delay_ms(10);
sdio_command_init_struct.argument = SD_VOLTAGE_WINDOW_MMC;
sdio_command_init_struct.cmd_index = SD_CMD_SEND_OP_COND;//发送CMD1
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;//短响应
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
/* waiting R3 */
status = command_rsp3_error();
if(status != SD_OK)
{
return status;
}
/* get response1 */
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
valid_voltage = (((response >> 31) == 1) ? 1 : 0);
count++;
}
if(count >= SD_MAX_VOLT_TRIAL)
{
status = SD_INVALID_VOLTRANGE;
return status;
}
card_type = SDIO_MULTIMEDIA_CARD;//MMC卡初始化完成
}
return(status);
}
/**
* @brief turns the sdio output signals off.
* @param none
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_power_off(void)
{
/* sdio power off */
sdio_power_set(SDIOx, SDIO_POWER_OFF);
return SD_OK;
}
/**
* @brief intialises all cards or single card as the case may be card(s) come
* into standby state.
* @param none
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_card_init(void)
{
sd_error_status_type status = SD_OK;
uint16_t rca_temp = 0x01;
/* check power status */
if(SDIO_POWER_OFF == sdio_power_status_get(SDIOx))
{
return SD_REQ_NOT_APPLICABLE;
}
/* check is secure_digital_io_card or not */
if(SDIO_SECURE_DIGITAL_IO_CARD != card_type)
{
/* send cmd2, get cid register */
sdio_command_init_struct.argument = 0x0;
sdio_command_init_struct.cmd_index = SD_CMD_ALL_SEND_CID;//CMD2获取CID寄存器数据
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_LONG;//长响应
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp2_error();
if(status != SD_OK)
{
return status;
}
cid_table[0] = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
cid_table[1] = sdio_response_get(SDIOx, SDIO_RSP2_INDEX);
cid_table[2] = sdio_response_get(SDIOx, SDIO_RSP3_INDEX);
cid_table[3] = sdio_response_get(SDIOx, SDIO_RSP4_INDEX);
}
/* check card type */
if((card_type == SDIO_STD_CAPACITY_SD_CARD_V1_1) || (card_type == SDIO_STD_CAPACITY_SD_CARD_V2_0) || \
(card_type == SDIO_SECURE_DIGITAL_IO_COMBO_CARD) || (card_type == SDIO_HIGH_CAPACITY_SD_CARD))
{
/* send cmd3, get rca */
sdio_command_init_struct.argument = 0x00;
sdio_command_init_struct.cmd_index = SD_CMD_SET_REL_ADDR;//CMD3设置卡相对地址
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;//短响应
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp6_error(SD_CMD_SET_REL_ADDR, &rca_temp);//SD卡返回相对地址
if(status != SD_OK)
{
return status;
}
}
/* mmc card */
if(card_type == SDIO_MULTIMEDIA_CARD)
{
/* send cmd3 */
sdio_command_init_struct.argument = (uint32_t)(rca_temp << 16);
sdio_command_init_struct.cmd_index = SD_CMD_SET_REL_ADDR;//MMC卡通过CMD3设置相对地址
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp2_error();
if(status != SD_OK)
{
return status;
}
}
/* check is secure_digital_io_card or not */
if(card_type != SDIO_SECURE_DIGITAL_IO_CARD)
{
rca = rca_temp;
sdio_command_init_struct.argument = (uint32_t)(rca << 16);
sdio_command_init_struct.cmd_index = SD_CMD_SEND_CSD;//CMD9获取CSD寄存器内容
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_LONG;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp2_error();
if(status != SD_OK)
{
return status;
}
csd_table[0] = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
csd_table[1] = sdio_response_get(SDIOx, SDIO_RSP2_INDEX);
csd_table[2] = sdio_response_get(SDIOx, SDIO_RSP3_INDEX);
csd_table[3] = sdio_response_get(SDIOx, SDIO_RSP4_INDEX);
}
return SD_OK;
}
/**
* @brief returns information about specific card.
* @param card_info: pointer to a sd_card_info_struct_type structure that contains all sd card
* information.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_card_info_get(sd_card_info_struct_type *card_info)
{
sd_error_status_type status = SD_OK;
uint8_t tmp = 0;
card_info->card_type = (uint8_t)card_type;
card_info->rca = (uint16_t)rca;
/* byte 0 */
tmp = (uint8_t)((csd_table[0] & 0xFF000000) >> 24);
card_info->sd_csd_reg.csd_struct = (tmp & 0xC0) >> 6;
card_info->sd_csd_reg.spec_version = (tmp & 0x3C) >> 2;
card_info->sd_csd_reg.reserved1 = tmp & 0x03;
/* byte 1 */
tmp = (uint8_t)((csd_table[0] & 0x00FF0000) >> 16);
card_info->sd_csd_reg.taac = tmp;
/* byte 2 */
tmp = (uint8_t)((csd_table[0] & 0x0000FF00) >> 8);
card_info->sd_csd_reg.nsac = tmp;
/* byte 3 */
tmp = (uint8_t)(csd_table[0] & 0x000000FF);
card_info->sd_csd_reg.max_bus_clk_freq = tmp;
/* byte 4 */
tmp = (uint8_t)((csd_table[1] & 0xFF000000) >> 24);
card_info->sd_csd_reg.card_cmd_classes = tmp << 4;
/* byte 5 */
tmp = (uint8_t)((csd_table[1] & 0x00FF0000) >> 16);
card_info->sd_csd_reg.card_cmd_classes |= (tmp & 0xF0) >> 4;
card_info->sd_csd_reg.max_read_blk_length = tmp & 0x0F;
/* byte 6 */
tmp = (uint8_t)((csd_table[1] & 0x0000FF00) >> 8);
card_info->sd_csd_reg.part_blk_read = (tmp & 0x80) >> 7;
card_info->sd_csd_reg.write_blk_misalign = (tmp & 0x40) >> 6;
card_info->sd_csd_reg.read_blk_misalign = (tmp & 0x20) >> 5;
card_info->sd_csd_reg.dsr_implemented = (tmp & 0x10) >> 4;
card_info->sd_csd_reg.reserved2 = 0; /* reserved */
if((card_type == SDIO_STD_CAPACITY_SD_CARD_V1_1) || (card_type == SDIO_STD_CAPACITY_SD_CARD_V2_0) || (card_type == SDIO_MULTIMEDIA_CARD))
{
card_info->sd_csd_reg.device_size = (tmp & 0x03) << 10;
/* byte 7 */
tmp = (uint8_t)(csd_table[1] & 0x000000FF);
card_info->sd_csd_reg.device_size |= (tmp) << 2;
/* byte 8 */
tmp = (uint8_t)((csd_table[2] & 0xFF000000) >> 24);
card_info->sd_csd_reg.device_size |= (tmp & 0xC0) >> 6;
card_info->sd_csd_reg.max_read_current_vdd_min = (tmp & 0x38) >> 3;
card_info->sd_csd_reg.max_read_current_vdd_max = (tmp & 0x07);
/* byte 9 */
tmp = (uint8_t)((csd_table[2] & 0x00FF0000) >> 16);
card_info->sd_csd_reg.max_write_current_vdd_min = (tmp & 0xE0) >> 5;
card_info->sd_csd_reg.max_write_current_vdd_max = (tmp & 0x1C) >> 2;
card_info->sd_csd_reg.device_size_mult = (tmp & 0x03) << 1;
/* byte 10 */
tmp = (uint8_t)((csd_table[2] & 0x0000FF00) >> 8);
card_info->sd_csd_reg.device_size_mult |= (tmp & 0x80) >> 7;
card_info->card_capacity = (card_info->sd_csd_reg.device_size + 1) ;
card_info->card_capacity *= (1 << (card_info->sd_csd_reg.device_size_mult + 2));
card_info->card_blk_size = 1 << (card_info->sd_csd_reg.max_read_blk_length);
card_info->card_capacity *= card_info->card_blk_size;
}
else if(card_type == SDIO_HIGH_CAPACITY_SD_CARD)
{
/* byte 7 */
tmp = (uint8_t)(csd_table[1] & 0x000000FF);
card_info->sd_csd_reg.device_size = (tmp & 0x3F) << 16;
/* byte 8 */
tmp = (uint8_t)((csd_table[2] & 0xFF000000) >> 24);
card_info->sd_csd_reg.device_size |= (tmp << 8);
/* byte 9 */
tmp = (uint8_t)((csd_table[2] & 0x00FF0000) >> 16);
card_info->sd_csd_reg.device_size |= (tmp);
/* byte 10 */
tmp = (uint8_t)((csd_table[2] & 0x0000FF00) >> 8);
card_info->card_capacity = (uint64_t)(card_info->sd_csd_reg.device_size + 1) * 512 * 1024;
card_info->card_blk_size = 512;
}
card_info->sd_csd_reg.erase_group_size = (tmp & 0x40) >> 6;
card_info->sd_csd_reg.erase_group_size_mult = (tmp & 0x3F) << 1;
/* byte 11 */
tmp = (uint8_t)(csd_table[2] & 0x000000FF);
card_info->sd_csd_reg.erase_group_size_mult |= (tmp & 0x80) >> 7;
card_info->sd_csd_reg.write_protect_group_size = (tmp & 0x7F);
/* byte 12 */
tmp = (uint8_t)((csd_table[3] & 0xFF000000) >> 24);
card_info->sd_csd_reg.write_protect_group_enable = (tmp & 0x80) >> 7;
card_info->sd_csd_reg.manufacturer_default_ecc = (tmp & 0x60) >> 5;
card_info->sd_csd_reg.write_speed_factor = (tmp & 0x1C) >> 2;
card_info->sd_csd_reg.max_write_blk_length = (tmp & 0x03) << 2;
/* byte 13 */
tmp = (uint8_t)((csd_table[3] & 0x00FF0000) >> 16);
card_info->sd_csd_reg.max_write_blk_length |= (tmp & 0xC0) >> 6;
card_info->sd_csd_reg.part_blk_write = (tmp & 0x20) >> 5;
card_info->sd_csd_reg.reserved3 = 0;
card_info->sd_csd_reg.content_protect_app = (tmp & 0x01);
/* byte 14 */
tmp = (uint8_t)((csd_table[3] & 0x0000FF00) >> 8);
card_info->sd_csd_reg.file_format_group = (tmp & 0x80) >> 7;
card_info->sd_csd_reg.copy_flag = (tmp & 0x40) >> 6;
card_info->sd_csd_reg.permanent_write_protect = (tmp & 0x20) >> 5;
card_info->sd_csd_reg.temp_write_protect = (tmp & 0x10) >> 4;
card_info->sd_csd_reg.file_formart = (tmp & 0x0C) >> 2;
card_info->sd_csd_reg.ecc_code = (tmp & 0x03);
/* byte 15 */
tmp = (uint8_t)(csd_table[3] & 0x000000FF);
card_info->sd_csd_reg.csd_crc = (tmp & 0xFE) >> 1;
card_info->sd_csd_reg.reserved4 = 1;
/* byte 0 */
tmp = (uint8_t)((cid_table[0] & 0xFF000000) >> 24);
card_info->sd_cid_reg.manufacturer_id = tmp;
/* byte 1 */
tmp = (uint8_t)((cid_table[0] & 0x00FF0000) >> 16);
card_info->sd_cid_reg.oem_app_id = tmp << 8;
/* byte 2 */
tmp = (uint8_t)((cid_table[0] & 0x000000FF00) >> 8);
card_info->sd_cid_reg.oem_app_id |= tmp;
/* byte 3 */
tmp = (uint8_t)(cid_table[0] & 0x000000FF);
card_info->sd_cid_reg.product_name1 = tmp << 24;
/* byte 4 */
tmp = (uint8_t)((cid_table[1] & 0xFF000000) >> 24);
card_info->sd_cid_reg.product_name1 |= tmp << 16;
/* byte 5 */
tmp = (uint8_t)((cid_table[1] & 0x00FF0000) >> 16);
card_info->sd_cid_reg.product_name1 |= tmp << 8;
/* byte 6 */
tmp = (uint8_t)((cid_table[1] & 0x0000FF00) >> 8);
card_info->sd_cid_reg.product_name1 |= tmp;
/* byte 7 */
tmp = (uint8_t)(cid_table[1] & 0x000000FF);
card_info->sd_cid_reg.product_name2 = tmp;
/* byte 8 */
tmp = (uint8_t)((cid_table[2] & 0xFF000000) >> 24);
card_info->sd_cid_reg.product_reversion = tmp;
/* byte 9 */
tmp = (uint8_t)((cid_table[2] & 0x00FF0000) >> 16);
card_info->sd_cid_reg.product_sn = tmp << 24;
/* byte 10 */
tmp = (uint8_t)((cid_table[2] & 0x0000FF00) >> 8);
card_info->sd_cid_reg.product_sn |= tmp << 16;
/* byte 11 */
tmp = (uint8_t)(cid_table[2] & 0x000000FF);
card_info->sd_cid_reg.product_sn |= tmp << 8;
/* byte 12 */
tmp = (uint8_t)((cid_table[3] & 0xFF000000) >> 24);
card_info->sd_cid_reg.product_sn |= tmp;
/* byte 13 */
tmp = (uint8_t)((cid_table[3] & 0x00FF0000) >> 16);
card_info->sd_cid_reg.reserved1 |= (tmp & 0xF0) >> 4;
card_info->sd_cid_reg.manufact_date = (tmp & 0x0F) << 8;
/* byte 14 */
tmp = (uint8_t)((cid_table[3] & 0x0000FF00) >> 8);
card_info->sd_cid_reg.manufact_date |= tmp;
/* byte 15 */
tmp = (uint8_t)(cid_table[3] & 0x000000FF);
card_info->sd_cid_reg.cid_crc = (tmp & 0xFE) >> 1;
card_info->sd_cid_reg.reserved2 = 1;
return(status);
}
/**
* @brief enable wide bus opeartion for the requeseted card if supported by
* card.
* @param mode: specifies the sd card wide bus mode.
* this parameter can be one of the following values:
* @arg SDIO_BUS_WIDTH_D8: 8-bit data transfer (only for mmc)
* @arg SDIO_BUS_WIDTH_D4: 4-bit data transfer
* @arg SDIO_BUS_WIDTH_D1: 1-bit data transfer
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_wide_bus_operation_config(sdio_bus_width_type mode)
{
sd_error_status_type status = SD_OK;
if((card_type == SDIO_MULTIMEDIA_CARD) || (card_type == SDIO_HIGH_SPEED_MULTIMEDIA_CARD))
{
status = mmc_switch(EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, (uint8_t)mode);
}
else if((card_type == SDIO_STD_CAPACITY_SD_CARD_V1_1) || (card_type == SDIO_STD_CAPACITY_SD_CARD_V2_0) || \
(card_type == SDIO_HIGH_CAPACITY_SD_CARD))
{
if(mode >= 2)
{
/* not support D8 mode */
return SD_UNSUPPORTED_FEATURE;
}
if(SDIO_BUS_WIDTH_D4 == mode)
{
status = sd_bus_wide_enable(TRUE);
}
else
{
status = sd_bus_wide_enable(FALSE);
}
}
if(status == SD_OK)
{
sdio_bus_width_config(SDIOx, mode);
}
return status;
}
/**
* @brief set sdio work mode.
* @param mode
* parameter as following values:
* - SD_TRANSFER_POLLING_MODE: dma mode.
* - SD_TRANSFER_POLLING_MODE: polling mode.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_device_mode_set(uint32_t mode)
{
sd_error_status_type status = SD_OK;
if((mode == SD_TRANSFER_DMA_MODE) || (mode == SD_TRANSFER_POLLING_MODE))
{
device_mode = (sd_data_transfer_mode_type)mode;
}
else
{
status = SD_INVALID_PARAMETER;
}
return status;
}
/**
* @brief selects od deselects the corresponding card.
* @param addr: address of the card to be selected.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_deselect_select(uint32_t addr)
{
/* send cmd7, select card */
sdio_command_init_struct.argument = addr;
sdio_command_init_struct.cmd_index = SD_CMD_SEL_DESEL_CARD;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_INT;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
return command_rsp1_error(SD_CMD_SEL_DESEL_CARD);
}
/**
* @brief read data from or write data to sd card.
* @param sdio_cmd_init_struct: pointer to sdio_command_struct_type structure.
* @param sdio_data_init_struct: pointer to sdio_data_struct_type structure.
* @param buf: pointer to data buffer read or write.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sdio_command_data_send(sdio_command_struct_type *sdio_cmd_init_t, \
sdio_data_struct_type* sdio_data_init_t, uint32_t *buf)
{
sd_error_status_type status = SD_OK;
uint32_t count = 0;
uint32_t timeout = SDIO_DATATIMEOUT;
uint32_t length = 0;
if(buf == NULL)
{
return SD_INVALID_PARAMETER;
}
/* clear dtctrl register */
SDIOx->dtcnt = 0x0;
/* sdio command config */
sdio_data_config(SDIOx, sdio_data_init_t);
/* enable dcsm */
sdio_data_state_machine_enable(SDIOx, TRUE);
length = sdio_data_init_t->data_length;
if(device_mode == SD_TRANSFER_DMA_MODE)
{
if(sdio_data_init_t->transfer_direction == SDIO_DATA_TRANSFER_TO_CONTROLLER)
{
sd_dma_config(buf, length, DMA_DIR_PERIPHERAL_TO_MEMORY);
SDIOx->inten |= SDIO_INTR_STS_READ_MASK;
transfer_error = SD_OK;
transfer_end = 0;
sdio_dma_enable(SDIOx, TRUE);//使能DMA
}
}
/* sdio command config */
sdio_command_config(SDIOx, sdio_cmd_init_t);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(sdio_cmd_init_t->cmd_index);
if(status != SD_OK)
{
return status;
}
/* polling mode */
if(device_mode == SD_TRANSFER_POLLING_MODE)
{
if(SDIO_DATA_TRANSFER_TO_CONTROLLER == sdio_data_init_t->transfer_direction)
{
while(!(SDIOx->sts & (SDIO_INTR_STS_READ_MASK)))
{
if(sdio_flag_get(SDIOx, SDIO_RXBUFH_FLAG) != RESET)
{
for(count = 0; count < 8; count++, buf++)
{
*buf = sdio_data_read(SDIOx);
}
timeout = 0x7FFFFF;
}
else
{
if(0 == timeout)
{
sd_init();
return SD_DATA_TIMEOUT;
}
timeout--;
}
}
while(sdio_flag_get(SDIOx, SDIO_RXBUF_FLAG) != RESET)
{
*buf = sdio_data_read(SDIOx);
buf++;
}
}
else
{
while(!(SDIOx->sts & SDIO_INTR_STS_WRITE_MASK))
{
if(sdio_flag_get(SDIOx, SDIO_TXBUFH_FLAG) != RESET)
{
for(count = 0; count < 8 && length > 0; count++, buf++, length -= 4)
{
sdio_data_write(SDIOx, *buf);
}
timeout = 0x3FFFFFFF;
}
else
{
if(timeout == 0)
{
sd_init();
return SD_DATA_TIMEOUT;
}
timeout--;
}
}
}
/* data timeout */
if(sdio_flag_get(SDIOx, SDIO_DTTIMEOUT_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_DTTIMEOUT_FLAG);
return SD_DATA_TIMEOUT;
}
/* crc error */
else if(sdio_flag_get(SDIOx, SDIO_DTFAIL_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_DTFAIL_FLAG);
return SD_DATA_FAIL;
}
/* over run error */
else if(sdio_flag_get(SDIOx, SDIO_RXERRO_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_RXERRO_FLAG);
return SD_RX_OVERRUN;
}
/* under run error */
else if(sdio_flag_get(SDIOx, SDIO_TXERRU_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_TXERRU_FLAG);
return SD_TX_UNDERRUN;
}
/* start bit error */
else if(sdio_flag_get(SDIOx, SDIO_SBITERR_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_SBITERR_FLAG);
return SD_START_BIT_ERR;
}
/* data transfer complete */
if((stop_flag == 1) && (sdio_flag_get(SDIOx, SDIO_DTCMPL_FLAG) != RESET))//???
{
/* send cmd12, stop transmission */
sdio_cmd_init_t->argument = 0;
sdio_cmd_init_t->cmd_index = SD_CMD_STOP_TRANSMISSION;
sdio_cmd_init_t->rsp_type = SDIO_RESPONSE_SHORT;
sdio_cmd_init_t->wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, sdio_cmd_init_t);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_STOP_TRANSMISSION);
if(status != SD_OK)
{
return status;
}
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
}
else if(device_mode == SD_TRANSFER_DMA_MODE)
{
if(sdio_data_init_t->transfer_direction == SDIO_DATA_TRANSFER_TO_CARD)
{
sd_dma_config(buf, length, DMA_DIR_MEMORY_TO_PERIPHERAL);
SDIOx->inten |= SDIO_INTR_STS_WRITE_MASK;
transfer_error = SD_OK;
transfer_end = 0;
sdio_dma_enable(SDIOx, TRUE);
}
while(!(SDIOx->sts & SDIOx->inten) && timeout)
{
timeout--;
if(transfer_end)
{
break;
}
}
if(timeout == 0)
{
sd_init();
return SD_DATA_TIMEOUT;
}
if(transfer_error != SD_OK)
{
status = transfer_error;
}
}
return status;
}
/**
* @brief erase continuous data block
* @param addr: starting address
* @param nblks: amount of erasing data block
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_blocks_erase(long long addr, uint32_t nblks)
{
sd_error_status_type status = SD_OK;
uint32_t start_addr = 0, end_addr = 0, response = 0;
uint8_t card_state;
/* high capacity sd card */
if(card_type == SDIO_HIGH_CAPACITY_SD_CARD)
{
start_addr = addr >> 9;
end_addr = start_addr + nblks - 1;
}
else
{
start_addr = addr;
end_addr = start_addr + (nblks - 1) * 512;
}
/* clear dcsm configuration */
sdio_data_init_struct.block_size = SDIO_DATA_BLOCK_SIZE_1B;
sdio_data_init_struct.data_length = 0 ;
sdio_data_init_struct.timeout = SD_DATATIMEOUT ;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CARD;
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;
sdio_data_config(SDIOx, &sdio_data_init_struct);
sdio_data_state_machine_enable(SDIOx, FALSE);
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
/* check card locked */
if(response & SD_CARD_LOCKED)
{
return SD_LOCK_UNLOCK_ERROR;
}
if(card_type == SDIO_MULTIMEDIA_CARD || card_type == SDIO_HIGH_SPEED_MULTIMEDIA_CARD)
{
/* send cmd35, set erase group start */
sdio_command_init_struct.argument = start_addr;
sdio_command_init_struct.cmd_index = SD_CMD_ERASE_GRP_START;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_ERASE_GRP_START);
if(status != SD_OK)
{
return status;
}
/* send cmd36, set erase group end */
sdio_command_init_struct.argument = end_addr;
sdio_command_init_struct.cmd_index = SD_CMD_ERASE_GRP_END;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_ERASE_GRP_END);
if(status != SD_OK)
{
return status;
}
/* send cmd38, start erase gourp */
sdio_command_init_struct.argument = 0;
sdio_command_init_struct.cmd_index = SD_CMD_ERASE;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_ERASE);
if(status != SD_OK)
{
return status;
}
}
else
{
/* send cmd32, set erase block start */
sdio_command_init_struct.argument = start_addr;
sdio_command_init_struct.cmd_index = SD_CMD_SD_ERASE_GRP_START;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_SD_ERASE_GRP_START);
if(status != SD_OK)
{
return status;
}
/* send cmd33, set erase block end */
sdio_command_init_struct.argument = end_addr;
sdio_command_init_struct.cmd_index = SD_CMD_SD_ERASE_GRP_END;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
if(status != SD_OK)
{
return status;
}
/* send cmd38, start erase block */
sdio_command_init_struct.argument = 0;
sdio_command_init_struct.cmd_index = SD_CMD_ERASE;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_ERASE);
if(status != SD_OK)
{
return status;
}
}
status = check_card_programming(&card_state);
while((status == SD_OK) && ((card_state == SD_CARD_PROGRAMMING) || \
(card_state == SD_CARD_RECEIVING)))
{
status = check_card_programming(&card_state);
}
return status;
}
/**
* @brief allows to read one block from a specified address in a card. the data
* transfer can be managed by dma mode or polling mode.
* @param buf: pointer to the buffer that will contain the received data
* @param addr: address from where data are to be read.
* @param blk_size: the sd card data block size. the block size should be 512.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_block_read(uint8_t *buf, long long addr, uint16_t blk_size)
{
sd_error_status_type status = SD_OK;
uint32_t response = 0;
uint8_t power;
if(NULL == buf)
{
return SD_INVALID_PARAMETER;
}
SDIOx->dtctrl = 0x0;
if(card_type == SDIO_HIGH_CAPACITY_SD_CARD)
{
blk_size = 512;
addr >>= 9;
}
/* clear dcsm configuration */
sdio_data_init_struct.block_size = SDIO_DATA_BLOCK_SIZE_1B;
sdio_data_init_struct.data_length = 0;
sdio_data_init_struct.timeout = SD_DATATIMEOUT;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CARD;
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;
sdio_data_config(SDIOx, &sdio_data_init_struct);
sdio_data_state_machine_enable(SDIOx, FALSE);
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);//???R1
/* check card locked */
if(response & SD_CARD_LOCKED)
{
return SD_LOCK_UNLOCK_ERROR;
}
if((blk_size > 0) && (blk_size <= 2048) && ((blk_size & (blk_size - 1)) == 0))//blk_size在1-2048之间,且必须是2的次方
{
power = convert_from_bytes_to_power_of_two(blk_size);
/* send cmd16, set block size */
sdio_command_init_struct.argument = blk_size;
sdio_command_init_struct.cmd_index = SD_CMD_SET_BLOCKLEN;//CMD16设置块大小
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_SET_BLOCKLEN);
if(status != SD_OK)
{
return status;
}
}
else
{
return SD_INVALID_PARAMETER;
}
sdio_data_init_struct.block_size = (sdio_block_size_type)(power);
sdio_data_init_struct.data_length = blk_size ;
sdio_data_init_struct.timeout = SD_DATATIMEOUT ;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CONTROLLER;
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;
sdio_command_init_struct.argument = addr;
sdio_command_init_struct.cmd_index = SD_CMD_READ_SINGLE_BLOCK;//CMD17读单个数据块
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
stop_flag = 0;
return sdio_command_data_send(&sdio_command_init_struct, &sdio_data_init_struct, (uint32_t *)buf);//读数据
}
/**
* @brief allows to read blocks from a specified address in a card. the data
* transfer can be managed by dma mode or polling mode.
* @param buf: pointer to the buffer that will contain the received data.
* @param addr: address from where data are to be read.
* @param blk_size: the sd card data block size. the block size should be 512.
* @param nblks: number of blocks to be read.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_mult_blocks_read(uint8_t *buf, long long addr, uint16_t blk_size, uint32_t nblks)
{
sd_error_status_type status = SD_OK;
uint32_t response = 0;
uint8_t power;
SDIOx->dtctrl = 0x0;
if(card_type == SDIO_HIGH_CAPACITY_SD_CARD)
{
blk_size = 512;
addr >>= 9;
}
/* clear dcsm configuration */
sdio_data_init_struct.block_size = (sdio_block_size_type)0;
sdio_data_init_struct.data_length = 0;
sdio_data_init_struct.timeout = SD_DATATIMEOUT;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CARD;
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;
sdio_data_config(SDIOx, &sdio_data_init_struct);
sdio_data_state_machine_enable(SDIOx, FALSE);
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
/* check card locked */
if(response & SD_CARD_LOCKED)
{
return SD_LOCK_UNLOCK_ERROR;
}
if((blk_size > 0) && (blk_size <= 2048) && ((blk_size & (blk_size - 1)) == 0))
{
power = convert_from_bytes_to_power_of_two(blk_size);
/* send cmd16, set block size */
sdio_command_init_struct.argument = blk_size;
sdio_command_init_struct.cmd_index = SD_CMD_SET_BLOCKLEN;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_SET_BLOCKLEN);
if(status != SD_OK)
{
return status;
}
}
else
{
return SD_INVALID_PARAMETER;
}
/* check max receive length */
if(nblks * blk_size > SD_MAX_DATA_LENGTH)
{
return SD_INVALID_PARAMETER;
}
sdio_data_init_struct.block_size = (sdio_block_size_type)(power);
sdio_data_init_struct.data_length = nblks * blk_size ;
sdio_data_init_struct.timeout = SD_DATATIMEOUT ;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CONTROLLER;
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;
/* send cmd18, read block data */
sdio_command_init_struct.argument = addr;
sdio_command_init_struct.cmd_index = SD_CMD_READ_MULT_BLOCK;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
stop_flag = 1;
return sdio_command_data_send(&sdio_command_init_struct, &sdio_data_init_struct, (uint32_t *)buf);
}
/**
* @brief allows to write one block starting from a specified address in a card.
* the data transfer can be managed by dma mode or polling mode.
* @param buf: pointer to the buffer that contain the data to be transferred.
* @param addr: address from where data are to be read.
* @param blk_size: the sd card data block size. the block size should be 512.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_block_write(const uint8_t *buf, long long addr, uint16_t blk_size)
{
sd_error_status_type status = SD_OK;
uint8_t power = 0, card_state = 0;
uint32_t timeout = 0, card_status = 0, response = 0;
if(buf == NULL)
{
return SD_INVALID_PARAMETER;
}
SDIOx->dtctrl = 0x0;
/* clear dcsm configuration */
sdio_data_init_struct.block_size = (sdio_block_size_type)0;
sdio_data_init_struct.data_length = 0;
sdio_data_init_struct.timeout = SD_DATATIMEOUT;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CARD;
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;
sdio_data_config(SDIOx, &sdio_data_init_struct);
sdio_data_state_machine_enable(SDIOx, FALSE);
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
/* check card locked */
if(response & SD_CARD_LOCKED)
{
return SD_LOCK_UNLOCK_ERROR;
}
if(card_type == SDIO_HIGH_CAPACITY_SD_CARD)
{
blk_size = 512;
addr >>= 9;
}
if((blk_size > 0) && (blk_size <= 2048) && ((blk_size & (blk_size - 1)) == 0))
{
power = convert_from_bytes_to_power_of_two(blk_size);
/* send cmd16, set block size */
sdio_command_init_struct.argument = blk_size;
sdio_command_init_struct.cmd_index = SD_CMD_SET_BLOCKLEN;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_SET_BLOCKLEN);
if(status != SD_OK)
{
return status;
}
}
else
{
return SD_INVALID_PARAMETER;
}
timeout = SD_DATATIMEOUT;
do
{
timeout--;
status = sd_status_send(&card_status);//发送CMD13查询卡状态
}
/* check ready_for_data flag */
while(((card_status & 0x00000100) == 0) && (timeout > 0));//READY_FOR_DATA
if(timeout == 0)
{
return SD_ERROR;
}
/* send cmd24, write single block */
sdio_command_init_struct.argument = addr;
sdio_command_init_struct.cmd_index = SD_CMD_WRITE_SINGLE_BLOCK;//CMD24单个数据块写入
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
sdio_data_init_struct.block_size = (sdio_block_size_type)(power);
sdio_data_init_struct.data_length = blk_size;
sdio_data_init_struct.timeout = SD_DATATIMEOUT;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CARD;
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;
stop_flag = 0;
/* single block, stop command is unnecessary */
status = sdio_command_data_send(&sdio_command_init_struct, &sdio_data_init_struct, (uint32_t *)buf);
if(status != SD_OK)
{
return status;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
status = check_card_programming(&card_state);
while((status == SD_OK) && ((card_state == SD_CARD_PROGRAMMING) || (card_state == SD_CARD_RECEIVING)))//SD卡正在编程或接收状态
{
status = check_card_programming(&card_state);
}
return status;
}
/**
* @brief allows to write blocks starting from a specified address in a card.
* the data transfer can be managed by dma mode only.
* @param buf: pointer to the buffer that contain the data to be transferred.
* @param addr: address from where data are to be read.
* @param blk_size: the sd card data block size. the block size should be 512.
* @param nblks: number of blocks to be written.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_mult_blocks_write(const uint8_t *buf, long long addr, uint16_t blk_size, uint32_t nblks)
{
sd_error_status_type status = SD_OK;
uint8_t power = 0, card_state = 0;
uint32_t timeout = 0, card_status = 0, response = 0;;
if(buf == NULL)
{
return SD_INVALID_PARAMETER;
}
SDIOx->dtctrl = 0x0;
/* clear dcsm configuration */
sdio_data_init_struct.block_size = (sdio_block_size_type)0;
sdio_data_init_struct.data_length = 0;
sdio_data_init_struct.timeout = SD_DATATIMEOUT;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CARD;
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;
sdio_data_config(SDIOx, &sdio_data_init_struct);
sdio_data_state_machine_enable(SDIOx, FALSE);
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
/* check card locked */
if(response & SD_CARD_LOCKED)
{
return SD_LOCK_UNLOCK_ERROR;
}
if(card_type == SDIO_HIGH_CAPACITY_SD_CARD)
{
blk_size = 512;
addr >>= 9;
}
if((blk_size > 0) && (blk_size <= 2048) && ((blk_size & (blk_size - 1)) == 0))
{
power = convert_from_bytes_to_power_of_two(blk_size);
/* send cmd16, set block size */
sdio_command_init_struct.argument = blk_size;
sdio_command_init_struct.cmd_index = SD_CMD_SET_BLOCKLEN;//发送CMD16设置块大小
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_SET_BLOCKLEN);
if(status != SD_OK)
{
return status;
}
}
else
{
return SD_INVALID_PARAMETER;
}
if((nblks * blk_size) > SD_MAX_DATA_LENGTH)
{
return SD_INVALID_PARAMETER;
}
timeout = SD_DATATIMEOUT;
do
{
timeout--;
status = sd_status_send(&card_status);
}
/* check ready_for_data flag */
while(((card_status & 0x00000100) == 0) && (timeout > 0));
if(timeout == 0)
{
return SD_ERROR;
}
if((card_type == SDIO_STD_CAPACITY_SD_CARD_V1_1) || (card_type == SDIO_STD_CAPACITY_SD_CARD_V2_0) || \
(card_type == SDIO_HIGH_CAPACITY_SD_CARD))
{
/* send cmd55 */
sdio_command_init_struct.argument = (uint32_t)rca << 16;
sdio_command_init_struct.cmd_index = SD_CMD_APP_CMD;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_APP_CMD);
if(status != SD_OK)
{
return status;
}
/* send cmd23, set block count */
sdio_command_init_struct.argument = blk_size;
sdio_command_init_struct.cmd_index = SD_CMD_SET_BLOCK_COUNT;//发送ACMD23预擦除数据块
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_SET_BLOCK_COUNT);
if(status != SD_OK)
{
return status;
}
}
/* send cmd25, write mult blocks */
sdio_command_init_struct.argument = addr;
sdio_command_init_struct.cmd_index = SD_CMD_WRITE_MULT_BLOCK;//发送CMD25开始写数据块
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
sdio_data_init_struct.block_size = (sdio_block_size_type)(power);
sdio_data_init_struct.data_length = nblks * blk_size ;
sdio_data_init_struct.timeout = SD_DATATIMEOUT ;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CARD;
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;
stop_flag = 1;
/* cmd12 is needed */
status = sdio_command_data_send(&sdio_command_init_struct, &sdio_data_init_struct, (uint32_t *)buf);
if(status != SD_OK)
{
return status;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
status = check_card_programming(&card_state);
while((status == SD_OK) && ((card_state == SD_CARD_PROGRAMMING) || (card_state == SD_CARD_RECEIVING)))
{
status = check_card_programming(&card_state);
}
return status;
}
/**
* @brief read mmc card data stream.
* @param buf: buffer of saving data from mmc card
* @param addr: start address of data from mmc card
* @param len: data size
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type mmc_stream_read(uint8_t *buf, long long addr, uint32_t len)
{
uint32_t response = 0;
SDIOx->dtctrl = 0x0;
/* clear dcsm configuration */
sdio_data_init_struct.block_size = SDIO_DATA_BLOCK_SIZE_1B;
sdio_data_init_struct.data_length = 0 ;
sdio_data_init_struct.timeout = SD_DATATIMEOUT ;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CARD;
sdio_data_init_struct.transfer_mode = SDIO_DATA_STREAM_TRANSFER;
sdio_data_config(SDIOx, &sdio_data_init_struct);
sdio_data_state_machine_enable(SDIOx, FALSE);
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
/* check card locked */
if(response & SD_CARD_LOCKED)
{
return SD_LOCK_UNLOCK_ERROR;
}
/* send cmd11, read data */
sdio_command_init_struct.argument = addr;
sdio_command_init_struct.cmd_index = SD_CMD_READ_DAT_UNTIL_STOP;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
sdio_data_init_struct.block_size = (sdio_block_size_type)(5 << 4);
sdio_data_init_struct.data_length = len;
sdio_data_init_struct.timeout = SD_DATATIMEOUT ;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CONTROLLER;
sdio_data_init_struct.transfer_mode = SDIO_DATA_STREAM_TRANSFER;
stop_flag = 1;
/* cmd12 is needed */
return sdio_command_data_send(&sdio_command_init_struct, &sdio_data_init_struct, (uint32_t *)buf);
}
/**
* @brief write mmc card data stream.
* @param buf: data that writing to mmc card
* @param addr: start address mmc card
* @param len: data size
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type mmc_stream_write(uint8_t *buf, long long addr, uint32_t len)
{
sd_error_status_type status = SD_OK;
uint32_t response = 0;
uint8_t card_state = 0;
if(buf == NULL)
{
return SD_INVALID_PARAMETER;
}
SDIOx->dtctrl = 0x0;
/* clear dcsm configuration */
sdio_data_init_struct.block_size = SDIO_DATA_BLOCK_SIZE_1B;
sdio_data_init_struct.data_length = 0;
sdio_data_init_struct.timeout = SD_DATATIMEOUT;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CARD;
sdio_data_init_struct.transfer_mode = SDIO_DATA_STREAM_TRANSFER;
sdio_data_config(SDIOx, &sdio_data_init_struct);
sdio_data_state_machine_enable(SDIOx, FALSE);
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
/* check card locked */
if(response & SD_CARD_LOCKED)
{
return SD_LOCK_UNLOCK_ERROR;
}
/* send cmd20, write data */
sdio_command_init_struct.argument = addr;
sdio_command_init_struct.cmd_index = SD_CMD_WRITE_DAT_UNTIL_STOP;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
sdio_data_init_struct.block_size = (sdio_block_size_type)(15 << 4);
sdio_data_init_struct.data_length = len;
sdio_data_init_struct.timeout = SD_DATATIMEOUT ;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CARD;
sdio_data_init_struct.transfer_mode = SDIO_DATA_STREAM_TRANSFER;
stop_flag = 1;
/* cmd12 is needed */
status = sdio_command_data_send(&sdio_command_init_struct, &sdio_data_init_struct, (uint32_t *)buf);
if(status != SD_OK)
{
return status;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
status = check_card_programming(&card_state);
while((status == SD_OK) && ((card_state == SD_CARD_PROGRAMMING) || (card_state == SD_CARD_RECEIVING)))
{
status = check_card_programming(&card_state);
}
return status;
}
/**
* @brief sdio1 isr.
* @param none.
* @retval none.
*/
void SDIO1_IRQHandler(void)
{
sd_irq_service();
}
/**
* @brief sdio2 isr.
* @param none.
* @retval none.
*/
void SDIO2_IRQHandler(void)
{
sd_irq_service();
}
/**
* @brief allows to process all the interrupts that are high.
* @param none
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_irq_service(void)
{
if(sdio_interrupt_flag_get(SDIOx, SDIO_DTCMPL_FLAG) != RESET)
{
if(stop_flag == 1)
{
/* send cmd12, stop transmission */
sdio_command_init_struct.argument = 0;
sdio_command_init_struct.cmd_index = SD_CMD_STOP_TRANSMISSION;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
transfer_error = command_rsp1_error(SD_CMD_STOP_TRANSMISSION);
}
else
{
transfer_error = SD_OK;
}
/* clear flag */
sdio_flag_clear(SDIOx, SDIO_DTCMPL_FLAG);
transfer_end = 1;
return transfer_error;
}
if(sdio_interrupt_flag_get(SDIOx, SDIO_DTFAIL_FLAG) != RESET)
{
/* clear flag */
sdio_flag_clear(SDIOx, SDIO_DTFAIL_FLAG);
transfer_error = SD_DATA_FAIL;
transfer_end = 1;
return transfer_error;
}
if(sdio_interrupt_flag_get(SDIOx, SDIO_DTTIMEOUT_FLAG) != RESET)
{
/* clear flag */
sdio_flag_clear(SDIOx, SDIO_DTTIMEOUT_FLAG);
transfer_error = SD_DATA_TIMEOUT;
transfer_end = 1;
return transfer_error;
}
if(sdio_interrupt_flag_get(SDIOx, SDIO_RXERRO_FLAG) != RESET)
{
/* clear flag */
sdio_flag_clear(SDIOx, SDIO_RXERRO_FLAG);
transfer_error = SD_RX_OVERRUN;
transfer_end = 1;
return(SD_RX_OVERRUN);
}
if(sdio_interrupt_flag_get(SDIOx, SDIO_TXERRU_FLAG) != RESET)
{
/* clear flag */
sdio_flag_clear(SDIOx, SDIO_TXERRU_FLAG);
transfer_error = SD_TX_UNDERRUN;
transfer_end = 1;
return(SD_TX_UNDERRUN);
}
if(sdio_interrupt_flag_get(SDIOx, SDIO_SBITERR_FLAG) != RESET)
{
/* clear flag */
sdio_flag_clear(SDIOx, SDIO_SBITERR_FLAG);
transfer_error = SD_START_BIT_ERR;
transfer_end = 1;
return(SD_START_BIT_ERR);
}
/* disable interrupt */
sdio_interrupt_enable(SDIOx, (SDIO_DTFAIL_INT | SDIO_DTTIMEOUT_INT | \
SDIO_DTCMP_INT | SDIO_TXBUFH_INT | SDIO_RXBUFH_INT | \
SDIO_TXERRU_INT| SDIO_RXERRO_INT | SDIO_SBITERR_INT), FALSE);
return(SD_OK);
}
/**
* @brief checks for error conditions for cmd0.
* @param none
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type command_error(void)
{
sd_error_status_type status = SD_OK;
uint32_t timeout = SDIO_CMD0TIMEOUT;
while(timeout--)
{
if(sdio_flag_get(SDIOx, SDIO_CMDCMPL_FLAG) != RESET)
{
break;
}
}
if(timeout == 0)
{
return SD_CMD_RSP_TIMEOUT;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
return status;
}
/**
* @brief checks for error conditions for R7 response.
* @param none
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type command_rsp7_error(void)
{
sd_error_status_type status = SD_OK;
uint32_t sts_reg = 0;
uint32_t timeout = SDIO_CMD0TIMEOUT;
while(timeout--)
{
sts_reg = SDIOx->sts;
if(sts_reg & (SDIO_CMDFAIL_FLAG | SDIO_CMDTIMEOUT_FLAG | SDIO_CMDRSPCMPL_FLAG))
{
break;
}
}
if((timeout == 0) || (sts_reg & SDIO_CMDTIMEOUT_FLAG))
{
status = SD_CMD_RSP_TIMEOUT;
sdio_flag_clear(SDIOx, SDIO_CMDTIMEOUT_FLAG);
return status;
}
if(sts_reg & SDIO_CMDRSPCMPL_FLAG)
{
status = SD_OK;
sdio_flag_clear(SDIOx, SDIO_CMDRSPCMPL_FLAG);
}
return status;
}
/**
* @brief checks for error conditions for R1 response.
* @param cmd: the sent command index.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type command_rsp1_error(uint8_t cmd)
{
uint32_t sts_reg = 0;
uint32_t rsp_cmd = 0;
while(1)
{
sts_reg = SDIOx->sts;
if(sts_reg & (SDIO_CMDFAIL_FLAG | SDIO_CMDTIMEOUT_FLAG | SDIO_CMDRSPCMPL_FLAG))
{
break;
}
}
if(sdio_flag_get(SDIOx, SDIO_CMDTIMEOUT_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDTIMEOUT_FLAG);
return SD_CMD_RSP_TIMEOUT;
}
if(sdio_flag_get(SDIOx, SDIO_CMDFAIL_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDFAIL_FLAG);
return SD_CMD_FAIL;
}
rsp_cmd = sdio_command_response_get(SDIOx);
if(rsp_cmd != cmd)
{
return SD_ILLEGAL_CMD;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
return (sd_error_status_type)(sdio_response_get(SDIOx, SDIO_RSP1_INDEX) & SD_OCR_ERRORBITS);//???
}
/**
* @brief checks for error conditions for R3 (ocr) response.
* @param none
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type command_rsp3_error(void)
{
uint32_t sts_reg = 0;;
while(1)
{
sts_reg = SDIOx->sts;
if(sts_reg & (SDIO_CMDFAIL_FLAG | SDIO_CMDTIMEOUT_FLAG | SDIO_CMDRSPCMPL_FLAG))
{
break;
}
}
if(sdio_flag_get(SDIOx, SDIO_CMDTIMEOUT_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDTIMEOUT_FLAG);
return SD_CMD_RSP_TIMEOUT;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
return SD_OK;
}
/**
* @brief checks for error conditions for R2 (cid or csd) response.
* @param none
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type command_rsp2_error(void)
{
sd_error_status_type status = SD_OK;
uint32_t sts_reg;
uint32_t timeout = SDIO_CMD0TIMEOUT;
while(timeout--)
{
sts_reg = SDIOx->sts;
if(sts_reg & (SDIO_CMDFAIL_FLAG | SDIO_CMDTIMEOUT_FLAG | SDIO_CMDRSPCMPL_FLAG))
{
break;
}
}
if((timeout == 0) || sdio_flag_get(SDIOx, SDIO_CMDTIMEOUT_FLAG) != RESET)
{
status = SD_CMD_RSP_TIMEOUT;
sdio_flag_clear(SDIOx, SDIO_CMDTIMEOUT_FLAG);
return status;
}
if(sdio_flag_get(SDIOx, SDIO_CMDFAIL_FLAG) != RESET)
{
status = SD_CMD_FAIL;
sdio_flag_clear(SDIOx, SDIO_CMDFAIL_FLAG);
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
return status;
}
/**
* @brief checks for error conditions for r4 response.
* @param cmd: the sent command index.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type command_rsp4_error(uint8_t cmd)
{
uint32_t sts_reg = 0, rsp_cmd = 0;
while(1)
{
sts_reg = SDIOx->sts;
if(sts_reg & (SDIO_CMDFAIL_FLAG | SDIO_CMDTIMEOUT_FLAG | SDIO_CMDRSPCMPL_FLAG))
{
break;
}
}
if(sdio_flag_get(SDIOx, SDIO_CMDTIMEOUT_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDTIMEOUT_FLAG);
return SD_CMD_RSP_TIMEOUT;
}
if(sdio_flag_get(SDIOx, SDIO_CMDFAIL_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDFAIL_FLAG);
return SD_CMD_FAIL;
}
rsp_cmd = sdio_command_response_get(SDIOx);
if(rsp_cmd != cmd)
{
return SD_ILLEGAL_CMD;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
return SD_OK;
}
/**
* @brief checks for error conditions for r5 response.
* @param cmd: the sent command index.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type command_rsp5_error(uint8_t cmd)
{
uint32_t sts_reg = 0, rsp_cmd = 0, response = 0;
while(1)
{
sts_reg = SDIOx->sts;
if(sts_reg & (SDIO_CMDFAIL_FLAG | SDIO_CMDTIMEOUT_FLAG | SDIO_CMDRSPCMPL_FLAG))
{
break;
}
}
if(sdio_flag_get(SDIOx, SDIO_CMDTIMEOUT_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDTIMEOUT_FLAG);
return SD_CMD_RSP_TIMEOUT;
}
if(sdio_flag_get(SDIOx, SDIO_CMDFAIL_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDFAIL_FLAG);
return SD_CMD_FAIL;
}
rsp_cmd = sdio_command_response_get(SDIOx);
if(rsp_cmd != cmd)
{
return SD_ILLEGAL_CMD;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
if(response & SD_R5_OUT_OF_RANGE)
{
return SD_CMD_OUT_OF_RANGE;
}
if(response & SD_R5_FUNCTION_NUMBER)
{
return SD_SDIO_UNKNOWN_FUNC;
}
if(response & SD_R5_ERROR)
{
return SD_GENERAL_UNKNOWN_ERROR;
}
return SD_OK;
}
/**
* @brief checks for error conditions for r6 (rca) response.
* @param cmd: the sent command index.
* @param prca: pointer to the variable that will contain the sd card relative
* address rca.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type command_rsp6_error(uint8_t cmd, uint16_t *prca)
{
sd_error_status_type status = SD_OK;
uint32_t sts_reg, rsp_cmd = 0, response = 0;
while(1)
{
sts_reg = SDIOx->sts;
if(sts_reg & (SDIO_CMDFAIL_FLAG | SDIO_CMDTIMEOUT_FLAG | SDIO_CMDRSPCMPL_FLAG))
{
break;
}
}
if(sdio_flag_get(SDIOx, SDIO_CMDTIMEOUT_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDTIMEOUT_FLAG);
return SD_CMD_RSP_TIMEOUT;
}
if(sdio_flag_get(SDIOx, SDIO_CMDFAIL_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDFAIL_FLAG);
return SD_CMD_FAIL;
}
rsp_cmd = sdio_command_response_get(SDIOx);
if(rsp_cmd != cmd)
{
return SD_ILLEGAL_CMD;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
if(SD_ALLZERO == (response & (SD_R6_GENERAL_UNKNOWN_ERROR | SD_R6_ILLEGAL_CMD | SD_R6_CMD_CRC_ERROR)))
{
*prca = (uint16_t)(response >> 16);
return status;
}
if(response & SD_R6_GENERAL_UNKNOWN_ERROR)
{
return SD_GENERAL_UNKNOWN_ERROR;
}
if(response & SD_R6_ILLEGAL_CMD)
{
return SD_ILLEGAL_CMD;
}
if(response & SD_R6_CMD_CRC_ERROR)
{
return SD_CMD_CRC_ERROR;
}
return status;
}
/**
* @brief enable or disable the sdio wide bus mode.
* @param new_state: new state of the sdio wide bus mode. (true or false)
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_bus_wide_enable(confirm_state new_state)
{
sd_error_status_type status = SD_OK;
uint32_t response = 0;
uint8_t arg = 0x00;
if(new_state == TRUE)
{
arg = 0x02;
}
else
{
arg = 0x00;
}
/* get response1 */
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
/* check card locked or not */
if(response & SD_CARD_LOCKED)
{
return SD_LOCK_UNLOCK_ERROR;
}
if(sd_card_info.sd_scr_reg.sd_bus_width)
{
sdio_command_init_struct.argument = (uint32_t)(sd_card_info.rca << 16);
sdio_command_init_struct.cmd_index = SD_CMD_APP_CMD;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_APP_CMD);
if(status != SD_OK)
{
return status;
}
sdio_command_init_struct.argument = arg;
sdio_command_init_struct.cmd_index = SD_CMD_APP_SD_SET_BUSWIDTH;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_APP_SD_SET_BUSWIDTH);
return status;
}
else
{
return SD_REQ_NOT_APPLICABLE;
}
}
/**
* @brief switch mmc card speed to high speed
* @param set: new state of the sdio wide bus mode.
* @param index: offset.
* @param value: expect speed.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type mmc_switch(uint8_t set, uint8_t index, uint8_t value)
{
sd_error_status_type status = SD_OK;
uint32_t card_status = 0, timeout = 0;
sdio_command_init_struct.argument = (EXT_CSD_Write_byte << 24) | (index << 16) | (value << 8) | set;
sdio_command_init_struct.cmd_index = SD_CMD_HS_SWITCH;
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_HS_SWITCH);
if(status != SD_OK)
{
return status;
}
card_status = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
if(card_status & MMC_SWITCH_ERROR)
{
return SD_SWITCH_ERROR;
}
timeout = SD_DATATIMEOUT;
do
{
timeout--;
status = sd_status_send(&card_status);
} /* check ready_for_data flag */
while(((card_status & 0x00000100) == 0) && (timeout > 0));
if(timeout == 0)
{
return SD_ERROR;
}
return status;
}
/**
* @brief switch sd card speed to high speed
* @param mode: polling mode or dma mode
* @param group: selected group
* @param value: wanted speed
* @param rsp: switch status
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_switch(uint32_t mode, uint32_t group, uint8_t value, uint8_t *rsp)
{
sd_error_status_type status = SD_OK;
uint8_t power;
uint16_t blk_size;
if(rsp == NULL)
{
return SD_INVALID_PARAMETER;
}
SDIOx->dtctrl = 0x0;
blk_size = 64;
power = convert_from_bytes_to_power_of_two(blk_size);
sdio_command_init_struct.argument = blk_size;
sdio_command_init_struct.cmd_index = SD_CMD_SET_BLOCKLEN;//CMD16设置块大小为64
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_SET_BLOCKLEN);
if(status != SD_OK)
{
return status;
}
sdio_data_init_struct.block_size = (sdio_block_size_type)(power);
sdio_data_init_struct.data_length = blk_size ;
sdio_data_init_struct.timeout = SD_DATATIMEOUT ;
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CONTROLLER;
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;
sdio_command_init_struct.argument = (mode << 31) | 0x00FFFFFF;
sdio_command_init_struct.argument &= ~(0xF << (group * 4));
sdio_command_init_struct.argument |= value << (group * 4);//??? 0x00FFFFF0
sdio_command_init_struct.cmd_index = SD_CMD_HS_SWITCH; //发送CMD6 Mode0(查询): group1(访问模式)-functionx(默认),group2(命令系统)-function(无影响),group3...
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
stop_flag = 0;
return sdio_command_data_send(&sdio_command_init_struct, &sdio_data_init_struct, (uint32_t *)rsp);
}
/**
* @brief checks if the sd card is in programming state.
* @param p_status: pointer to the variable that will contain the sd card state.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type check_card_programming(uint8_t *p_status)
{
volatile uint32_t response = 0, sts_reg = 0, rsp_cmd = 0;
/* send cmd13 */
sdio_command_init_struct.argument = (uint32_t)(rca << 16);
sdio_command_init_struct.cmd_index = SD_CMD_SEND_STATUS;//发送CMD13查询卡状态
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
sts_reg = SDIOx->sts;
while(!(sts_reg & (SDIO_CMDFAIL_FLAG | SDIO_CMDTIMEOUT_FLAG | SDIO_CMDRSPCMPL_FLAG)))
{
sts_reg = SDIOx->sts;
}
if(sdio_flag_get(SDIOx, SDIO_CMDFAIL_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDFAIL_FLAG);
return SD_CMD_FAIL;
}
if(sdio_flag_get(SDIOx, SDIO_CMDTIMEOUT_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_CMDTIMEOUT_FLAG);
return SD_CMD_RSP_TIMEOUT;
}
rsp_cmd = sdio_command_response_get(SDIOx);
if(rsp_cmd!= SD_CMD_SEND_STATUS)
{
return SD_ILLEGAL_CMD;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
response = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
*p_status = (uint8_t)((response >> 9) & 0x0000000F);//CURRENT_STATE
return SD_OK;
}
/**
* @brief read current card status.
* @param p_card_status: card status.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type sd_status_send(uint32_t *p_card_status)
{
sd_error_status_type status = SD_OK;
if(p_card_status == NULL)
{
status = SD_INVALID_PARAMETER;
return status;
}
sdio_command_init_struct.argument = (uint32_t)(rca << 16);
sdio_command_init_struct.cmd_index = SD_CMD_SEND_STATUS;//发送CMD13查询卡状态
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_SEND_STATUS);
if(status != SD_OK)
{
return status;
}
*p_card_status = sdio_response_get(SDIOx, SDIO_RSP1_INDEX);
return status;
}
/**
* @brief returns information about sd card.
* @param none.
* @retval sd_card_state_type: sd card state code.
*/
sd_card_state_type sd_state_get(void)
{
uint32_t response = 0;
if(sd_status_send(&response) != SD_OK)
{
return SD_CARD_ERROR;
}
else
{
return (sd_card_state_type)((response >> 9) & 0x0F);
}
}
/**
* @brief find the sd card scr register value.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type scr_find(void)
{
uint32_t index = 0, sts_reg = 0;
sd_error_status_type status = SD_OK;
uint32_t *tempscr;
tempscr = (uint32_t *) &(sd_card_info.sd_scr_reg);
/* send cmd16, set block length */
sdio_command_init_struct.argument = (uint32_t)8;//设置块大小为8bytes
sdio_command_init_struct.cmd_index = SD_CMD_SET_BLOCKLEN;//发送CMD16设置数据块大小
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;//短响应
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_SET_BLOCKLEN);
if(status != SD_OK)
{
return status;
}
/* send cmd55 */
sdio_command_init_struct.argument = (uint32_t)(sd_card_info.rca << 16);//被选中卡片的相对地址
sdio_command_init_struct.cmd_index = SD_CMD_APP_CMD;//发送CMD55
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;//短响应
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_APP_CMD);
if(status != SD_OK)
{
return status;
}
sdio_data_init_struct.block_size = SDIO_DATA_BLOCK_SIZE_8B;//数据块大小
sdio_data_init_struct.data_length = 8;//数据长度8bytes,及1块
sdio_data_init_struct.timeout = SD_DATATIMEOUT;//数据超时拉满
sdio_data_init_struct.transfer_direction = SDIO_DATA_TRANSFER_TO_CONTROLLER;//read
sdio_data_init_struct.transfer_mode = SDIO_DATA_BLOCK_TRANSFER;//块传输非数据流
sdio_data_config(SDIOx, &sdio_data_init_struct);
sdio_data_state_machine_enable(SDIOx, TRUE);
/* send cmd51 */
sdio_command_init_struct.argument = 0x0;
sdio_command_init_struct.cmd_index = SD_CMD_SD_APP_SEND_SCR;//发送ACMD51发送SCR
sdio_command_init_struct.rsp_type = SDIO_RESPONSE_SHORT;
sdio_command_init_struct.wait_type = SDIO_WAIT_FOR_NO;
/* sdio command config */
sdio_command_config(SDIOx, &sdio_command_init_struct);
/* enable ccsm */
sdio_command_state_machine_enable(SDIOx, TRUE);
status = command_rsp1_error(SD_CMD_SD_APP_SEND_SCR);
if(status != SD_OK)
{
return status;
}
sts_reg = SDIOx->sts;
while(!(sts_reg & (SDIO_RXERRO_FLAG | SDIO_DTFAIL_FLAG | SDIO_DTTIMEOUT_FLAG | SDIO_DTBLKCMPL_FLAG | SDIO_SBITERR_FLAG)))
{
if(sdio_flag_get(SDIOx, SDIO_RXBUF_FLAG) != RESET)
{
*(tempscr + index) = sdio_data_read(SDIOx);//读数据buff寄存器
index++;
}
sts_reg = SDIOx->sts;
}
if(sdio_flag_get(SDIOx, SDIO_DTTIMEOUT_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_DTTIMEOUT_FLAG);
return SD_DATA_TIMEOUT;
}
else if(sdio_flag_get(SDIOx, SDIO_DTFAIL_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_DTFAIL_FLAG);
return SD_DATA_FAIL;
}
else if(sdio_flag_get(SDIOx, SDIO_RXERRO_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_RXERRO_FLAG);
return SD_RX_OVERRUN;
}
else if(sdio_flag_get(SDIOx, SDIO_SBITERR_FLAG) != RESET)
{
sdio_flag_clear(SDIOx, SDIO_SBITERR_FLAG);
return SD_START_BIT_ERR;
}
sdio_flag_clear(SDIOx, SDIO_STATIC_FLAGS);
return status;
}
/**
* @brief set bus speed
* @param speed: 0,normal speed; 1,high speed.
* @retval sd_error_status_type: sd card error code.
*/
sd_error_status_type speed_change(uint8_t speed)
{
sd_error_status_type status = SD_OK;
uint8_t switch_sts[64];
if(speed > 1)
{
return SD_ERROR;
}
/* check card type */
if((SDIO_STD_CAPACITY_SD_CARD_V1_1 == card_type) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == card_type) || \
(SDIO_HIGH_CAPACITY_SD_CARD == card_type))
{
/* version 1.01 card does not support cmd6 */
if(sd_card_info.sd_scr_reg.sd_spec == 0)
{
return SD_ERROR;
}
/* check group 1 function support speed */
status = sd_switch(0, 0, speed, switch_sts);
//CMD6 Mode0(查询): group1(访问模式)-functionx(默认),group2(命令系统)-function(无影响),group3...
if(status != 0)
{
return status;
}
if((switch_sts[13] & (1 << speed)) == 0)
{
return SD_ERROR;
}
status = sd_switch(1, 0, speed, switch_sts);
//CMD6 Mode1(切换): group1(访问模式)-functionx(默认),group2(命令系统)-function(无影响),group3...
if(status != 0)
{
return status;
}
/* read it back for confirmation */
if((switch_sts[16] & 0xF) != speed)
{
return SD_ERROR;
}
}
else if(card_type == SDIO_HIGH_SPEED_MULTIMEDIA_CARD)
{
status = mmc_switch(EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, (uint8_t)speed);
if(status != 0)
{
return status;
}
}
return status;
}
/**
* @brief converts the number of bytes in power of two and returns the power.
* @param number_of_bytes: number of bytes.
* @retval none
*/
uint8_t convert_from_bytes_to_power_of_two(uint16_t number_of_bytes)
{
uint8_t count = 0;
while(number_of_bytes != 1)
{
number_of_bytes >>= 1;
count++;
}
return count;
}
/**
* @brief set dma configuation for sdio
* @param mbuf: buffer address
* @param buf_size: transmission data size
* @param dir: dma direction, it is DMA_DIR_MEMORY_TO_PERIPHERAL(writing data)
* or DMA_DIR_PERIPHERAL_TO_MEMORY(read data)
* @retval none
*/
void sd_dma_config(uint32_t *mbuf, uint32_t buf_size, dma_dir_type dir)
{
dma_init_type dma_init_struct;
dma_default_para_init(&dma_init_struct);
crm_periph_clock_enable(CRM_DMA2_PERIPH_CLOCK, TRUE);
dma_reset(DMA2_CHANNEL4);
dma_channel_enable(DMA2_CHANNEL4, FALSE);
dma_init_struct.peripheral_base_addr = (uint32_t)&SDIOx->buf;
dma_init_struct.memory_base_addr = (uint32_t)mbuf;
dma_init_struct.direction = dir;
dma_init_struct.buffer_size = buf_size / 4;
dma_init_struct.peripheral_inc_enable = FALSE;
dma_init_struct.memory_inc_enable = TRUE;
dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_WORD;
dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_WORD;
dma_init_struct.loop_mode_enable = FALSE;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init(DMA2_CHANNEL4, &dma_init_struct);
dmamux_init(DMA2MUX_CHANNEL4, DMAMUX_SDIOx);
dmamux_enable(DMA2, TRUE);
dma_channel_enable(DMA2_CHANNEL4, TRUE);
}
/**
* @}
*/
/**
* @}
*/
3.读写代码解读
结合以上代码与以下图片: