#include "sd_mmc.h" #include "sd_mmc_intf.h" #include // Functions used to interact with the SD card static sd_mmc_err_t sd_mmc_power_up_sequence(); static sd_mmc_err_t sd_mmc_idle_sequence(); static sd_mmc_err_t sd_mmc_if_sequence(); static sd_mmc_err_t sd_mmc_read_ocr(uint8_t* res); static sd_mmc_err_t sd_mmc_read_res1(uint8_t* res1); static sd_mmc_err_t sd_mmc_read_res7(uint8_t* res7); static sd_mmc_err_t sd_mmc_read_res3(uint8_t* res3); static bool sd_mmc_check_cd(void); static void sd_mmc_send_clock(void); // Functions used for debugging static void sd_mmc_print_err(sd_mmc_err_t err); static void sd_mmc_print_res1(uint8_t res); static void sd_mmc_print_res7(const uint8_t* const res); static void sd_mmc_print_res3(const uint8_t* const res); sd_mmc_err_t sd_mmc_init(void) { sd_mmc_err_t ret = SD_MMC_SUCCESS; uint8_t ocr[5]; do { // Hardware initialize if(0 != SD_MMC_INTF_INIT()) { ret = SD_MMC_INIT_FAILURE; break; } // Power up sd card sequence if((ret = sd_mmc_power_up_sequence()) != SD_MMC_SUCCESS) { break; } // go idle if((ret = sd_mmc_idle_sequence()) != SD_MMC_SUCCESS) { break; } // interface sequence if((ret = sd_mmc_if_sequence()) != SD_MMC_SUCCESS) { break; } // read operation conditions register if((ret = sd_mmc_read_ocr(ocr)) != SD_MMC_SUCCESS) { break; } }while(0); if(ret != SD_MMC_SUCCESS) { printf("Error initializing SD Card: "); sd_mmc_print_err(ret); } return ret; } sd_mmc_err_t sd_mmc_deselect(uint8_t slot) { UNUSED(slot); sd_mmc_err_t ret = SD_MMC_SUCCESS; uint8_t dummy = 0xFF; SD_MMC_INTF_WRITE(&dummy, 1); SD_MMC_INTF_DESELECT_DEVICE(); SD_MMC_INTF_WRITE(&dummy, 1); return ret; } sd_mmc_err_t sd_mmc_select(uint8_t slot, uint32_t clock, uint8_t bus_width, bool high_speed) { UNUSED(slot); UNUSED(clock); UNUSED(bus_width); UNUSED(high_speed); sd_mmc_err_t ret = SD_MMC_SUCCESS; uint8_t dummy = 0xFF; SD_MMC_INTF_WRITE(&dummy, 1); SD_MMC_INTF_SELECT_DEVICE(); SD_MMC_INTF_WRITE(&dummy, 1); return ret; } sd_mmc_err_t sd_mmc_send_command(uint8_t cmd, uint32_t arg, uint8_t crc) { sd_mmc_err_t ret = SD_MMC_SUCCESS; uint8_t bytes[6]; bytes[0] = cmd | 0x40; bytes[1] = ((uint8_t)(arg >> 24)); bytes[2] = ((uint8_t)(arg >> 16)); bytes[3] = ((uint8_t)(arg >> 8)); bytes[4] = ((uint8_t)(arg)); bytes[5] = crc | 0x01; if(6 != SD_MMC_INTF_WRITE(bytes, 6)) { ret = SD_MMC_ERR_COMM; } return ret; } sd_mmc_err_t sd_mmc_read_res1(uint8_t* byte) { sd_mmc_err_t ret = SD_MMC_SUCCESS; uint8_t i = 0; do { i++; SD_MMC_INTF_READ(byte, 1); }while(i <= 8 && *byte == 0xFF); if(*byte == 0xFF) { return SD_MMC_TIMEOUT; } return ret; } sd_mmc_err_t sd_mmc_read_res7(uint8_t* res7) { sd_mmc_err_t ret = SD_MMC_SUCCESS; do { if(5 != SD_MMC_INTF_READ(res7, 5)) { ret = SD_MMC_ERR_COMM; break; } printf("hi"); if(res7[0] > 1) { // handle this error better later printf("res7: 0x%02x\n", res7[0]); perr("res7 > 1..."); ret = SD_MMC_ERR_COMM; break; } }while(0); return ret; } sd_mmc_err_t sd_mmc_read_res3(uint8_t* res3) { sd_mmc_err_t ret = SD_MMC_SUCCESS; do { if(5 != SD_MMC_INTF_READ(res3, 5)) { ret = SD_MMC_ERR_COMM; break; } if(res3[0] > 1) { // handle this error better later ret = SD_MMC_ERR_COMM; break; } }while(0); return ret; } sd_mmc_err_t sd_mmc_power_up_sequence() { sd_mmc_err_t ret = SD_MMC_SUCCESS; // power up sequence delay_ms(1); sd_mmc_send_clock(); return ret; } sd_mmc_err_t sd_mmc_idle_sequence() { sd_mmc_err_t ret = SD_MMC_SUCCESS; uint8_t res1 = 0xFF; sd_mmc_select(0, 0, 0, false); do { sd_mmc_select(0, 0, 0, true); if((ret = sd_mmc_send_command(CMD0, CMD0_ARG, CMD0_CRC)) != SD_MMC_SUCCESS) { break; } if((ret = sd_mmc_read_res1(&res1)) != SD_MMC_SUCCESS) { break; } sd_mmc_print_res1(res1); }while(0); sd_mmc_deselect(0); return ret; } sd_mmc_err_t sd_mmc_if_sequence() { sd_mmc_err_t ret = SD_MMC_SUCCESS; uint8_t res7[5]; sd_mmc_select(0, 0, 0, false); do { if((ret = sd_mmc_send_command(CMD8, CMD8_ARG, CMD8_CRC)) != SD_MMC_SUCCESS) { break; } if((ret = sd_mmc_read_res7(res7)) != SD_MMC_SUCCESS) { break; } sd_mmc_print_res7(res7); }while(0); sd_mmc_deselect(0); return ret; } void sd_mmc_print_err(sd_mmc_err_t err) { switch(err) { case SD_MMC_SUCCESS: { printf("%s", "SD_MMC_SUCCESS\n"); }break; case SD_MMC_FAILURE: { printf("%s", "SD_MMC_FAILURE\n"); }break; case SD_MMC_INIT_ONGOING: { printf("%s", "SD_MMC_INIT_ONGOING\n"); }break; case SD_MMC_ERR_NO_CARD: { printf("%s", "SD_MMC_ERR_NO_CARD\n"); } case SD_MMC_ERR_UNUSABLE: { printf("%s", "SD_MMC_UNUSABLE\n"); }break; case SD_MMC_ERR_SLOT: { printf("%s", "SD_MMC_ERR_SLOT\n"); }break; case SD_MMC_ERR_COMM: { printf("%s", "SD_MMC_ERR_COMM\n"); }break; case SD_MMC_ERR_PARAM: { printf("%s", "SD_MMC_ERR_PARAM\n"); }break; case SD_MMC_ERR_WP: { printf("%s", "SD_MMC_ERR_WP\n"); }break; case SD_MMC_INIT_FAILURE: { printf("%s", "SD_MMC_INIT_FAILURE\n"); }break; case SD_MMC_TIMEOUT: { printf("%s", "SD_MMC_TIMEOUT\n"); }break; default: { printf("%s", "UNKNOWN ERROR"); } } } void sd_mmc_print_res1(uint8_t res) { printf("RES1: 0x%02x\n", res); if(res & 0b10000000) { printf("\tError: MSB = 1\r\n"); return; } if(res == 0) { printf("\tCard Ready\r\n"); return; } if(PARAM_ERROR(res)) printf("\tParameter Error\r\n"); if(ADDR_ERROR(res)) printf("\tAddress Error\r\n"); if(ERASE_SEQ_ERROR(res)) printf("\tErase Sequence Error\r\n"); if(CRC_ERROR(res)) printf("\tCRC Error\r\n"); if(ILLEGAL_CMD(res)) printf("\tIllegal Command\r\n"); if(ERASE_RESET(res)) printf("\tErase Reset Error\r\n"); if(IN_IDLE(res)) printf("\tIn Idle State\r\n"); } void sd_mmc_print_res7(const uint8_t* const res) { sd_mmc_print_res1(res[0]); if(res[0] > 1) return; printf("\tCommand Version: 0x%02x\n", CMD_VER(res[1])); printf("\tVoltage Accepted: %s\n", (res[3] == VOLTAGE_ACC_27_33) ? "2.7-3.6V" : (res[3] == VOLTAGE_ACC_LOW) ? "LOW VOLTAGE" : (res[3] == VOLTAGE_ACC_RES1 || res[3] == VOLTAGE_ACC_RES2) ? "RESERVED" : "NOT DEFINED"); printf("\tEcho: 0x%02x\n", res[4]); } void sd_mmc_print_res3(const uint8_t* const res) { sd_mmc_print_res1(res[0]); if(res[0] > 1) return; printf("\tCard Power Up Status: 0x%02x\n", res[1]); if(POWER_UP_STATUS(res[1])) { printf("READY\n"); printf("\tCCS Status: %d\n", CCS_VAL(res[1]) ? 1 : 0); } else { printf("BUSY\n"); } printf("\tVDD Window: "); if(VDD_2728(res[3])) printf("2.7-2.8, "); if(VDD_2829(res[2])) printf("2.8-2.9, "); if(VDD_2930(res[2])) printf("2.9-3.0, "); if(VDD_3031(res[2])) printf("3.0-3.1, "); if(VDD_3132(res[2])) printf("3.1-3.2, "); if(VDD_3233(res[2])) printf("3.2-3.3, "); if(VDD_3334(res[2])) printf("3.3-3.4, "); if(VDD_3435(res[2])) printf("3.4-3.5, "); if(VDD_3536(res[2])) printf("3.5-3.6"); printf("\n"); } bool sd_mmc_check_cd(void) { switch(SD_MMC_CD_MODE) { case SD_MMC_CD_MODE_UNUSED: { // Always assume the card is detected since we aren't using this function return true; }break; case SD_MMC_CD_MODE_SYNC: { return SD_MMC_INTF_CHECK_CD(); }break; case SD_MMC_CD_MODE_ASYNC: { // Unimplemented return false; }break; default: { return false; } } return false; } void sd_mmc_send_clock() { uint8_t i; uint8_t dummy = 0xFF; for(i = 0; i < 10; i++) { SD_MMC_INTF_WRITE(&dummy, 1); } } sd_mmc_err_t sd_mmc_read_ocr(uint8_t* res) { sd_mmc_err_t ret = SD_MMC_SUCCESS; sd_mmc_select(0, 0, 0, false); do { if((ret = sd_mmc_send_command(CMD58, CMD58_ARG, CMD58_CRC)) != SD_MMC_SUCCESS) { break; } if((ret = sd_mmc_read_res3(res)) != SD_MMC_SUCCESS) { break; } sd_mmc_print_res3(res); }while(0); sd_mmc_deselect(0); return ret; }