GONEC_ARTERY_FF/at32_sdio.c

2503 lines
80 KiB
C

/**
**************************************************************************
* @file at32_sdio.c
* @version v2.0.4
* @date 2021-12-31
* @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"
/** @addtogroup AT32F437_periph_examples
* @{
*/
/** @addtogroup 437_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);
#include "SystemDelayInterface.h"
/**
* @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};
/* gpioc and gpiod periph clock enable */
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOC_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_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOC, &gpio_init_struct);
/* configure pb.2 pin: clk pin */
gpio_init_struct.gpio_pins = GPIO_PINS_2;
gpio_init(GPIOB, &gpio_init_struct);
/* configure pa.06 cmd line */
gpio_init_struct.gpio_pins = GPIO_PINS_6;
gpio_init(GPIOA, &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(GPIOB, GPIO_PINS_SOURCE2, GPIO_MUX_12);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE6, GPIO_MUX_12);
/* reset sdio */
sdio_reset(SDIOx);
/* power on */
status = sd_power_on();
if (status == SD_OK) {
/* sdio card initialize */
status = sd_card_init();
}
if (status == SD_OK) {
/* get card information*/
status = sd_card_info_get(&sd_card_info);
}
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));
}
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();
}
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 / 100000000;
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;
/* 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);
/* config sdio bus width */
sdio_bus_width_config(SDIOx, SDIO_BUS_WIDTH_D1);
/* 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 power on */
sdio_power_set(SDIOx, SDIO_POWER_ON);
/* enable to output sdio_ck */
sdio_clock_enable(SDIOx, TRUE);
for (retry = 0; retry < 10; 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();
if (status == SD_OK) {
break;
}
}
/* if any errors occured, return status */
if (status != SD_OK) {
return status;
}
/* send cmd8, check card interface feature */
sdio_command_init_struct.argument = SD_CHECK_PATTERN;
sdio_command_init_struct.cmd_index = SDIO_SEND_IF_COND;
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();
if (status == SD_OK) {
/* set card type and sd type */
card_type = SDIO_STD_CAPACITY_SD_CARD_V2_0;
sd_type = SD_HIGH_CAPACITY;
}
/* send cmd55, check sd version */
sdio_command_init_struct.argument = 0x00;
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);
/* waiting R1 */
status = command_rsp1_error(SD_CMD_APP_CMD);
/* check sd card or mmc card */
if (SD_OK == status) {
/* send acmd41, check voltage operation range */
while ((!valid_voltage) && (count < SD_MAX_VOLT_TRIAL)) {
/* send cmd55 before acmd41 */
sdio_command_init_struct.argument = 0x00;
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);
/* 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;
sdio_command_init_struct.cmd_index = SD_CMD_SD_APP_OP_COND;
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 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);
count++;
}
if (count >= SD_MAX_VOLT_TRIAL) {
status = SD_INVALID_VOLTRANGE;
return status;
}
if (response &= SD_HIGH_CAPACITY) {
card_type = SDIO_HIGH_CAPACITY_SD_CARD;
}
}
/* mmc card */
else {
/* send cmd1 */
while ((!valid_voltage) && (count < SD_MAX_VOLT_TRIAL)) {
sdio_command_init_struct.argument = SD_VOLTAGE_WINDOW_MMC;
sdio_command_init_struct.cmd_index = SD_CMD_SEND_OP_COND;
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;
}
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;
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;
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);
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;
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;
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);
}
}
/* 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);
/* 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;
}
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;
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);
}
/* check ready_for_data flag */
while (((card_status & 0x00000100) == 0) && (timeout > 0));
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;
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))) {
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;
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;
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;
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_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_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_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_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_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_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;
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);
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;
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;
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);
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;
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);
}
}
void delay_sec(uint16_t sec);
/**
* @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;
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;
}
/* send cmd55 */
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;
}
// SystemDelayMs(2000);
delay_sec(2);
sdio_data_init_struct.block_size = SDIO_DATA_BLOCK_SIZE_8B;
sdio_data_init_struct.data_length = 8;
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_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;
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);
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);
if (status != 0) {
return status;
}
if ((switch_sts[13] & (1 << speed)) == 0) {
return SD_ERROR;
}
status = sd_switch(1, 0, speed, switch_sts);
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_VERY_HIGH;
dma_init(DMA2_CHANNEL4, &dma_init_struct);
dmamux_init(DMA2MUX_CHANNEL4, DMAMUX_SDIOx);
dmamux_enable(DMA2, TRUE);
dma_channel_enable(DMA2_CHANNEL4, TRUE);
// nvic_irq_enable(SDIO1_IRQn, 1, 0);
// NVIC_SetPriority((IRQn_Type) SDIO1_IRQn, 0x0);
}
/**
* @}
*/
/**
* @}
*/