123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- /*-----------------------------------------------------------------------*/
- /* MMCv3/SDv1/SDv2 (in SPI mode) control module (C)ChaN, 2012 */
- /*-----------------------------------------------------------------------*/
- #include <avr/io.h>
- #include "ff.h"
- #include "diskio.h"
- #include "main.h"
- /* Port controls (Platform dependent) */
- #define CS_LOW() {SD_CS_PORT &= ~SD_CS; LEDR_ON();} /* CS=low, LED on */
- #define CS_HIGH() {SD_CS_PORT |= SD_CS; LEDR_OFF();} /* CS=high, LED off */
- #define POWER_ON() {SD_PWROFF_PORT &= ~SD_PWROFF;} /* MMC power on */
- #define POWER_OFF() {SD_PWROFF_PORT |= SD_PWROFF;} /* MMC power off */
- #define POWER (!(SD_PWROFF_PIN & SD_PWROFF)) /* Test for power state. on:true, off:false */
- #define SOCKINS (!(SD_CD_PIN & SD_CD)) /* Test for card exist. yes:true, true:false, default:true */
- #define SOCKWP (SD_WP_PIN & SD_WP) /* Test for write protect. yes:true, no:false, default:false */
- #define FCLK_SLOW() SPCR = _BV(MSTR) | _BV(SPE) | _BV(SPR1) | _BV(SPR0) /* Set slow clock (F_CPU / 64) */
- #define FCLK_FAST() SPCR = _BV(MSTR) | _BV(SPE) /* Set fast clock (F_CPU / 2) */
- /*--------------------------------------------------------------------------
- Module Private Functions
- ---------------------------------------------------------------------------*/
- /* Definitions for MMC/SDC command */
- #define CMD0 (0) /* GO_IDLE_STATE */
- #define CMD1 (1) /* SEND_OP_COND (MMC) */
- #define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
- #define CMD8 (8) /* SEND_IF_COND */
- #define CMD9 (9) /* SEND_CSD */
- #define CMD10 (10) /* SEND_CID */
- #define CMD12 (12) /* STOP_TRANSMISSION */
- #define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
- #define CMD16 (16) /* SET_BLOCKLEN */
- #define CMD17 (17) /* READ_SINGLE_BLOCK */
- #define CMD18 (18) /* READ_MULTIPLE_BLOCK */
- #define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */
- #define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
- #define CMD24 (24) /* WRITE_BLOCK */
- #define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
- #define CMD55 (55) /* APP_CMD */
- #define CMD58 (58) /* READ_OCR */
- /* Card type flags (CardType) */
- #define CT_MMC 0x01 /* MMC ver 3 */
- #define CT_SD1 0x02 /* SD ver 1 */
- #define CT_SD2 0x04 /* SD ver 2 */
- #define CT_SDC (CT_SD1|CT_SD2) /* SD */
- #define CT_BLOCK 0x08 /* Block addressing */
- static volatile
- DSTATUS Stat = STA_NOINIT; /* Disk status */
- static volatile
- BYTE Timer1, Timer2; /* 100Hz decrement timer */
- static
- BYTE CardType; /* Card type flags */
- /*-----------------------------------------------------------------------*/
- /* Power Control (Platform dependent) */
- /*-----------------------------------------------------------------------*/
- /* When the target system does not support socket power control, there */
- /* is nothing to do in these functions and chk_power always returns 1. */
- static
- void power_off (void)
- {
- SPCR = 0; /* Disable SPI function */
- DDRB &= ~(_BV(PB5) | _BV(PB7)); /* Set SCK/MOSI to hi-z */
- SD_CS_DDR &= ~SD_CS;
- POWER_OFF();
- Stat |= STA_NOINIT;
- }
- static
- void power_on (void) /* Apply power sequence */
- {
- SD_PWROFF_DDR |= SD_PWROFF;
- for (Timer1 = 30; Timer1; ) {}; /* 300ms */
- POWER_ON(); /* Power on */
- for (Timer1 = 3; Timer1; ) {}; /* 30ms */
- SD_CS_PORT |= SD_CS;
- SD_CS_DDR |= SD_CS;
- PORTB |= _BV(PB5) | _BV(PB7); /* Configure SCK/MOSI as output */
- DDRB |= _BV(PB5) | _BV(PB7);
- FCLK_SLOW(); /* Enable SPI function in mode 0 */
- SPSR = _BV(SPI2X); /* SPI 2x mode */
- }
- /*-----------------------------------------------------------------------*/
- /* Transmit/Receive data from/to MMC via SPI (Platform dependent) */
- /*-----------------------------------------------------------------------*/
- /* Exchange a byte */
- static
- BYTE xchg_spi ( /* Returns received data */
- BYTE dat /* Data to be sent */
- )
- {
- SPDR = dat;
- loop_until_bit_is_set(SPSR, SPIF);
- return SPDR;
- }
- /* Send a data block */
- static
- void xmit_spi_multi (
- const BYTE *p, /* Data block to be sent */
- UINT cnt /* Size of data block */
- )
- {
- do {
- SPDR = *p++; loop_until_bit_is_set(SPSR,SPIF);
- SPDR = *p++; loop_until_bit_is_set(SPSR,SPIF);
- } while (cnt -= 2);
- }
- /* Receive a data block */
- static
- void rcvr_spi_multi (
- BYTE *p, /* Data buffer */
- UINT cnt /* Size of data block */
- )
- {
- do {
- SPDR = 0xFF; loop_until_bit_is_set(SPSR,SPIF); *p++ = SPDR;
- SPDR = 0xFF; loop_until_bit_is_set(SPSR,SPIF); *p++ = SPDR;
- } while (cnt -= 2);
- }
- /*-----------------------------------------------------------------------*/
- /* Wait for card ready */
- /*-----------------------------------------------------------------------*/
- static
- int wait_ready (void) /* 1:OK, 0:Timeout */
- {
- BYTE d;
- Timer2 = 50; /* Wait for ready in timeout of 500ms */
- do
- d = xchg_spi(0xFF);
- while (d != 0xFF && Timer2);
- return (d == 0xFF) ? 1 : 0;
- }
- /*-----------------------------------------------------------------------*/
- /* Deselect the card and release SPI bus */
- /*-----------------------------------------------------------------------*/
- static
- void deselect (void)
- {
- CS_HIGH();
- xchg_spi(0xFF); /* Dummy clock (force DO hi-z for multiple slave SPI) */
- }
- /*-----------------------------------------------------------------------*/
- /* Select the card and wait for ready */
- /*-----------------------------------------------------------------------*/
- static
- int select (void) /* 1:Successful, 0:Timeout */
- {
- CS_LOW();
- xchg_spi(0xFF); /* Dummy clock (force DO enabled) */
- if (wait_ready()) return 1; /* OK */
- deselect();
- return 0; /* Timeout */
- }
- /*-----------------------------------------------------------------------*/
- /* Receive a data packet from MMC */
- /*-----------------------------------------------------------------------*/
- static
- int rcvr_datablock (
- BYTE *buff, /* Data buffer to store received data */
- UINT btr /* Byte count (must be multiple of 4) */
- )
- {
- BYTE token;
- Timer1 = 20;
- do { /* Wait for data packet in timeout of 200ms */
- token = xchg_spi(0xFF);
- } while ((token == 0xFF) && Timer1);
- if (token != 0xFE) return 0; /* If not valid data token, retutn with error */
- rcvr_spi_multi(buff, btr); /* Receive the data block into buffer */
- xchg_spi(0xFF); /* Discard CRC */
- xchg_spi(0xFF);
- return 1; /* Return with success */
- }
- /*-----------------------------------------------------------------------*/
- /* Send a data packet to MMC */
- /*-----------------------------------------------------------------------*/
- static
- int xmit_datablock (
- const BYTE *buff, /* 512 byte data block to be transmitted */
- BYTE token /* Data/Stop token */
- )
- {
- BYTE resp;
- if (!wait_ready()) return 0;
- xchg_spi(token); /* Xmit data token */
- if (token != 0xFD) { /* Is data token */
- xmit_spi_multi(buff, 512); /* Xmit the data block to the MMC */
- xchg_spi(0xFF); /* CRC (Dummy) */
- xchg_spi(0xFF);
- resp = xchg_spi(0xFF); /* Reveive data response */
- if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */
- return 0;
- }
- return 1;
- }
- /*-----------------------------------------------------------------------*/
- /* Send a command packet to MMC */
- /*-----------------------------------------------------------------------*/
- static
- BYTE send_cmd ( /* Returns R1 resp (bit7==1:Send failed) */
- BYTE cmd, /* Command index */
- DWORD arg /* Argument */
- )
- {
- BYTE n, res;
- if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
- cmd &= 0x7F;
- res = send_cmd(CMD55, 0);
- if (res > 1) return res;
- }
- /* Select the card and wait for ready */
- deselect();
- if (!select()) return 0xFF;
- /* Send command packet */
- xchg_spi(0x40 | cmd); /* Start + Command index */
- xchg_spi((BYTE)(arg >> 24)); /* Argument[31..24] */
- xchg_spi((BYTE)(arg >> 16)); /* Argument[23..16] */
- xchg_spi((BYTE)(arg >> 8)); /* Argument[15..8] */
- xchg_spi((BYTE)arg); /* Argument[7..0] */
- n = 0x01; /* Dummy CRC + Stop */
- if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) + Stop */
- if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) Stop */
- xchg_spi(n);
- /* Receive command response */
- if (cmd == CMD12) xchg_spi(0xFF); /* Skip a stuff byte when stop reading */
- n = 10; /* Wait for a valid response in timeout of 10 attempts */
- do
- res = xchg_spi(0xFF);
- while ((res & 0x80) && --n);
- return res; /* Return with the response value */
- }
- /*--------------------------------------------------------------------------
- Public Functions
- ---------------------------------------------------------------------------*/
- /*-----------------------------------------------------------------------*/
- /* Initialize Disk Drive */
- /*-----------------------------------------------------------------------*/
- DSTATUS disk_initialize (
- BYTE drv /* Physical drive nmuber (0) */
- )
- {
- BYTE n, cmd, ty, ocr[4];
- if (drv) return STA_NOINIT; /* Supports only single drive */
- power_off(); /* Turn off the socket power to reset the card */
- if (Stat & STA_NODISK) return Stat; /* No card in the socket */
- power_on(); /* Turn on the socket power */
- FCLK_SLOW();
- for (n = 10; n; n--) xchg_spi(0xFF); /* 80 dummy clocks */
- ty = 0;
- if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */
- Timer1 = 100; /* Initialization timeout of 1000 msec */
- if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */
- for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF); /* Get trailing return value of R7 resp */
- if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */
- while (Timer1 && send_cmd(ACMD41, 1UL << 30)); /* Wait for leaving idle state (ACMD41 with HCS bit) */
- if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
- for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF);
- ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */
- }
- }
- } else { /* SDv1 or MMCv3 */
- if (send_cmd(ACMD41, 0) <= 1) {
- ty = CT_SD1; cmd = ACMD41; /* SDv1 */
- } else {
- ty = CT_MMC; cmd = CMD1; /* MMCv3 */
- }
- while (Timer1 && send_cmd(cmd, 0)); /* Wait for leaving idle state */
- if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */
- ty = 0;
- }
- }
- CardType = ty;
- deselect();
- if (ty) { /* Initialization succeded */
- Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */
- FCLK_FAST();
- } else { /* Initialization failed */
- power_off();
- }
- return Stat;
- }
- /*-----------------------------------------------------------------------*/
- /* Get Disk Status */
- /*-----------------------------------------------------------------------*/
- DSTATUS disk_status (
- BYTE drv /* Physical drive nmuber (0) */
- )
- {
- if (drv) return STA_NOINIT | STA_NODISK; /* Supports only single drive */
- return Stat;
- }
- /*-----------------------------------------------------------------------*/
- /* Read Sector(s) */
- /*-----------------------------------------------------------------------*/
- DRESULT disk_read (
- BYTE drv, /* Physical drive nmuber (0) */
- BYTE *buff, /* Pointer to the data buffer to store read data */
- DWORD sector, /* Start sector number (LBA) */
- UINT count /* Sector count (1..255) */
- )
- {
- if (drv || !count) return RES_PARERR;
- if (Stat & STA_NOINIT) return RES_NOTRDY;
- if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */
- if (count == 1) { /* Single block read */
- if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */
- && rcvr_datablock(buff, 512))
- count = 0;
- }
- else { /* Multiple block read */
- if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
- do {
- if (!rcvr_datablock(buff, 512)) break;
- buff += 512;
- } while (--count);
- send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
- }
- }
- deselect();
- return count ? RES_ERROR : RES_OK;
- }
- /*-----------------------------------------------------------------------*/
- /* Write Sector(s) */
- /*-----------------------------------------------------------------------*/
- DRESULT disk_write (
- BYTE drv, /* Physical drive nmuber (0) */
- const BYTE *buff, /* Pointer to the data to be written */
- DWORD sector, /* Start sector number (LBA) */
- UINT count /* Sector count (1..255) */
- )
- {
- if (drv || !count) return RES_PARERR;
- if (Stat & STA_NOINIT) return RES_NOTRDY;
- if (Stat & STA_PROTECT) return RES_WRPRT;
- if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */
- if (count == 1) { /* Single block write */
- if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */
- && xmit_datablock(buff, 0xFE))
- count = 0;
- }
- else { /* Multiple block write */
- if (CardType & CT_SDC) send_cmd(ACMD23, count);
- if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
- do {
- if (!xmit_datablock(buff, 0xFC)) break;
- buff += 512;
- } while (--count);
- if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
- count = 1;
- }
- }
- deselect();
- return count ? RES_ERROR : RES_OK;
- }
- /*-----------------------------------------------------------------------*/
- /* Miscellaneous Functions */
- /*-----------------------------------------------------------------------*/
- DRESULT disk_ioctl (
- BYTE drv, /* Physical drive nmuber (0) */
- BYTE ctrl, /* Control code */
- void *buff __attribute((unused)) /* Buffer to send/receive control data */
- )
- {
- DRESULT res;
- if (drv) return RES_PARERR;
- buff = 0;
- if (ctrl == CTRL_POWER) {
- power_off();
- return RES_OK;
- }
- if (Stat & STA_NOINIT) return RES_NOTRDY;
- res = RES_ERROR;
- switch (ctrl) {
- case CTRL_SYNC :
- if (select()) {
- deselect();
- res = RES_OK;
- }
- break;
- default:
- res = RES_PARERR;
- }
- deselect();
- return res;
- }
- /*-----------------------------------------------------------------------*/
- /* Device Timer Interrupt Procedure */
- /*-----------------------------------------------------------------------*/
- /* This function must be called in period of 10ms */
- void disk_timerproc (void)
- {
- BYTE n, s;
- n = Timer1; /* 100Hz decrement timer */
- if (n) Timer1 = --n;
- n = Timer2;
- if (n) Timer2 = --n;
- s = Stat;
- if (SOCKWP) /* Write protected */
- s |= STA_PROTECT;
- else /* Write enabled */
- s &= ~STA_PROTECT;
- if (SOCKINS) /* Card inserted */
- s &= ~STA_NODISK;
- else /* Socket empty */
- s |= (STA_NODISK | STA_NOINIT);
- Stat = s; /* Update MMC status */
- }
|