/// MMC/SD Library. CopyLefter from Procyon AVRlib /// _________________ /// / 1 2 3 4 5 6 78 | <- view of MMC/SD card looking at contacts /// / 9 | Pins 8 and 9 are present only on SD cards /// | MMC/SD Card | /// | | /// /\/\/\/\/\/\/\/\/\/\ /// 1 - CS (chip select) - wire to any available I/O pin(*) /// 2 - DIN (data in, card<-host) - wire to SPI MOSI pin /// 3 - VSS (ground) - wire to ground /// 4 - VDD (power, 3.3V only?) - wire to power (MIGHT BE 3.3V ONLY!) /// 5 - SCLK (data clock) - wire to SPI SCK pin /// 6 - VSS (ground) - wire to ground /// 7 - DOUT (data out, card->host) - wire to SPI MISO pin /// 8,9 (SD card only) - PullUP to VDD #define MMC_CS_PORT PORTB #define MMC_CS_DDR DDRB #define MMC_CS_PIN 0 // function prototypes // SPI interface initializer void spiInit(void); // spiSendByte(unsigned char data) waits until the SPI interface is ready // and then sends a single byte over the SPI port. This command // does not receive anything. void spiSendByte(unsigned char data); // spiTransferByte(unsigned char data) waits until the SPI interface is ready // and then sends a single byte over the SPI port. The function also // returns the byte that was received during transmission. unsigned char spiTransferByte(unsigned char data); // spiTransferWord(unsigned char data) works just like spiTransferByte but // operates on a whole word (16-bits of data). unsigned int spiTransferWord(unsigned int data); // global variables volatile unsigned char spiTransferComplete; // access routines void spiInit() { unsigned char zzz; // setup SPI I/O pins PORTB.5=1; // set SCK hi DDRB.5=1; // set SCK as output DDRB.4=0; // set MISO as input DDRB.3=1; // set MOSI as output DDRB.2=1; // SS must be output for Master mode to work SPCR.4=1; // clock = f/16 SPCR.0=0; SPCR.1=0; SPSR.0=0; // select clock phase positive-going in middle of data SPCR.3=0; // Data order MSB first SPCR.5=0; // enable SPI SPCR.6=1; // clear status zzz=SPSR; spiTransferComplete = 255; } void spiSendByte(unsigned char data) { while(!((SPSR) & (1<< SPSR.7))); spiTransferComplete = 0; SPDR=data; } unsigned char spiTransferByte(unsigned char data) { // send the given data spiTransferComplete = 0; SPDR=data; while ((SPSR & (1<<7))==0); spiTransferComplete = 255; return SPDR; } // constants/macros/typdefs // MMC commands (taken from sandisk MMC reference) #define MMC_GO_IDLE_STATE 0 ///< initialize card to SPI-type access #define MMC_SEND_OP_COND 1 ///< set card operational mode #define MMC_SEND_CSD 9 ///< get card's CSD #define MMC_SEND_CID 10 ///< get card's CID #define MMC_SEND_STATUS 13 #define MMC_SET_BLOCKLEN 16 ///< Set number of bytes to transfer per block #define MMC_READ_SINGLE_BLOCK 17 ///< read a block #define MMC_WRITE_BLOCK 24 ///< write a block #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 ///< Sets beginning of erase group (mass erase) #define MMC_TAG_ERARE_GROUP_END 36 ///< Sets end of erase group (mass erase) #define MMC_UNTAG_ERASE_GROUP 37 ///< Untag (unset) erase group (mass erase) #define MMC_ERASE 38 ///< Perform block/mass erase #define MMC_CRC_ON_OFF 59 ///< Turns CRC check on/off // R1 Response bit-defines #define MMC_R1_BUSY 0x80 ///< R1 response: bit indicates card is busy #define MMC_R1_PARAMETER 0x40 #define MMC_R1_ADDRESS 0x20 #define MMC_R1_ERASE_SEQ 0x10 #define MMC_R1_COM_CRC 0x08 #define MMC_R1_ILLEGAL_COM 0x04 #define MMC_R1_ERASE_RESET 0x02 #define MMC_R1_IDLE_STATE 0x01 // Data Start tokens #define MMC_STARTBLOCK_READ 0xFE ///< when received from card, indicates that a block of data will follow #define MMC_STARTBLOCK_WRITE 0xFE ///< when sent to card, indicates that a block of data will follow #define MMC_STARTBLOCK_MWRITE 0xFC // Data Stop tokens #define MMC_STOPTRAN_WRITE 0xFD // Data Error Token values #define MMC_DE_MASK 0x1F #define MMC_DE_ERROR 0x01 #define MMC_DE_CC_ERROR 0x02 #define MMC_DE_ECC_FAIL 0x04 #define MMC_DE_OUT_OF_RANGE 0x04 #define MMC_DE_CARD_LOCKED 0x04 // Data Response Token values #define MMC_DR_MASK 0x1F #define MMC_DR_ACCEPT 0x05 #define MMC_DR_REJECT_CRC 0x0B #define MMC_DR_REJECT_WRITE_ERROR 0x0D // functions //! Initialize AVR<->MMC hardware interface. /// Prepares hardware for MMC access. void mmcInit(void); //! Initialize the card and prepare it for use. /// Returns zero if successful. unsigned char mmcReset(void); //! Send card an MMC command. /// Returns R1 result code. unsigned char mmcSendCommand(unsigned char cmd, unsigned long arg); //! Read 512-byte sector from card to buffer /// Returns zero if successful. unsigned char mmcRead(unsigned long sector, unsigned char* buffer); //! Write 512-byte sector from buffer to card /// Returns zero if successful. unsigned char mmcWrite(unsigned long sector, unsigned char* buffer); //! Internal command function. /// Issues a generic MMC command as specified by cmd and arg. unsigned char mmcCommand(unsigned char cmd, unsigned long arg); void mmcInit(void) { // initialize SPI interface spiInit(); // release chip select DDRB.0=1; PORTB.0=1; } unsigned char mmcReset(void) { unsigned char retry,rn; unsigned char r1=0; retry = 0; do { // send dummy bytes with CS high before accessing for (rn=0; rn<74;rn++) spiTransferByte(0xFF); delay_ms(1); // resetting card, go to SPI mode r1 = mmcSendCommand(MMC_GO_IDLE_STATE, 0); // do retry counter retry++; if(retry>10) return 255; } while(r1 != 0x01); retry = 0; do { // initializing card for operation r1 = mmcSendCommand(MMC_SEND_OP_COND, 0); // do retry counter retry++; if(retry>250) return 254; } while(!(r1==0)); // turn off CRC checking to simplify communication //r1 = mmcSendCommand(MMC_CRC_ON_OFF, 0); // set block length to 512 bytes r1 = mmcSendCommand(MMC_SET_BLOCKLEN, 512); // return success return 0; } unsigned char mmcSendCommand(unsigned char cmd, unsigned long arg) { unsigned char r1; // assert chip select PORTB.0=0; // issue the command r1 = mmcCommand(cmd, arg); // release chip select PORTB.0=1; return r1; } unsigned char mmcRead(unsigned long sector, unsigned char* buffer) { unsigned char r1; unsigned int i; // assert chip select PORTB.0=0; // issue command r1 = mmcCommand(MMC_READ_SINGLE_BLOCK, sector<<9); // check for valid response if(r1 != 0x00) return r1; // wait for block start while(spiTransferByte(0xFF) != MMC_STARTBLOCK_READ); // read in data for(i=0; i<0x200; i++) { *buffer++ = spiTransferByte(0xFF); } // read 16-bit CRC spiTransferByte(0xFF); spiTransferByte(0xFF); // release chip select PORTB.0=1; // return success return 0; } unsigned char mmcWrite(unsigned long sector, unsigned char* buffer) { unsigned char r1; unsigned int i; // assert chip select PORTB.0=0; // issue command r1 = mmcCommand(MMC_WRITE_BLOCK, sector<<9); // check for valid response if(r1 != 0x00) return r1; // send dummy spiTransferByte(0xFF); // send data start token spiTransferByte(MMC_STARTBLOCK_WRITE); // write data for(i=0; i<0x200; i++) { spiTransferByte(*buffer++); } // write 16-bit CRC (dummy values) spiTransferByte(0xFF); spiTransferByte(0xFF); // read data response token r1 = spiTransferByte(0xFF); if( (r1&MMC_DR_MASK) != MMC_DR_ACCEPT) return r1; // wait until card not busy while(!spiTransferByte(0xFF)); // release chip select PORTB.0=1; // return success return 0; } unsigned char mmcCommand(unsigned char cmd, unsigned long arg) { unsigned char r1; unsigned char retry=0; // send command spiTransferByte(cmd | 0x40); spiTransferByte(arg>>24); spiTransferByte(arg>>16); spiTransferByte(arg>>8); spiTransferByte(arg); spiTransferByte(0x95); // crc valid only for MMC_GO_IDLE_STATE // end command // wait for response // if more than 8 retries, card has timed-out // return the received 0xFF while((r1 = spiTransferByte(0xFF)) == 0xFF) if(retry++ > 8) break; // return response return r1; }