Difference between revisions of "Mmcsd.c"
From Mech
Jump to navigationJump to searchMat Kotowsky (talk | contribs) |
Mat Kotowsky (talk | contribs) |
||
| Line 1: | Line 1: | ||
<pre> |
<pre> |
||
///////////////////////////////////////////////////////////////////////// |
|||
//// MMCSD.c //// |
|||
//// //// |
|||
//// This is a low-level driver for MMC and SD cards. //// |
|||
//// //// |
|||
//// --User Functions-- //// |
|||
//// //// |
|||
//// mmcsd_init(): Initializes the media. //// |
|||
//// //// |
|||
//// mmcsd_read_byte(a, p) //// |
|||
//// Reads a byte from the MMC/SD card at location a, saves to //// |
|||
//// pointer p. Returns 0 if OK, non-zero if error. //// |
|||
//// //// |
|||
//// mmcsd_read_data(a, n, p) //// |
|||
//// Reads n bytes of data from the MMC/SD card starting at address //// |
|||
//// a, saves result to pointer p. Returns 0 if OK, non-zero if //// |
|||
//// error. //// |
|||
//// //// |
|||
//// mmcsd_flush_buffer() //// |
|||
//// The two user write functions (mmcsd_write_byte() and //// |
|||
//// mmcsd_write_data()) maintain a buffer to speed up the writing //// |
|||
//// process. Whenever a read or write is performed, the write //// |
|||
//// buffer is loaded with the specified page and only the //// |
|||
//// contents of this buffer is changed. If any future writes //// |
|||
//// cross a page boundary then the buffer in RAM is written //// |
|||
//// to the MMC/SD and then the next page is loaded into the //// |
|||
//// buffer. mmcsd_flush_buffer() forces the contents in RAM //// |
|||
//// to the MMC/SD card. Returns 0 if OK, non-zero if errror. //// |
|||
//// //// |
|||
//// mmcsd_write_byte(a, d) //// |
|||
//// Writes data byte d to the MMC/SD address a. Intelligently //// |
|||
//// manages a write buffer, therefore you may need to call //// |
|||
//// mmcsd_flush_buffer() to flush the buffer. //// |
|||
//// //// |
|||
//// mmcsd_write_data(a, n, p) //// |
|||
//// Writes n bytes of data from pointer p to the MMC/SD card //// |
|||
//// starting at address a. This function intelligently manages //// |
|||
//// a write buffer, therefore if you may need to call //// |
|||
//// mmcsd_flush_buffer() to flush any buffered characters. //// |
|||
//// returns 0 if OK, non-zero if error. //// |
|||
//// //// |
|||
//// mmcsd_read_block(a, s, p) //// |
|||
//// Reads an entire page from the SD/MMC. Keep in mind that the //// |
|||
//// start of the read has to be aligned to a block //// |
|||
//// (Address % 512 = 0). Therefore s must be evenly divisible by //// |
|||
//// 512. At the application level it is much more effecient to //// |
|||
//// to use mmcsd_read_data() or mmcsd_read_byte(). Returns 0 //// |
|||
//// if successful, non-zero if error. //// |
|||
//// //// |
|||
//// mmcsd_write_block(a, s, p): //// |
|||
//// Writes an entire page to the SD/MMC. This will write an //// |
|||
//// entire page to the SD/MMC, so the address and size must be //// |
|||
//// evenly divisble by 512. At the application level it is much //// |
|||
//// more effecient to use mmcsd_write_data() or mmcsd_write_byte().//// |
|||
//// Returns 0 if successful, non-zero if error. //// |
|||
//// //// |
|||
//// mmcsd_print_cid(): Displays all data in the Card Identification //// |
|||
//// Register. Note this only works on SD cards. //// |
|||
//// //// |
|||
//// mmcsd_print_csd(): Displays all data in the Card Specific Data //// |
|||
//// Register. Note this only works on SD cards. //// |
|||
//// //// |
|||
//// //// |
|||
//// --Non-User Functions-- //// |
|||
//// //// |
|||
//// mmcsd_go_idle_state(): Sends the GO_IDLE_STATE command to the //// |
|||
//// SD/MMC. //// |
|||
//// mmcsd_send_op_cond(): Sends the SEND_OP_COND command to the //// |
|||
//// SD. Note this command only works on SD. //// |
|||
//// mmcsd_send_if_cond(): Sends the SEND_IF_COND command to the //// |
|||
//// SD. Note this command only works on SD. //// |
|||
//// mmcsd_sd_status(): Sends the SD_STATUS command to the SD. Note //// |
|||
//// This command only works on SD cards. //// |
|||
//// mmcsd_send_status(): Sends the SEND_STATUS command to the //// |
|||
//// SD/MMC. //// |
|||
//// mmcsd_set_blocklen(): Sends the SET_BLOCKLEN command along with //// |
|||
//// the desired block length. //// |
|||
//// mmcsd_app_cmd(): Sends the APP_CMD command to the SD. This only //// |
|||
//// works on SD cards and is used just before any //// |
|||
//// SD-only command (e.g. send_op_cond()). //// |
|||
//// mmcsd_read_ocr(): Sends the READ_OCR command to the SD/MMC. //// |
|||
//// mmcsd_crc_on_off(): Sends the CRC_ON_OFF command to the SD/MMC //// |
|||
//// along with a bit to turn the CRC on/off. //// |
|||
//// mmcsd_send_cmd(): Sends a command and argument to the SD/MMC. //// |
|||
//// mmcsd_get_r1(): Waits for an R1 response from the SD/MMC and //// |
|||
//// then saves the response to a buffer. //// |
|||
//// mmcsd_get_r2(): Waits for an R2 response from the SD/MMC and //// |
|||
//// then saves the response to a buffer. //// |
|||
//// mmcsd_get_r3(): Waits for an R3 response from the SD/MMC and //// |
|||
//// then saves the response to a buffer. //// |
|||
//// mmcsd_get_r7(): Waits for an R7 response from the SD/MMC and //// |
|||
//// then saves the response to a buffer. //// |
|||
//// mmcsd_wait_for_token(): Waits for a specified token from the //// |
|||
//// SD/MMC. //// |
|||
//// mmcsd_crc7(): Generates a CRC7 using a pointer to some data, //// |
|||
//// and how many bytes long the data is. //// |
|||
//// mmcsd_crc16(): Generates a CRC16 using a pointer to some data, //// |
|||
//// and how many bytes long the data is. //// |
|||
//// //// |
|||
///////////////////////////////////////////////////////////////////////// |
|||
//// (C) Copyright 2007 Custom Computer Services //// |
|||
//// This source code may only be used by licensed users of the CCS //// |
|||
//// C compiler. This source code may only be distributed to other //// |
|||
//// licensed users of the CCS C compiler. No other use, //// |
|||
//// reproduction or distribution is permitted without written //// |
|||
//// permission. Derivative programs created using this software //// |
|||
//// in object code form are not restricted in any way. //// |
|||
///////////////////////////////////////////////////////////////////////// |
|||
#ifndef MMCSD_C |
|||
#define MMCSD_C |
|||
///////////////////// |
|||
//// //// |
|||
//// User Config //// |
|||
//// //// |
|||
///////////////////// |
|||
#ifndef MMCSD_PIN_SCL |
|||
#define MMCSD_PIN_SCL PIN_C3 //o |
|||
#define MMCSD_PIN_SDI PIN_C4 //i |
|||
#define MMCSD_PIN_SDO PIN_C5 //o |
|||
#define MMCSD_PIN_SELECT PIN_C2 //o |
|||
#endif |
|||
#use spi(MASTER, DI=MMCSD_PIN_SDI, DO=MMCSD_PIN_SDO, CLK=MMCSD_PIN_SCL, BITS=8, MSB_FIRST, IDLE=1, stream=mmcsd_spi) |
|||
//////////////////////// |
|||
//// //// |
|||
//// Useful Defines //// |
|||
//// //// |
|||
//////////////////////// |
|||
enum MMCSD_err |
|||
{MMCSD_GOODEC = 0, |
|||
MMCSD_IDLE = 0x01, |
|||
MMCSD_ERASE_RESET = 0x02, |
|||
MMCSD_ILLEGAL_CMD = 0x04, |
|||
MMCSD_CRC_ERR = 0x08, |
|||
MMCSD_ERASE_SEQ_ERR = 0x10, |
|||
MMCSD_ADDR_ERR = 0x20, |
|||
MMCSD_PARAM_ERR = 0x40, |
|||
RESP_TIMEOUT = 0x80}; |
|||
#define GO_IDLE_STATE 0 |
|||
#define SEND_OP_COND 1 |
|||
#define SEND_IF_COND 8 |
|||
#define SEND_CSD 9 |
|||
#define SEND_CID 10 |
|||
#define SD_STATUS 13 |
|||
#define SEND_STATUS 13 |
|||
#define SET_BLOCKLEN 16 |
|||
#define READ_SINGLE_BLOCK 17 |
|||
#define WRITE_BLOCK 24 |
|||
#define SD_SEND_OP_COND 41 |
|||
#define APP_CMD 55 |
|||
#define READ_OCR 58 |
|||
#define CRC_ON_OFF 59 |
|||
#define IDLE_TOKEN 0x01 |
|||
#define DATA_START_TOKEN 0xFE |
|||
#define MMCSD_MAX_BLOCK_SIZE 512 |
|||
//////////////////////// |
|||
/// /// |
|||
/// Global Variables /// |
|||
/// /// |
|||
//////////////////////// |
|||
int g_mmcsd_buffer[MMCSD_MAX_BLOCK_SIZE]; |
|||
int1 g_CRC_enabled; |
|||
int1 g_MMCSDBufferChanged; |
|||
int32 g_mmcsdBufferAddress; |
|||
enum _card_type{SD, MMC} g_card_type; |
|||
///////////////////////////// |
|||
//// //// |
|||
//// Function Prototypes //// |
|||
//// //// |
|||
///////////////////////////// |
|||
MMCSD_err mmcsd_init(); |
|||
MMCSD_err mmcsd_read_data(int32 address, int16 size, int* ptr); |
|||
MMCSD_err mmcsd_read_block(int32 address, int16 size, int* ptr); |
|||
MMCSD_err mmcsd_write_data(int32 address, int16 size, int* ptr); |
|||
MMCSD_err mmcsd_write_block(int32 address, int16 size, int* ptr); |
|||
MMCSD_err mmcsd_go_idle_state(void); |
|||
MMCSD_err mmcsd_send_op_cond(void); |
|||
MMCSD_err mmcsd_send_if_cond(int r7[]); |
|||
MMCSD_err mmcsd_print_csd(); |
|||
MMCSD_err mmcsd_print_cid(); |
|||
MMCSD_err mmcsd_sd_status(int r2[]); |
|||
MMCSD_err mmcsd_send_status(int r2[]); |
|||
MMCSD_err mmcsd_set_blocklen(int32 blocklen); |
|||
MMCSD_err mmcsd_read_single_block(int32 address); |
|||
MMCSD_err mmcsd_write_single_block(int32 address); |
|||
MMCSD_err mmcsd_sd_send_op_cond(void); |
|||
MMCSD_err mmcsd_app_cmd(void); |
|||
MMCSD_err mmcsd_read_ocr(int* r1); |
|||
MMCSD_err mmcsd_crc_on_off(int1 crc_enabled); |
|||
MMCSD_err mmcsd_send_cmd(int cmd, int32 arg); |
|||
MMCSD_err mmcsd_get_r1(void); |
|||
MMCSD_err mmcsd_get_r2(int r2[]); |
|||
MMCSD_err mmcsd_get_r3(int r3[]); |
|||
MMCSD_err mmcsd_get_r7(int r7[]); |
|||
MMCSD_err mmcsd_wait_for_token(int token); |
|||
unsigned int8 mmcsd_crc7(char *data, unsigned int8 length); |
|||
unsigned int16 mmcsd_crc16(char *data, unsigned int8 length); |
|||
void mmcsd_select(); |
|||
void mmcsd_deselect(); |
|||
/// Fast Functions ! /// |
|||
MMCSD_err mmcsd_load_buffer(void); |
|||
MMCSD_err mmcsd_flush_buffer(void); |
|||
MMCSD_err mmcsd_move_buffer(int32 new_addr); |
|||
MMCSD_err mmcsd_read_byte(int32 addr, char* data); |
|||
MMCSD_err mmcsd_write_byte(int32 addr, char data); |
|||
////////////////////////////////// |
|||
//// //// |
|||
//// Function Implementations //// |
|||
//// //// |
|||
////////////////////////////////// |
|||
MMCSD_err mmcsd_init() |
|||
{ |
|||
int |
|||
i, |
|||
r1; |
|||
g_CRC_enabled = TRUE; |
|||
g_mmcsdBufferAddress = 0; |
|||
output_drive(MMCSD_PIN_SCL); |
|||
output_drive(MMCSD_PIN_SDO); |
|||
output_drive(MMCSD_PIN_SELECT); |
|||
output_float(MMCSD_PIN_SDI); |
|||
mmcsd_deselect(); |
|||
delay_ms(15); |
|||
/* begin initialization */ |
|||
i = 0; |
|||
do |
|||
{ |
|||
delay_ms(1); |
|||
mmcsd_select(); |
|||
r1=mmcsd_go_idle_state(); |
|||
mmcsd_deselect(); |
|||
i++; |
|||
if(i == 0xFF) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return r1; |
|||
} |
|||
} while(!bit_test(r1, 0)); |
|||
i = 0; |
|||
do |
|||
{ |
|||
delay_ms(1); |
|||
mmcsd_select(); |
|||
r1=mmcsd_send_op_cond(); |
|||
mmcsd_deselect(); |
|||
i++; |
|||
if(i == 0xFF) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return r1; |
|||
} |
|||
} while(r1 & MMCSD_IDLE); |
|||
/* figure out if we have an SD or MMC */ |
|||
mmcsd_select(); |
|||
r1=mmcsd_app_cmd(); |
|||
r1=mmcsd_sd_send_op_cond(); |
|||
mmcsd_deselect(); |
|||
/* an mmc will return an 0x04 here */ |
|||
if(r1 == 0x04) |
|||
g_card_type = MMC; |
|||
else |
|||
g_card_type = SD; |
|||
/* set block length to 512 bytes */ |
|||
mmcsd_select(); |
|||
r1 = mmcsd_set_blocklen(MMCSD_MAX_BLOCK_SIZE); |
|||
if(r1 != MMCSD_GOODEC) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return r1; |
|||
} |
|||
mmcsd_deselect(); |
|||
/* turn CRCs off to speed up reading/writing */ |
|||
mmcsd_select(); |
|||
r1 = mmcsd_crc_on_off(0); |
|||
if(r1 != MMCSD_GOODEC) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return r1; |
|||
} |
|||
mmcsd_deselect(); |
|||
r1 = mmcsd_load_buffer(); |
|||
return r1; |
|||
} |
|||
MMCSD_err mmcsd_read_data(int32 address, int16 size, int* ptr) |
|||
{ |
|||
MMCSD_err r1; |
|||
int16 i; // counter for loops |
|||
for(i = 0; i < size; i++) |
|||
{ |
|||
r1 = mmcsd_read_byte(address++, ptr++); |
|||
if(r1 != MMCSD_GOODEC) |
|||
return r1; |
|||
} |
|||
return MMCSD_GOODEC; |
|||
} |
|||
MMCSD_err mmcsd_read_block(int32 address, int16 size, int* ptr) |
|||
{ |
|||
MMCSD_err ec; |
|||
int16 i; // counter for loops |
|||
// send command |
|||
mmcsd_select(); |
|||
ec = mmcsd_read_single_block(address); |
|||
if(ec != MMCSD_GOODEC) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return ec; |
|||
} |
|||
// wait for the data start token |
|||
ec = mmcsd_wait_for_token(DATA_START_TOKEN); |
|||
if(ec != MMCSD_GOODEC) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return ec; |
|||
} |
|||
// read in the data |
|||
for(i = 0; i < size; i += 1) |
|||
ptr[i] = spi_xfer(mmcsd_spi, 0xFF); |
|||
if(g_CRC_enabled) |
|||
{ |
|||
/* check the crc */ |
|||
if(make16(spi_xfer(mmcsd_spi, 0xFF), spi_xfer(mmcsd_spi, 0xFF)) != mmcsd_crc16(g_mmcsd_buffer, MMCSD_MAX_BLOCK_SIZE)) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return MMCSD_CRC_ERR; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
/* have the card transmit the CRC, but ignore it */ |
|||
spi_xfer(mmcsd_spi, 0xFF); |
|||
spi_xfer(mmcsd_spi, 0xFF); |
|||
} |
|||
mmcsd_deselect(); |
|||
return MMCSD_GOODEC; |
|||
} |
|||
MMCSD_err mmcsd_write_data(int32 address, int16 size, int* ptr) |
|||
{ |
|||
MMCSD_err ec; |
|||
int16 i; // counter for loops |
|||
for(i = 0; i < size; i++) |
|||
{ |
|||
ec = mmcsd_write_byte(address++, *ptr++); |
|||
if(ec != MMCSD_GOODEC) |
|||
return ec; |
|||
} |
|||
return MMCSD_GOODEC; |
|||
} |
|||
MMCSD_err mmcsd_write_block(int32 address, int16 size, int* ptr) |
|||
{ |
|||
MMCSD_err ec; |
|||
int16 i; |
|||
// send command |
|||
mmcsd_select(); |
|||
ec = mmcsd_write_single_block(address); |
|||
if(ec != MMCSD_GOODEC) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return ec; |
|||
} |
|||
// send a data start token |
|||
spi_xfer(mmcsd_spi, DATA_START_TOKEN); |
|||
// send all the data |
|||
for(i = 0; i < size; i += 1) |
|||
spi_xfer(mmcsd_spi, ptr[i]); |
|||
// if the CRC is enabled we have to calculate it, otherwise just send an 0xFFFF |
|||
if(g_CRC_enabled) |
|||
spi_xfer(mmcsd_spi, mmcsd_crc16(ptr, size)); |
|||
else |
|||
{ |
|||
spi_xfer(mmcsd_spi, 0xFF); |
|||
spi_xfer(mmcsd_spi, 0xFF); |
|||
} |
|||
// get the error code back from the card; "data accepted" is 0bXXX00101 |
|||
ec = mmcsd_get_r1(); |
|||
if(ec & 0x0A) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return ec; |
|||
} |
|||
// wait for the line to go back high, this indicates that the write is complete |
|||
while(spi_xfer(mmcsd_spi, 0xFF) == 0); |
|||
mmcsd_deselect(); |
|||
return MMCSD_GOODEC; |
|||
} |
|||
MMCSD_err mmcsd_go_idle_state(void) |
|||
{ |
|||
mmcsd_send_cmd(GO_IDLE_STATE, 0); |
|||
return mmcsd_get_r1(); |
|||
} |
|||
MMCSD_err mmcsd_send_op_cond(void) |
|||
{ |
|||
mmcsd_send_cmd(SEND_OP_COND, 0); |
|||
return mmcsd_get_r1(); |
|||
} |
|||
MMCSD_err mmcsd_send_if_cond(int r7[]) |
|||
{ |
|||
mmcsd_send_cmd(SEND_IF_COND, 0x45A); |
|||
return mmcsd_get_r7(r7); |
|||
} |
|||
MMCSD_err mmcsd_print_csd() |
|||
{ |
|||
int |
|||
buf[16], |
|||
i, |
|||
r1; |
|||
// MMCs don't support this command |
|||
if(g_card_type == MMC) |
|||
return MMCSD_PARAM_ERR; |
|||
mmcsd_select(); |
|||
mmcsd_send_cmd(SEND_CSD, 0); |
|||
r1 = mmcsd_get_r1(); |
|||
if(r1 != MMCSD_GOODEC) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return r1; |
|||
} |
|||
r1 = mmcsd_wait_for_token(DATA_START_TOKEN); |
|||
if(r1 != MMCSD_GOODEC) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return r1; |
|||
} |
|||
for(i = 0; i < 16; i++) |
|||
buf[i] = spi_xfer(mmcsd_spi, 0xFF); |
|||
mmcsd_deselect(); |
|||
printf("\r\nCSD_STRUCTURE: %X", (buf[0] & 0x0C) >> 2); |
|||
printf("\r\nTAAC: %X", buf[1]); |
|||
printf("\r\nNSAC: %X", buf[2]); |
|||
printf("\r\nTRAN_SPEED: %X", buf[3]); |
|||
printf("\r\nCCC: %lX", (make16(buf[4], buf[5]) & 0xFFF0) >> 4); |
|||
printf("\r\nREAD_BL_LEN: %X", buf[5] & 0x0F); |
|||
printf("\r\nREAD_BL_PARTIAL: %X", (buf[6] & 0x80) >> 7); |
|||
printf("\r\nWRITE_BLK_MISALIGN: %X", (buf[6] & 0x40) >> 6); |
|||
printf("\r\nREAD_BLK_MISALIGN: %X", (buf[6] & 0x20) >> 5); |
|||
printf("\r\nDSR_IMP: %X", (buf[6] & 0x10) >> 4); |
|||
printf("\r\nC_SIZE: %lX", (((buf[6] & 0x03) << 10) | (buf[7] << 2) | ((buf[8] & 0xC0) >> 6))); |
|||
printf("\r\nVDD_R_CURR_MIN: %X", (buf[8] & 0x38) >> 3); |
|||
printf("\r\nVDD_R_CURR_MAX: %X", buf[8] & 0x07); |
|||
printf("\r\nVDD_W_CURR_MIN: %X", (buf[9] & 0xE0) >> 5); |
|||
printf("\r\nVDD_W_CURR_MAX: %X", (buf[9] & 0x1C) >> 2); |
|||
printf("\r\nC_SIZE_MULT: %X", ((buf[9] & 0x03) << 1) | ((buf[10] & 0x80) >> 7)); |
|||
printf("\r\nERASE_BLK_EN: %X", (buf[10] & 0x40) >> 6); |
|||
printf("\r\nSECTOR_SIZE: %X", ((buf[10] & 0x3F) << 1) | ((buf[11] & 0x80) >> 7)); |
|||
printf("\r\nWP_GRP_SIZE: %X", buf[11] & 0x7F); |
|||
printf("\r\nWP_GRP_ENABLE: %X", (buf[12] & 0x80) >> 7); |
|||
printf("\r\nR2W_FACTOR: %X", (buf[12] & 0x1C) >> 2); |
|||
printf("\r\nWRITE_BL_LEN: %X", ((buf[12] & 0x03) << 2) | ((buf[13] & 0xC0) >> 6)); |
|||
printf("\r\nWRITE_BL_PARTIAL: %X", (buf[13] & 0x20) >> 5); |
|||
printf("\r\nFILE_FORMAT_GRP: %X", (buf[14] & 0x80) >> 7); |
|||
printf("\r\nCOPY: %X", (buf[14] & 0x40) >> 6); |
|||
printf("\r\nPERM_WRITE_PROTECT: %X", (buf[14] & 0x20) >> 5); |
|||
printf("\r\nTMP_WRITE_PROTECT: %X", (buf[14] & 0x10) >> 4); |
|||
printf("\r\nFILE_FORMAT: %X", (buf[14] & 0x0C) >> 2); |
|||
printf("\r\nCRC: %X", buf[15]); |
|||
return r1; |
|||
} |
|||
MMCSD_err mmcsd_print_cid() |
|||
{ |
|||
int |
|||
buf[16], |
|||
i, |
|||
r1; |
|||
// MMCs don't support this command |
|||
if(g_card_type == MMC) |
|||
return MMCSD_PARAM_ERR; |
|||
mmcsd_select(); |
|||
mmcsd_send_cmd(SEND_CID, 0); |
|||
r1 = mmcsd_get_r1(); |
|||
if(r1 != MMCSD_GOODEC) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return r1; |
|||
} |
|||
r1 = mmcsd_wait_for_token(DATA_START_TOKEN); |
|||
if(r1 != MMCSD_GOODEC) |
|||
{ |
|||
mmcsd_deselect(); |
|||
return r1; |
|||
} |
|||
for(i = 0; i < 16; i++) |
|||
buf[i] = spi_xfer(mmcsd_spi, 0xFF); |
|||
mmcsd_deselect(); |
|||
printf("\r\nManufacturer ID: %X", buf[0]); |
|||
printf("\r\nOEM/Application ID: %c%c", buf[1], buf[2]); |
|||
printf("\r\nOEM/Application ID: %c%c%c%c%c", buf[3], buf[4], buf[5], buf[6], buf[7]); |
|||
printf("\r\nProduct Revision: %X", buf[8]); |
|||
printf("\r\nSerial Number: %X%X%X%X", buf[9], buf[10], buf[11], buf[12]); |
|||
printf("\r\nManufacturer Date Code: %X%X", buf[13] & 0x0F, buf[14]); |
|||
printf("\r\nCRC-7 Checksum: %X", buf[15]); |
|||
return r1; |
|||
} |
|||
MMCSD_err mmcsd_sd_status(int r2[]) |
|||
{ |
|||
int i; |
|||
mmcsd_select(); |
|||
mmcsd_send_cmd(APP_CMD, 0); |
|||
r2[0]=mmcsd_get_r1(); |
|||
mmcsd_deselect(); |
|||
mmcsd_select(); |
|||
mmcsd_send_cmd(SD_STATUS, 0); |
|||
for(i = 0; i < 64; i++) |
|||
spi_xfer(mmcsd_spi, 0xFF); |
|||
mmcsd_deselect(); |
|||
return mmcsd_get_r2(r2); |
|||
} |
|||
MMCSD_err mmcsd_send_status(int r2[]) |
|||
{ |
|||
mmcsd_send_cmd(SEND_STATUS, 0); |
|||
return mmcsd_get_r2(r2); |
|||
} |
|||
MMCSD_err mmcsd_set_blocklen(int32 blocklen) |
|||
{ |
|||
mmcsd_send_cmd(SET_BLOCKLEN, blocklen); |
|||
return mmcsd_get_r1(); |
|||
} |
|||
MMCSD_err mmcsd_read_single_block(int32 address) |
|||
{ |
|||
mmcsd_send_cmd(READ_SINGLE_BLOCK, address); |
|||
return mmcsd_get_r1(); |
|||
} |
|||
MMCSD_err mmcsd_write_single_block(int32 address) |
|||
{ |
|||
mmcsd_send_cmd(WRITE_BLOCK, address); |
|||
return mmcsd_get_r1(); |
|||
} |
|||
MMCSD_err mmcsd_sd_send_op_cond(void) |
|||
{ |
|||
mmcsd_send_cmd(SD_SEND_OP_COND, 0); |
|||
return mmcsd_get_r1(); |
|||
} |
|||
MMCSD_err mmcsd_app_cmd(void) |
|||
{ |
|||
mmcsd_send_cmd(APP_CMD, 0); |
|||
return mmcsd_get_r1(); |
|||
} |
|||
MMCSD_err mmcsd_read_ocr(int r3[]) |
|||
{ |
|||
mmcsd_send_cmd(READ_OCR, 0); |
|||
return mmcsd_get_r3(r3); |
|||
} |
|||
MMCSD_err mmcsd_crc_on_off(int1 crc_enabled) |
|||
{ |
|||
mmcsd_send_cmd(CRC_ON_OFF, crc_enabled); |
|||
g_CRC_enabled = crc_enabled; |
|||
return mmcsd_get_r1(); |
|||
} |
|||
MMCSD_err mmcsd_send_cmd(int cmd, int32 arg) |
|||
{ |
|||
int packet[6]; // the entire command, argument, and crc in one variable |
|||
// construct the packet |
|||
// every command on an SD card is or'ed with 0x40 |
|||
packet[0] = cmd | 0x40; |
|||
packet[1] = make8(arg, 3); |
|||
packet[2] = make8(arg, 2); |
|||
packet[3] = make8(arg, 1); |
|||
packet[4] = make8(arg, 0); |
|||
// calculate the crc if needed |
|||
if(g_CRC_enabled) |
|||
packet[5] = mmcsd_crc7(packet, 5); |
|||
else |
|||
packet[5] = 0xFF; |
|||
// transfer the command and argument, with an extra 0xFF hacked in there |
|||
spi_xfer(mmcsd_spi, packet[0]); |
|||
spi_xfer(mmcsd_spi, packet[1]); |
|||
spi_xfer(mmcsd_spi, packet[2]); |
|||
spi_xfer(mmcsd_spi, packet[3]); |
|||
spi_xfer(mmcsd_spi, packet[4]); |
|||
spi_xfer(mmcsd_spi, packet[5]); |
|||
return MMCSD_GOODEC; |
|||
} |
|||
MMCSD_err mmcsd_get_r1(void) |
|||
{ |
|||
int |
|||
response = 0, // place to hold the response coming back from the SPI line |
|||
timeout = 0xFF; // maximum amount loops to wait for idle before getting impatient and leaving the function with an error code |
|||
// loop until timeout == 0 |
|||
while(timeout) |
|||
{ |
|||
// read what's on the SPI line |
|||
// the SD/MMC requires that you leave the line high when you're waiting for data from it |
|||
response = spi_xfer(mmcsd_spi, 0xFF); |
|||
// check to see if we got a response |
|||
if(response != 0xFF) |
|||
{ |
|||
// fill in the response that we got and leave the function |
|||
return response; |
|||
} |
|||
// wait for a little bit longer |
|||
timeout--; |
|||
} |
|||
// for some reason, we didn't get a response back from the card |
|||
// return the proper error codes |
|||
return RESP_TIMEOUT; |
|||
} |
|||
MMCSD_err mmcsd_get_r2(int r2[]) |
|||
{ |
|||
r2[1] = mmcsd_get_r1(); |
|||
r2[0] = spi_xfer(mmcsd_spi, 0xFF); |
|||
return 0; |
|||
} |
|||
MMCSD_err mmcsd_get_r3(int r3[]) |
|||
{ |
|||
return mmcsd_get_r7(r3); |
|||
} |
|||
MMCSD_err mmcsd_get_r7(int r7[]) |
|||
{ |
|||
int i; // counter for loop |
|||
// the top byte of r7 is r1 |
|||
r7[4]=mmcsd_get_r1(); |
|||
// fill in the other 4 bytes |
|||
for(i = 0; i < 4; i++) |
|||
r7[3 - i] = spi_xfer(mmcsd_spi, 0xFF); |
|||
return r7[4]; |
|||
} |
|||
MMCSD_err mmcsd_wait_for_token(int token) |
|||
{ |
|||
MMCSD_err r1; |
|||
// get a token |
|||
r1 = mmcsd_get_r1(); |
|||
// check to see if the token we recieved was the one that we were looking for |
|||
if(r1 == token) |
|||
return MMCSD_GOODEC; |
|||
// if that wasn't right, return the error |
|||
return r1; |
|||
} |
|||
unsigned int8 mmcsd_crc7(char *data, unsigned int8 length) |
|||
{ |
|||
unsigned int8 i, ibit, c, crc; |
|||
crc = 0x00; // Set initial value |
|||
for (i = 0; i < length; i++, data++) |
|||
{ |
|||
c = *data; |
|||
for (ibit = 0; ibit < 8; ibit++) |
|||
{ |
|||
crc = crc << 1; |
|||
if ((c ^ crc) & 0x80) crc = crc ^ 0x09; // ^ is XOR |
|||
c = c << 1; |
|||
} |
|||
crc = crc & 0x7F; |
|||
} |
|||
shift_left(&crc, 1, 1); // MMC card stores the result in the top 7 bits so shift them left 1 |
|||
// Should shift in a 1 not a 0 as one of the cards I have won't work otherwise |
|||
return crc; |
|||
} |
|||
unsigned int16 mmcsd_crc16(char *data, unsigned int8 length) |
|||
{ |
|||
unsigned int8 i, ibit, c; |
|||
unsigned int16 crc; |
|||
crc = 0x0000; // Set initial value |
|||
for (i = 0; i < length; i++, data++) |
|||
{ |
|||
c = *data; |
|||
for (ibit = 0; ibit < 8; ibit++) |
|||
{ |
|||
crc = crc << 1; |
|||
if ((c ^ crc) & 0x8000) crc = crc ^ 0x1021; // ^ is XOR |
|||
c = c << 1; |
|||
} |
|||
crc = crc & 0x7FFF; |
|||
} |
|||
shift_left(&crc, 2, 1); // MMC card stores the result in the top 7 bits so shift them left 1 |
|||
// Should shift in a 1 not a 0 as one of the cards I have won't work otherwise |
|||
return crc; |
|||
} |
|||
void mmcsd_select() |
|||
{ |
|||
output_low(MMCSD_PIN_SELECT); |
|||
} |
|||
void mmcsd_deselect() |
|||
{ |
|||
spi_xfer(mmcsd_spi, 0xFF); |
|||
output_high(MMCSD_PIN_SELECT); |
|||
} |
|||
MMCSD_err mmcsd_load_buffer(void) |
|||
{ |
|||
g_MMCSDBufferChanged = FALSE; |
|||
return(mmcsd_read_block(g_mmcsdBufferAddress, MMCSD_MAX_BLOCK_SIZE, g_mmcsd_buffer)); |
|||
} |
|||
MMCSD_err mmcsd_flush_buffer(void) |
|||
{ |
|||
if (g_MMCSDBufferChanged) |
|||
{ |
|||
g_MMCSDBufferChanged = FALSE; |
|||
return(mmcsd_write_block(g_mmcsdBufferAddress, MMCSD_MAX_BLOCK_SIZE, g_mmcsd_buffer)); |
|||
} |
|||
return(0); //ok |
|||
} |
|||
MMCSD_err mmcsd_move_buffer(int32 new_addr) |
|||
{ |
|||
MMCSD_err ec = MMCSD_GOODEC; |
|||
int32 |
|||
//cur_block, |
|||
new_block; |
|||
// make sure we're still on the same block |
|||
//cur_block = g_mmcsdBufferAddress - (g_mmcsdBufferAddress % MMCSD_MAX_BLOCK_SIZE); |
|||
new_block = new_addr - (new_addr % MMCSD_MAX_BLOCK_SIZE); |
|||
//if(cur_block != new_block) |
|||
if(g_mmcsdBufferAddress != new_block) |
|||
{ |
|||
// dump the old buffer |
|||
if (g_MMCSDBufferChanged) |
|||
{ |
|||
ec = mmcsd_flush_buffer(); |
|||
if(ec != MMCSD_GOODEC) |
|||
return ec; |
|||
g_MMCSDBufferChanged = FALSE; |
|||
} |
|||
// figure out the best place for a block |
|||
g_mmcsdBufferAddress = new_block; |
|||
// load up a new buffer |
|||
ec = mmcsd_load_buffer(); |
|||
} |
|||
return ec; |
|||
} |
|||
MMCSD_err mmcsd_read_byte(int32 addr, char* data) |
|||
{ |
|||
MMCSD_err ec; |
|||
ec = mmcsd_move_buffer(addr); |
|||
if(ec != MMCSD_GOODEC) |
|||
{ |
|||
return ec; |
|||
} |
|||
*data = g_mmcsd_buffer[addr % MMCSD_MAX_BLOCK_SIZE]; |
|||
return MMCSD_GOODEC; |
|||
} |
|||
MMCSD_err mmcsd_write_byte(int32 addr, char data) |
|||
{ |
|||
MMCSD_err ec; |
|||
ec = mmcsd_move_buffer(addr); |
|||
if(ec != MMCSD_GOODEC) |
|||
return ec; |
|||
g_mmcsd_buffer[addr % MMCSD_MAX_BLOCK_SIZE] = data; |
|||
g_MMCSDBufferChanged = TRUE; |
|||
return MMCSD_GOODEC; |
|||
} |
|||
#endif |
|||
</pre> |
</pre> |
||
Latest revision as of 17:57, 11 February 2009
/////////////////////////////////////////////////////////////////////////
//// MMCSD.c ////
//// ////
//// This is a low-level driver for MMC and SD cards. ////
//// ////
//// --User Functions-- ////
//// ////
//// mmcsd_init(): Initializes the media. ////
//// ////
//// mmcsd_read_byte(a, p) ////
//// Reads a byte from the MMC/SD card at location a, saves to ////
//// pointer p. Returns 0 if OK, non-zero if error. ////
//// ////
//// mmcsd_read_data(a, n, p) ////
//// Reads n bytes of data from the MMC/SD card starting at address ////
//// a, saves result to pointer p. Returns 0 if OK, non-zero if ////
//// error. ////
//// ////
//// mmcsd_flush_buffer() ////
//// The two user write functions (mmcsd_write_byte() and ////
//// mmcsd_write_data()) maintain a buffer to speed up the writing ////
//// process. Whenever a read or write is performed, the write ////
//// buffer is loaded with the specified page and only the ////
//// contents of this buffer is changed. If any future writes ////
//// cross a page boundary then the buffer in RAM is written ////
//// to the MMC/SD and then the next page is loaded into the ////
//// buffer. mmcsd_flush_buffer() forces the contents in RAM ////
//// to the MMC/SD card. Returns 0 if OK, non-zero if errror. ////
//// ////
//// mmcsd_write_byte(a, d) ////
//// Writes data byte d to the MMC/SD address a. Intelligently ////
//// manages a write buffer, therefore you may need to call ////
//// mmcsd_flush_buffer() to flush the buffer. ////
//// ////
//// mmcsd_write_data(a, n, p) ////
//// Writes n bytes of data from pointer p to the MMC/SD card ////
//// starting at address a. This function intelligently manages ////
//// a write buffer, therefore if you may need to call ////
//// mmcsd_flush_buffer() to flush any buffered characters. ////
//// returns 0 if OK, non-zero if error. ////
//// ////
//// mmcsd_read_block(a, s, p) ////
//// Reads an entire page from the SD/MMC. Keep in mind that the ////
//// start of the read has to be aligned to a block ////
//// (Address % 512 = 0). Therefore s must be evenly divisible by ////
//// 512. At the application level it is much more effecient to ////
//// to use mmcsd_read_data() or mmcsd_read_byte(). Returns 0 ////
//// if successful, non-zero if error. ////
//// ////
//// mmcsd_write_block(a, s, p): ////
//// Writes an entire page to the SD/MMC. This will write an ////
//// entire page to the SD/MMC, so the address and size must be ////
//// evenly divisble by 512. At the application level it is much ////
//// more effecient to use mmcsd_write_data() or mmcsd_write_byte().////
//// Returns 0 if successful, non-zero if error. ////
//// ////
//// mmcsd_print_cid(): Displays all data in the Card Identification ////
//// Register. Note this only works on SD cards. ////
//// ////
//// mmcsd_print_csd(): Displays all data in the Card Specific Data ////
//// Register. Note this only works on SD cards. ////
//// ////
//// ////
//// --Non-User Functions-- ////
//// ////
//// mmcsd_go_idle_state(): Sends the GO_IDLE_STATE command to the ////
//// SD/MMC. ////
//// mmcsd_send_op_cond(): Sends the SEND_OP_COND command to the ////
//// SD. Note this command only works on SD. ////
//// mmcsd_send_if_cond(): Sends the SEND_IF_COND command to the ////
//// SD. Note this command only works on SD. ////
//// mmcsd_sd_status(): Sends the SD_STATUS command to the SD. Note ////
//// This command only works on SD cards. ////
//// mmcsd_send_status(): Sends the SEND_STATUS command to the ////
//// SD/MMC. ////
//// mmcsd_set_blocklen(): Sends the SET_BLOCKLEN command along with ////
//// the desired block length. ////
//// mmcsd_app_cmd(): Sends the APP_CMD command to the SD. This only ////
//// works on SD cards and is used just before any ////
//// SD-only command (e.g. send_op_cond()). ////
//// mmcsd_read_ocr(): Sends the READ_OCR command to the SD/MMC. ////
//// mmcsd_crc_on_off(): Sends the CRC_ON_OFF command to the SD/MMC ////
//// along with a bit to turn the CRC on/off. ////
//// mmcsd_send_cmd(): Sends a command and argument to the SD/MMC. ////
//// mmcsd_get_r1(): Waits for an R1 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_get_r2(): Waits for an R2 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_get_r3(): Waits for an R3 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_get_r7(): Waits for an R7 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_wait_for_token(): Waits for a specified token from the ////
//// SD/MMC. ////
//// mmcsd_crc7(): Generates a CRC7 using a pointer to some data, ////
//// and how many bytes long the data is. ////
//// mmcsd_crc16(): Generates a CRC16 using a pointer to some data, ////
//// and how many bytes long the data is. ////
//// ////
/////////////////////////////////////////////////////////////////////////
//// (C) Copyright 2007 Custom Computer Services ////
//// This source code may only be used by licensed users of the CCS ////
//// C compiler. This source code may only be distributed to other ////
//// licensed users of the CCS C compiler. No other use, ////
//// reproduction or distribution is permitted without written ////
//// permission. Derivative programs created using this software ////
//// in object code form are not restricted in any way. ////
/////////////////////////////////////////////////////////////////////////
#ifndef MMCSD_C
#define MMCSD_C
/////////////////////
//// ////
//// User Config ////
//// ////
/////////////////////
#ifndef MMCSD_PIN_SCL
#define MMCSD_PIN_SCL PIN_C3 //o
#define MMCSD_PIN_SDI PIN_C4 //i
#define MMCSD_PIN_SDO PIN_C5 //o
#define MMCSD_PIN_SELECT PIN_C2 //o
#endif
#use spi(MASTER, DI=MMCSD_PIN_SDI, DO=MMCSD_PIN_SDO, CLK=MMCSD_PIN_SCL, BITS=8, MSB_FIRST, IDLE=1, stream=mmcsd_spi)
////////////////////////
//// ////
//// Useful Defines ////
//// ////
////////////////////////
enum MMCSD_err
{MMCSD_GOODEC = 0,
MMCSD_IDLE = 0x01,
MMCSD_ERASE_RESET = 0x02,
MMCSD_ILLEGAL_CMD = 0x04,
MMCSD_CRC_ERR = 0x08,
MMCSD_ERASE_SEQ_ERR = 0x10,
MMCSD_ADDR_ERR = 0x20,
MMCSD_PARAM_ERR = 0x40,
RESP_TIMEOUT = 0x80};
#define GO_IDLE_STATE 0
#define SEND_OP_COND 1
#define SEND_IF_COND 8
#define SEND_CSD 9
#define SEND_CID 10
#define SD_STATUS 13
#define SEND_STATUS 13
#define SET_BLOCKLEN 16
#define READ_SINGLE_BLOCK 17
#define WRITE_BLOCK 24
#define SD_SEND_OP_COND 41
#define APP_CMD 55
#define READ_OCR 58
#define CRC_ON_OFF 59
#define IDLE_TOKEN 0x01
#define DATA_START_TOKEN 0xFE
#define MMCSD_MAX_BLOCK_SIZE 512
////////////////////////
/// ///
/// Global Variables ///
/// ///
////////////////////////
int g_mmcsd_buffer[MMCSD_MAX_BLOCK_SIZE];
int1 g_CRC_enabled;
int1 g_MMCSDBufferChanged;
int32 g_mmcsdBufferAddress;
enum _card_type{SD, MMC} g_card_type;
/////////////////////////////
//// ////
//// Function Prototypes ////
//// ////
/////////////////////////////
MMCSD_err mmcsd_init();
MMCSD_err mmcsd_read_data(int32 address, int16 size, int* ptr);
MMCSD_err mmcsd_read_block(int32 address, int16 size, int* ptr);
MMCSD_err mmcsd_write_data(int32 address, int16 size, int* ptr);
MMCSD_err mmcsd_write_block(int32 address, int16 size, int* ptr);
MMCSD_err mmcsd_go_idle_state(void);
MMCSD_err mmcsd_send_op_cond(void);
MMCSD_err mmcsd_send_if_cond(int r7[]);
MMCSD_err mmcsd_print_csd();
MMCSD_err mmcsd_print_cid();
MMCSD_err mmcsd_sd_status(int r2[]);
MMCSD_err mmcsd_send_status(int r2[]);
MMCSD_err mmcsd_set_blocklen(int32 blocklen);
MMCSD_err mmcsd_read_single_block(int32 address);
MMCSD_err mmcsd_write_single_block(int32 address);
MMCSD_err mmcsd_sd_send_op_cond(void);
MMCSD_err mmcsd_app_cmd(void);
MMCSD_err mmcsd_read_ocr(int* r1);
MMCSD_err mmcsd_crc_on_off(int1 crc_enabled);
MMCSD_err mmcsd_send_cmd(int cmd, int32 arg);
MMCSD_err mmcsd_get_r1(void);
MMCSD_err mmcsd_get_r2(int r2[]);
MMCSD_err mmcsd_get_r3(int r3[]);
MMCSD_err mmcsd_get_r7(int r7[]);
MMCSD_err mmcsd_wait_for_token(int token);
unsigned int8 mmcsd_crc7(char *data, unsigned int8 length);
unsigned int16 mmcsd_crc16(char *data, unsigned int8 length);
void mmcsd_select();
void mmcsd_deselect();
/// Fast Functions ! ///
MMCSD_err mmcsd_load_buffer(void);
MMCSD_err mmcsd_flush_buffer(void);
MMCSD_err mmcsd_move_buffer(int32 new_addr);
MMCSD_err mmcsd_read_byte(int32 addr, char* data);
MMCSD_err mmcsd_write_byte(int32 addr, char data);
//////////////////////////////////
//// ////
//// Function Implementations ////
//// ////
//////////////////////////////////
MMCSD_err mmcsd_init()
{
int
i,
r1;
g_CRC_enabled = TRUE;
g_mmcsdBufferAddress = 0;
output_drive(MMCSD_PIN_SCL);
output_drive(MMCSD_PIN_SDO);
output_drive(MMCSD_PIN_SELECT);
output_float(MMCSD_PIN_SDI);
mmcsd_deselect();
delay_ms(15);
/* begin initialization */
i = 0;
do
{
delay_ms(1);
mmcsd_select();
r1=mmcsd_go_idle_state();
mmcsd_deselect();
i++;
if(i == 0xFF)
{
mmcsd_deselect();
return r1;
}
} while(!bit_test(r1, 0));
i = 0;
do
{
delay_ms(1);
mmcsd_select();
r1=mmcsd_send_op_cond();
mmcsd_deselect();
i++;
if(i == 0xFF)
{
mmcsd_deselect();
return r1;
}
} while(r1 & MMCSD_IDLE);
/* figure out if we have an SD or MMC */
mmcsd_select();
r1=mmcsd_app_cmd();
r1=mmcsd_sd_send_op_cond();
mmcsd_deselect();
/* an mmc will return an 0x04 here */
if(r1 == 0x04)
g_card_type = MMC;
else
g_card_type = SD;
/* set block length to 512 bytes */
mmcsd_select();
r1 = mmcsd_set_blocklen(MMCSD_MAX_BLOCK_SIZE);
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
mmcsd_deselect();
/* turn CRCs off to speed up reading/writing */
mmcsd_select();
r1 = mmcsd_crc_on_off(0);
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
mmcsd_deselect();
r1 = mmcsd_load_buffer();
return r1;
}
MMCSD_err mmcsd_read_data(int32 address, int16 size, int* ptr)
{
MMCSD_err r1;
int16 i; // counter for loops
for(i = 0; i < size; i++)
{
r1 = mmcsd_read_byte(address++, ptr++);
if(r1 != MMCSD_GOODEC)
return r1;
}
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_read_block(int32 address, int16 size, int* ptr)
{
MMCSD_err ec;
int16 i; // counter for loops
// send command
mmcsd_select();
ec = mmcsd_read_single_block(address);
if(ec != MMCSD_GOODEC)
{
mmcsd_deselect();
return ec;
}
// wait for the data start token
ec = mmcsd_wait_for_token(DATA_START_TOKEN);
if(ec != MMCSD_GOODEC)
{
mmcsd_deselect();
return ec;
}
// read in the data
for(i = 0; i < size; i += 1)
ptr[i] = spi_xfer(mmcsd_spi, 0xFF);
if(g_CRC_enabled)
{
/* check the crc */
if(make16(spi_xfer(mmcsd_spi, 0xFF), spi_xfer(mmcsd_spi, 0xFF)) != mmcsd_crc16(g_mmcsd_buffer, MMCSD_MAX_BLOCK_SIZE))
{
mmcsd_deselect();
return MMCSD_CRC_ERR;
}
}
else
{
/* have the card transmit the CRC, but ignore it */
spi_xfer(mmcsd_spi, 0xFF);
spi_xfer(mmcsd_spi, 0xFF);
}
mmcsd_deselect();
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_write_data(int32 address, int16 size, int* ptr)
{
MMCSD_err ec;
int16 i; // counter for loops
for(i = 0; i < size; i++)
{
ec = mmcsd_write_byte(address++, *ptr++);
if(ec != MMCSD_GOODEC)
return ec;
}
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_write_block(int32 address, int16 size, int* ptr)
{
MMCSD_err ec;
int16 i;
// send command
mmcsd_select();
ec = mmcsd_write_single_block(address);
if(ec != MMCSD_GOODEC)
{
mmcsd_deselect();
return ec;
}
// send a data start token
spi_xfer(mmcsd_spi, DATA_START_TOKEN);
// send all the data
for(i = 0; i < size; i += 1)
spi_xfer(mmcsd_spi, ptr[i]);
// if the CRC is enabled we have to calculate it, otherwise just send an 0xFFFF
if(g_CRC_enabled)
spi_xfer(mmcsd_spi, mmcsd_crc16(ptr, size));
else
{
spi_xfer(mmcsd_spi, 0xFF);
spi_xfer(mmcsd_spi, 0xFF);
}
// get the error code back from the card; "data accepted" is 0bXXX00101
ec = mmcsd_get_r1();
if(ec & 0x0A)
{
mmcsd_deselect();
return ec;
}
// wait for the line to go back high, this indicates that the write is complete
while(spi_xfer(mmcsd_spi, 0xFF) == 0);
mmcsd_deselect();
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_go_idle_state(void)
{
mmcsd_send_cmd(GO_IDLE_STATE, 0);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_send_op_cond(void)
{
mmcsd_send_cmd(SEND_OP_COND, 0);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_send_if_cond(int r7[])
{
mmcsd_send_cmd(SEND_IF_COND, 0x45A);
return mmcsd_get_r7(r7);
}
MMCSD_err mmcsd_print_csd()
{
int
buf[16],
i,
r1;
// MMCs don't support this command
if(g_card_type == MMC)
return MMCSD_PARAM_ERR;
mmcsd_select();
mmcsd_send_cmd(SEND_CSD, 0);
r1 = mmcsd_get_r1();
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
r1 = mmcsd_wait_for_token(DATA_START_TOKEN);
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
for(i = 0; i < 16; i++)
buf[i] = spi_xfer(mmcsd_spi, 0xFF);
mmcsd_deselect();
printf("\r\nCSD_STRUCTURE: %X", (buf[0] & 0x0C) >> 2);
printf("\r\nTAAC: %X", buf[1]);
printf("\r\nNSAC: %X", buf[2]);
printf("\r\nTRAN_SPEED: %X", buf[3]);
printf("\r\nCCC: %lX", (make16(buf[4], buf[5]) & 0xFFF0) >> 4);
printf("\r\nREAD_BL_LEN: %X", buf[5] & 0x0F);
printf("\r\nREAD_BL_PARTIAL: %X", (buf[6] & 0x80) >> 7);
printf("\r\nWRITE_BLK_MISALIGN: %X", (buf[6] & 0x40) >> 6);
printf("\r\nREAD_BLK_MISALIGN: %X", (buf[6] & 0x20) >> 5);
printf("\r\nDSR_IMP: %X", (buf[6] & 0x10) >> 4);
printf("\r\nC_SIZE: %lX", (((buf[6] & 0x03) << 10) | (buf[7] << 2) | ((buf[8] & 0xC0) >> 6)));
printf("\r\nVDD_R_CURR_MIN: %X", (buf[8] & 0x38) >> 3);
printf("\r\nVDD_R_CURR_MAX: %X", buf[8] & 0x07);
printf("\r\nVDD_W_CURR_MIN: %X", (buf[9] & 0xE0) >> 5);
printf("\r\nVDD_W_CURR_MAX: %X", (buf[9] & 0x1C) >> 2);
printf("\r\nC_SIZE_MULT: %X", ((buf[9] & 0x03) << 1) | ((buf[10] & 0x80) >> 7));
printf("\r\nERASE_BLK_EN: %X", (buf[10] & 0x40) >> 6);
printf("\r\nSECTOR_SIZE: %X", ((buf[10] & 0x3F) << 1) | ((buf[11] & 0x80) >> 7));
printf("\r\nWP_GRP_SIZE: %X", buf[11] & 0x7F);
printf("\r\nWP_GRP_ENABLE: %X", (buf[12] & 0x80) >> 7);
printf("\r\nR2W_FACTOR: %X", (buf[12] & 0x1C) >> 2);
printf("\r\nWRITE_BL_LEN: %X", ((buf[12] & 0x03) << 2) | ((buf[13] & 0xC0) >> 6));
printf("\r\nWRITE_BL_PARTIAL: %X", (buf[13] & 0x20) >> 5);
printf("\r\nFILE_FORMAT_GRP: %X", (buf[14] & 0x80) >> 7);
printf("\r\nCOPY: %X", (buf[14] & 0x40) >> 6);
printf("\r\nPERM_WRITE_PROTECT: %X", (buf[14] & 0x20) >> 5);
printf("\r\nTMP_WRITE_PROTECT: %X", (buf[14] & 0x10) >> 4);
printf("\r\nFILE_FORMAT: %X", (buf[14] & 0x0C) >> 2);
printf("\r\nCRC: %X", buf[15]);
return r1;
}
MMCSD_err mmcsd_print_cid()
{
int
buf[16],
i,
r1;
// MMCs don't support this command
if(g_card_type == MMC)
return MMCSD_PARAM_ERR;
mmcsd_select();
mmcsd_send_cmd(SEND_CID, 0);
r1 = mmcsd_get_r1();
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
r1 = mmcsd_wait_for_token(DATA_START_TOKEN);
if(r1 != MMCSD_GOODEC)
{
mmcsd_deselect();
return r1;
}
for(i = 0; i < 16; i++)
buf[i] = spi_xfer(mmcsd_spi, 0xFF);
mmcsd_deselect();
printf("\r\nManufacturer ID: %X", buf[0]);
printf("\r\nOEM/Application ID: %c%c", buf[1], buf[2]);
printf("\r\nOEM/Application ID: %c%c%c%c%c", buf[3], buf[4], buf[5], buf[6], buf[7]);
printf("\r\nProduct Revision: %X", buf[8]);
printf("\r\nSerial Number: %X%X%X%X", buf[9], buf[10], buf[11], buf[12]);
printf("\r\nManufacturer Date Code: %X%X", buf[13] & 0x0F, buf[14]);
printf("\r\nCRC-7 Checksum: %X", buf[15]);
return r1;
}
MMCSD_err mmcsd_sd_status(int r2[])
{
int i;
mmcsd_select();
mmcsd_send_cmd(APP_CMD, 0);
r2[0]=mmcsd_get_r1();
mmcsd_deselect();
mmcsd_select();
mmcsd_send_cmd(SD_STATUS, 0);
for(i = 0; i < 64; i++)
spi_xfer(mmcsd_spi, 0xFF);
mmcsd_deselect();
return mmcsd_get_r2(r2);
}
MMCSD_err mmcsd_send_status(int r2[])
{
mmcsd_send_cmd(SEND_STATUS, 0);
return mmcsd_get_r2(r2);
}
MMCSD_err mmcsd_set_blocklen(int32 blocklen)
{
mmcsd_send_cmd(SET_BLOCKLEN, blocklen);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_read_single_block(int32 address)
{
mmcsd_send_cmd(READ_SINGLE_BLOCK, address);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_write_single_block(int32 address)
{
mmcsd_send_cmd(WRITE_BLOCK, address);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_sd_send_op_cond(void)
{
mmcsd_send_cmd(SD_SEND_OP_COND, 0);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_app_cmd(void)
{
mmcsd_send_cmd(APP_CMD, 0);
return mmcsd_get_r1();
}
MMCSD_err mmcsd_read_ocr(int r3[])
{
mmcsd_send_cmd(READ_OCR, 0);
return mmcsd_get_r3(r3);
}
MMCSD_err mmcsd_crc_on_off(int1 crc_enabled)
{
mmcsd_send_cmd(CRC_ON_OFF, crc_enabled);
g_CRC_enabled = crc_enabled;
return mmcsd_get_r1();
}
MMCSD_err mmcsd_send_cmd(int cmd, int32 arg)
{
int packet[6]; // the entire command, argument, and crc in one variable
// construct the packet
// every command on an SD card is or'ed with 0x40
packet[0] = cmd | 0x40;
packet[1] = make8(arg, 3);
packet[2] = make8(arg, 2);
packet[3] = make8(arg, 1);
packet[4] = make8(arg, 0);
// calculate the crc if needed
if(g_CRC_enabled)
packet[5] = mmcsd_crc7(packet, 5);
else
packet[5] = 0xFF;
// transfer the command and argument, with an extra 0xFF hacked in there
spi_xfer(mmcsd_spi, packet[0]);
spi_xfer(mmcsd_spi, packet[1]);
spi_xfer(mmcsd_spi, packet[2]);
spi_xfer(mmcsd_spi, packet[3]);
spi_xfer(mmcsd_spi, packet[4]);
spi_xfer(mmcsd_spi, packet[5]);
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_get_r1(void)
{
int
response = 0, // place to hold the response coming back from the SPI line
timeout = 0xFF; // maximum amount loops to wait for idle before getting impatient and leaving the function with an error code
// loop until timeout == 0
while(timeout)
{
// read what's on the SPI line
// the SD/MMC requires that you leave the line high when you're waiting for data from it
response = spi_xfer(mmcsd_spi, 0xFF);
// check to see if we got a response
if(response != 0xFF)
{
// fill in the response that we got and leave the function
return response;
}
// wait for a little bit longer
timeout--;
}
// for some reason, we didn't get a response back from the card
// return the proper error codes
return RESP_TIMEOUT;
}
MMCSD_err mmcsd_get_r2(int r2[])
{
r2[1] = mmcsd_get_r1();
r2[0] = spi_xfer(mmcsd_spi, 0xFF);
return 0;
}
MMCSD_err mmcsd_get_r3(int r3[])
{
return mmcsd_get_r7(r3);
}
MMCSD_err mmcsd_get_r7(int r7[])
{
int i; // counter for loop
// the top byte of r7 is r1
r7[4]=mmcsd_get_r1();
// fill in the other 4 bytes
for(i = 0; i < 4; i++)
r7[3 - i] = spi_xfer(mmcsd_spi, 0xFF);
return r7[4];
}
MMCSD_err mmcsd_wait_for_token(int token)
{
MMCSD_err r1;
// get a token
r1 = mmcsd_get_r1();
// check to see if the token we recieved was the one that we were looking for
if(r1 == token)
return MMCSD_GOODEC;
// if that wasn't right, return the error
return r1;
}
unsigned int8 mmcsd_crc7(char *data, unsigned int8 length)
{
unsigned int8 i, ibit, c, crc;
crc = 0x00; // Set initial value
for (i = 0; i < length; i++, data++)
{
c = *data;
for (ibit = 0; ibit < 8; ibit++)
{
crc = crc << 1;
if ((c ^ crc) & 0x80) crc = crc ^ 0x09; // ^ is XOR
c = c << 1;
}
crc = crc & 0x7F;
}
shift_left(&crc, 1, 1); // MMC card stores the result in the top 7 bits so shift them left 1
// Should shift in a 1 not a 0 as one of the cards I have won't work otherwise
return crc;
}
unsigned int16 mmcsd_crc16(char *data, unsigned int8 length)
{
unsigned int8 i, ibit, c;
unsigned int16 crc;
crc = 0x0000; // Set initial value
for (i = 0; i < length; i++, data++)
{
c = *data;
for (ibit = 0; ibit < 8; ibit++)
{
crc = crc << 1;
if ((c ^ crc) & 0x8000) crc = crc ^ 0x1021; // ^ is XOR
c = c << 1;
}
crc = crc & 0x7FFF;
}
shift_left(&crc, 2, 1); // MMC card stores the result in the top 7 bits so shift them left 1
// Should shift in a 1 not a 0 as one of the cards I have won't work otherwise
return crc;
}
void mmcsd_select()
{
output_low(MMCSD_PIN_SELECT);
}
void mmcsd_deselect()
{
spi_xfer(mmcsd_spi, 0xFF);
output_high(MMCSD_PIN_SELECT);
}
MMCSD_err mmcsd_load_buffer(void)
{
g_MMCSDBufferChanged = FALSE;
return(mmcsd_read_block(g_mmcsdBufferAddress, MMCSD_MAX_BLOCK_SIZE, g_mmcsd_buffer));
}
MMCSD_err mmcsd_flush_buffer(void)
{
if (g_MMCSDBufferChanged)
{
g_MMCSDBufferChanged = FALSE;
return(mmcsd_write_block(g_mmcsdBufferAddress, MMCSD_MAX_BLOCK_SIZE, g_mmcsd_buffer));
}
return(0); //ok
}
MMCSD_err mmcsd_move_buffer(int32 new_addr)
{
MMCSD_err ec = MMCSD_GOODEC;
int32
//cur_block,
new_block;
// make sure we're still on the same block
//cur_block = g_mmcsdBufferAddress - (g_mmcsdBufferAddress % MMCSD_MAX_BLOCK_SIZE);
new_block = new_addr - (new_addr % MMCSD_MAX_BLOCK_SIZE);
//if(cur_block != new_block)
if(g_mmcsdBufferAddress != new_block)
{
// dump the old buffer
if (g_MMCSDBufferChanged)
{
ec = mmcsd_flush_buffer();
if(ec != MMCSD_GOODEC)
return ec;
g_MMCSDBufferChanged = FALSE;
}
// figure out the best place for a block
g_mmcsdBufferAddress = new_block;
// load up a new buffer
ec = mmcsd_load_buffer();
}
return ec;
}
MMCSD_err mmcsd_read_byte(int32 addr, char* data)
{
MMCSD_err ec;
ec = mmcsd_move_buffer(addr);
if(ec != MMCSD_GOODEC)
{
return ec;
}
*data = g_mmcsd_buffer[addr % MMCSD_MAX_BLOCK_SIZE];
return MMCSD_GOODEC;
}
MMCSD_err mmcsd_write_byte(int32 addr, char data)
{
MMCSD_err ec;
ec = mmcsd_move_buffer(addr);
if(ec != MMCSD_GOODEC)
return ec;
g_mmcsd_buffer[addr % MMCSD_MAX_BLOCK_SIZE] = data;
g_MMCSDBufferChanged = TRUE;
return MMCSD_GOODEC;
}
#endif