📄 mmc.c
字号:
// Basic MMC/SD erase/read/write - with or without full crc error checking (see top of mmc.h file).
// also reads all possible info on the MMC/SD - card size etc, though not all info is avaliable in SPI mode.
//
// free code, free usage, modify as you see fit, no ties.
//
// last updated 11:25 19th March 2007.
//
// appears to be bug free (famous last words) but wouldunt swear by it!
//
// we do do the crc calcs while we are waiting for each byte to finish being moved out the SPI
// tx register to the MMC/SD so the crc calcs shouldnt slow things down - unless you use a high spi sclk
// rate (not possible on SPI0).
//
// in our case the SD card is on SPI port-0 (could be moved to the SSP port (still SPI mode) for more speed.
//
// LOTS of ascii debug info sent down the UART-0 port to your terminal program if you
// compile with the mmc_debug1 option (see top of mmc.h file).
//
// note..
// the debug info is sent down the uart port at a high rate, as we don't use any kind of hand shaking then
// your terminal program may not be able to keep up (more than one we tested can't cope).
// the result maybe what looks like a mess/corrupted MMC/SD data in the terminals rx window - but this
// is not the case (unless of cause it is???).
//
// anuva note..
// enabling the mmc_debug1 info slows the code down 10 fold (or more)! .. mainly due to the limited uart
// port speed the debug info is being pushed down.
//
// n anuva note..
// one thing we did notice while writing this was that if the MMC card is improperly accessed at
// some point (due to early bugs), then the card requires a power-cycle before you can access it
// properly again - a note you should bare in mind if you intend using MMC/SD's in your projects!!!!
// (design into your project a way to power cycle the MMC/SD just incase I guess - would allow for
// hot swapping as well if you did - assuming you monitor the card slot switch(s)!).
//
// this module was fully tested on a 64MB SD card - no idea if it works with all cards though!
//
// A BIG NOTE ..
// the sector test routine (bottom of this file) uses 2 local large buffers (large for embedded) for
// sector data - make sure your stack size is big enough to hold em!!!!!!! .. you have been warned!!
// our project was set to a stack size of 2048 bytes (over kill i know).
// you could always move the buffers from local to global though if you want too so as not to need
// a large stack size.
// you could also just delete the sector test function altogether! - remove the reference to it in mmc.h if you do.
//
// heap usage is zero (no malloc's etc).
#include "mmc.h"
#include <string.h>
#ifdef mmc_debug
#include <stdio.h>
#endif
// ****************************************
// MMC commands
#define MMC_GO_IDLE_STATE 0
#define MMC_SEND_OP_COND 1
#define MMC_ALL_SEND_CID 2
#define MMC_SET_RELATIVE_ADDR 3
#define MMC_SWITCH 6
#define MMC_SEND_CSD 9
#define MMC_SEND_CID 10
#define MMC_READ_DAT_UNTIL_STOP 11
#define MMC_STOP_TRANSMISSION 12
#define MMC_SEND_STATUS 13
#define MMC_GO_INACTIVE_STATE 15 // mmc/sd card power cycle needed after sending this command!!!!
#define MMC_SET_BLOCKLEN 16
#define MMC_READ_SINGLE_BLOCK 17
#define MMC_READ_MULTIPLE_BLOCK 18
#define MMC_WRITE_DAT_UNTIL_STOP 20
#define MMC_SET_BLOCK_COUNT 23
#define MMC_WRITE_BLOCK 24
#define MMC_WRITE_MULTIPLE_BLOCK 25
#define MMC_PROGRAM_CSD 27
#define MMC_SET_WRITE_PROT 28
#define MMC_CLR_WRITE_PROT 29
#define MMC_SEND_WRITE_PROT 30
#define MMC_TAG_SECTOR_START 32
#define MMC_TAG_SECTOR_END 33
#define MMC_UNTAG_SECTOR 34
#define MMC_TAG_ERASE_GROUP_START 35
#define MMC_TAG_ERARE_GROUP_END 36
#define MMC_UNTAG_ERASE_GROUP 37
#define MMC_ERASE 38
#define MMC_LOCK_UNLOCK 42
#define MMC_APP_CMD 55
#define MMC_GEN_CMD 56
#define MMC_READ_OCR 58
#define MMC_CRC_ON_OFF 59
// R1 bits
#define MMC_R1_BUSY 0x80
#define MMC_R1_PARAMETER_ERROR 0x40
#define MMC_R1_ADDRESS_ERROR 0x20
#define MMC_R1_ERASE_SEQ_ERROR 0x10
#define MMC_R1_COM_CRC_ERROR 0x08
#define MMC_R1_ILLEGAL_COM 0x04
#define MMC_R1_ERASE_RESET 0x02
#define MMC_R1_IDLE_STATE 0x01
// R2 bits
#define MMC_R2_OUT_OF_RANGE 0x80
#define MMC_R2_ERASE_PARAM_BAD 0x40
#define MMC_R2_WP_VIOLATION 0x20
#define MMC_R2_CARD_ECC_FAIL 0x10
#define MMC_R2_CC_ERROR 0x08 // CC error - card controller failure
#define MMC_R2_ERROR 0x04 // General or unknown error occured
#define MMC_R2_WP_ERASE_SKIP 0x02 // Write protect erase skip
// data tokens
#define MMC_SINGLE_BLK_RD 0xfe
#define MMC_MULTI_BLK_READ 0xfe
#define MMC_STRT_SINGLE_BLK_WR 0xfe
#define MMC_STRT_MULTI_BLK_WR 0xfc
#define MMC_STP_MULTI_BLK_WR 0xfd
// data error tokens
#define MMC_DE_MISALIGN 0x10
#define MMC_DE_ADDR_OUT_OF_RANGE 0x08
#define MMC_DE_CARD_EEC_FAIL 0x04
#define MMC_DE_CARD_ERROR 0x02
#define MMC_DE_EX_ERROR 0x01
// data response tokens
#define MMC_DR_ACCEPT 0x02
#define MMC_DR_REJECT_CRC 0x05
#define MMC_DR_REJECT_WRITE_ERROR 0x06
// ****************************************
T_MMC_Info MMC_Info = {0}; // info we get back from the MMC
// ****************************************
// SPI pass offs
#define SPI_Initialize(a, b, c, d, e, f, g, h) SPI0_Initialize(a, b, c, d, e, f, g, h)
#define SPI_8(x) SPI0_8(x)
#define SPI_16(x) SPI0_16(x)
#define SPI_SendByte(x) SPI0_SendByte(x)
#define SPI_WaitForTx() SPI0_WaitForTx()
#define SPI_WaitForTxGetRx() SPI0_WaitForTxGetRx()
// ****************************************
#ifdef mmc_use_crc
#ifdef mmc_crc_tab
//const _u16 crc_table_16[] = {
const _u32 crc_table_16[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0};
#endif
static _u8 mmc_update_crc7(_u8 crc, _u8 b)
{ // update a crc7 value
//
// as the crc7 is only used on the command bytes and a couple of
// short register reads (cid & csd) it's not worth tabling this.
register _u8 d = b;
register _u8 c = crc;
register _u8 mask = 0x80;
register _u8 poly = 0x09; // x7 + x3 + 1
register int i;
for (i = 8; i > 0; i--)
{
c <<= 1;
if ((d ^ c) & mask) c ^= poly;
d <<= 1;
}
return c;
}
#ifndef mmc_crc_tab
static _u16 mmc_update_crc16(_u16 crc, _u8 b)
{ // update a crc16 value
//
// bit-bang method - slow method (but conserves rom space)
register _u16 d = (_u16)b << 8;
register _u16 c = crc;
register _u16 poly = 0x1021; // x16 + x12 + x5 + 1 (CRC-CCITT)
register _u16 mask = 0x8000;
register int i;
for (i = 8; i > 0; i--)
{
if ((c ^ d) & mask)
c = (c << 1) ^ poly;
else
c = (c << 1);
d <<= 1;
}
return c;
}
#else
// look-up table method - several times faster (but needs more rom space for the look-up table)
#define mmc_update_crc16(crc, b) (((crc << 8) & 0xff00) ^ crc_table_16[(crc >> 8) ^ b])
//static _u16 mmc_update_crc16(_u16 crc, _u8 b)
//{
// return ((crc << 8) ^ crc_table_16[(crc >> 8) ^ b]);
//}
#endif
/*
static void mmc_create_crc_table_16(void)
{ // create the crc16 look-up table in ram
int i, j;
_u16 d, crc;
for (i = 0; i < 256; i++)
{
crc = 0;
d = i << 8;
for (j = 8; j > 0; j--)
{
if ((crc ^ d) & 0x8000)
crc = (crc << 1) ^ 0x1021; // x16 + x12 + x5 + 1 (CRC-CCITT)
else
crc = (crc << 1);
d <<= 1;
}
crc_table_16[i] = crc;
}
}
*/
static int CheckCRC7(void *buffer, int buf_size)
{ // check the crc located in a data packet
int i;
_u8 *buf = (_u8*)buffer;
_u8 crc;
#ifdef mmc_debug
char s[56];
#endif
crc = 0; // init crc
for (i = buf_size - 1; i > 0; i--) crc = mmc_update_crc7(crc, *buf++); // calculate the crc
crc = (crc << 1) | 0x01;
if (crc == *buf) return mmc_ok;
// crc mismatch
#ifdef mmc_debug
sprintf(s, "mmc crc7 error .. our crc: %0.2X there crc: %0.2X\r\n", crc, *buf);
WriteStr_uart0(s);
#endif
return mmc_err_data;
}
#endif
//*************************************************************************************
/*
void mmc_ISR(void)
{
_u32 regValue;
_u8 b;
ctl_global_interrupts_re_enable_from_isr();
S0SPINT = Bit0; // clear interrupt flag
regValue = S0SPSR; // read status register - this also clears the flags
if (regValue & S0SPSR_ABRT)
{ // slave abort
}
if (regValue & S0SPSR_MODF)
{ // mode fault
}
if (regValue & S0SPSR_ROVR)
{ // read overrun
}
if (regValue & S0SPSR_WCOL)
{ // write collison
}
if (regValue & S0SPSR_SPIF)
{ // SPI transfer complete
b = S0SPDR; // read rx'ed byte
// SPI0Status |= SPI0_TX_DONE;
// TxCounter++;
S0SPDR = 0xff; // start another byte transfer
}
ctl_global_interrupts_un_re_enable_from_isr();
}
*/
//*************************************************************************************
#ifdef mmc_debug
void mmc_show_r1(char *s, _u8 r1)
{ // show the R1 response
if (r1 & MMC_R1_BUSY)
{
WriteStr_uart0(s);
WriteStr_uart0(" R1_BUSY\r\n");
}
if (r1 & MMC_R1_PARAMETER_ERROR)
{
WriteStr_uart0(s);
WriteStr_uart0(" R1_PARAMETER_ERROR\r\n");
}
if (r1 & MMC_R1_ADDRESS_ERROR)
{
WriteStr_uart0(s);
WriteStr_uart0(" R1_ADDRESS_ERROR\r\n");
}
if (r1 & MMC_R1_ERASE_SEQ_ERROR)
{
WriteStr_uart0(s);
WriteStr_uart0(" R1_ERASE_SEQ_ERROR\r\n");
}
if (r1 & MMC_R1_COM_CRC_ERROR)
{
WriteStr_uart0(s);
WriteStr_uart0(" R1_COM_CRC_ERROR\r\n");
}
if (r1 & MMC_R1_ILLEGAL_COM)
{
WriteStr_uart0(s);
WriteStr_uart0(" R1_ILLEGAL_COM\r\n");
}
if (r1 & MMC_R1_ERASE_RESET)
{
WriteStr_uart0(s);
WriteStr_uart0(" R1_ERASE_RESET\r\n");
}
if (r1 & MMC_R1_IDLE_STATE)
{
WriteStr_uart0(s);
WriteStr_uart0(" R1_IDLE_STATE\r\n");
}
}
void mmc_show_r2(char *s, _u8 r2)
{ // show the R2 response
if (r2 & MMC_R2_OUT_OF_RANGE)
{
WriteStr_uart0(s);
WriteStr_uart0(" R2_OUT_OF_RANGE\r\n");
}
if (r2 & MMC_R2_ERASE_PARAM_BAD)
{
WriteStr_uart0(s);
WriteStr_uart0(" R2_ERASE_PARAM_BAD\r\n");
}
if (r2 & MMC_R2_WP_VIOLATION)
{
WriteStr_uart0(s);
WriteStr_uart0(" R2_WP_VIOLATION\r\n");
}
if (r2 & MMC_R2_CARD_ECC_FAIL)
{
WriteStr_uart0(s);
WriteStr_uart0(" R2_CARD_ECC_FAIL\r\n");
}
if (r2 & MMC_R2_CC_ERROR)
{
WriteStr_uart0(s);
WriteStr_uart0(" R2_CC_ERROR\r\n");
}
if (r2 & MMC_R2_ERROR)
{
WriteStr_uart0(s);
WriteStr_uart0(" R2_ERROR\r\n");
}
if (r2 & MMC_R2_WP_ERASE_SKIP)
{
WriteStr_uart0(s);
WriteStr_uart0(" R2_WP_ERASE_SKIP\r\n");
}
}
#endif
_u8 mmc_calc_mantissa(_u8 b)
{
switch (b)
{
case 1 : b = 10;
break;
case 2 : b = 12;
break;
case 3 : b = 13;
break;
case 4 : b = 15;
break;
case 5 : b = 20;
break;
case 6 : b = 25;
break;
case 7 : b = 30;
break;
case 8 : b = 35;
break;
case 9 : b = 40;
break;
case 10 : b = 45;
break;
case 11 : b = 50;
break;
case 12 : b = 55;
break;
case 13 : b = 60;
break;
case 14 : b = 70;
break;
case 15 : b = 80;
break;
default : b = 0;
break;
}
return b;
}
static int mmc_get_response(_u8 byte, bool match, int max_loops)
{ // read the MMC until we ..
//
// get a different rx'ed byte from the supplied byte .. if 'match' is false
// .. or ..
// get the same rx'ed byte from the supplied byte .. if 'match' is true
register _u8 b;
register int i;
#ifdef mmc_debug
int j = 0;
char s[8];
#endif
if (max_loops < 1) max_loops = 1;
for (i = max_loops - 1; ; i--)
{
if (!mmc_inserted()) return mmc_err_not_inserted; // the MMC is not in it's card slot!
b = SPI_8(0xff); // send byte & wait for returned byte
#ifdef mmc_debug1
if (j <= 0)
{
WriteStr_uart0("mmc_res:");
j = 0;
}
sprintf(s, " %0.2X", b);
WriteStr_uart0(s);
j++;
if (j >= 32)
{
WriteStr_uart0("\r\n");
j = 0;
}
#endif
if (match)
{
if (b == byte) break; // the rx'ed byte is the same
}
else
{
if (b != byte) break; // the rx'ed byte is different
}
if (i > 0) continue; // still waiting
// timeout
#ifdef mmc_debug
#ifdef mmc_debug1
if (j > 0) WriteStr_uart0("\r\n");
#endif
WriteStr_uart0("mmc time-out\r\n");
#endif
return mmc_err_timeout; // return time-out error code
}
#ifdef mmc_debug1
if (j > 0) WriteStr_uart0("\r\n");
#endif
#ifdef mmc_debug
WriteStr_uart0("waited for ");
sprintf(s, "%u", (max_loops - 1) - i);
WriteStr_uart0(s);
WriteStr_uart0(" bytes for response\r\n");
#endif
return b;
}
static int mmc_send_cmd(_u8 cmd, _u32 arg, _u32 res_time)
{ // send an command to the MMC - return the response we get back
_u8 cmd_buf[6];
register _u8 b;
register _u8 crc;
register int i;
#ifdef mmc_debug
char s[24];
#endif
#ifdef mmc_debug
switch (cmd)
{
case MMC_GO_IDLE_STATE : WriteStr_uart0("MMC_GO_IDLE_STATE");
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -