| 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 volatileDSTATUS Stat = STA_NOINIT;	/* Disk status */static volatileBYTE Timer1, Timer2;	/* 100Hz decrement timer */staticBYTE 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.   */staticvoid 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;}staticvoid 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 */staticBYTE 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 */staticvoid 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 */staticvoid 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                                                   *//*-----------------------------------------------------------------------*/staticint 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                                 *//*-----------------------------------------------------------------------*/staticvoid deselect (void){	CS_HIGH();	xchg_spi(0xFF);	/* Dummy clock (force DO hi-z for multiple slave SPI) */}/*-----------------------------------------------------------------------*//* Select the card and wait for ready                                    *//*-----------------------------------------------------------------------*/staticint 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                                        *//*-----------------------------------------------------------------------*/staticint 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                                             *//*-----------------------------------------------------------------------*/staticint 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                                          *//*-----------------------------------------------------------------------*/staticBYTE 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 */}
 |