/** ************************************************************************** * @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 #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); } /** * @} */ /** * @} */