IJFW - IchigoJamのBASICプログラムをメモリカード(MMCまたは互換カード)に保存したり読み出したりできるプログラム。メモリカードにファームウェアのファイルを置くだけで、電源ON時に自動的に書き換える機能も搭載(一応こちらがメイン)。LPC1114FN28専用。

Dependencies:   mbed

参考URL http://www.cyberchabudai.org/index.php/entry?tag=IJFW

FatfsIJFW/FatfsIJFW.cpp

Committer:
oks486
Date:
2016-08-21
Revision:
2:daf6c4719496
Parent:
0:43cce7b453d0

File content as of revision 2:daf6c4719496:

#include "mbed.h"
#include "ff.h"
#include "FatfsIJFW.h"


// Command of memory card
const BYTE CMD0 = 0;			// GO_IDLE_STATE
const BYTE CMD1 = 1;			// SEND_OP_COND
const BYTE ACMD41 = 0x80+41;	// SEND_OP_COND
const BYTE CMD8 = 8;			// SEND_IF_COND
const BYTE CMD12 = 12;			// STOP_TRANSMISSION
const BYTE CMD16 = 16;			// SET_BLOCKLEN
const BYTE CMD17 = 17;			// READ_SINGLE_BLOCK
const BYTE CMD18 = 18;			// READ_MULTIPLE_BLOCK
const BYTE ACMD23 = 0x80+23;	// SET_WR_BLK_ERASE_COUNT
const BYTE CMD24 = 24;			// WRITE_BLOCK
const BYTE CMD25 = 25;			// WRITE_MULTIPLE_BLOCK
const BYTE CMD55 = 55;			// APP_CMD
const BYTE CMD58 = 58;			// READ_OCR

// Card type flag
int CardType;

// Extern to diskio.cpp
extern FatfsIJFW* _fatfs;


FatfsIJFW::FatfsIJFW(SPI* _spi, DigitalOut* _cs) : spi(_spi), cs(_cs) {
	_fatfs = this;
	Stat = 0;
	spi->format(8, 0);
	f_mount(NULL, "", 0);
}


int FatfsIJFW::mount() {
	Stat = 0;
	FRESULT res = f_mount(&fs, "", 1);
	return (int)res;
}


int FatfsIJFW::open(const char* name, const FileMode mode) {
	BYTE flags;

	if (mode == MODE_WR) {
		// Write and Read
		flags = FA_OPEN_EXISTING | FA_READ | FA_WRITE;
	} else if (mode == MODE_RO) {
		// Read Only
		flags = FA_OPEN_EXISTING | FA_READ;
	} else if (mode == MODE_APPEND) {
		// Append write
		flags = FA_OPEN_ALWAYS | FA_WRITE;
	} else if (mode == MODE_OVERWRITE) {
		// Overwrite
		flags = FA_CREATE_ALWAYS | FA_WRITE;
	}

    FRESULT res = f_open(&file, name, flags);

	if (mode == MODE_APPEND) {
		f_lseek(&file, f_size(&file));
	}
	
	return (int)res;
}


int FatfsIJFW::close() {
	FRESULT res = f_close(&file);
	return (int)res;
}


int FatfsIJFW::remove(const char* filename) {
	FRESULT res = f_unlink(filename);
	return (int)res;
}


int FatfsIJFW::mkdir(const char* filename) {
	FRESULT res = f_mkdir(filename);
	return (int)res;
}


int FatfsIJFW::read(char* buf, const int length) {
	UINT br;
	f_read(&file, buf, (UINT)length, &br);
	return (int)br;
}


int FatfsIJFW::write(const char* buf, const int length) {
	UINT br;
	f_write(&file, buf, (UINT)length, &br);
	return (int)br;
}


int FatfsIJFW::lseek(int pos) {
	return (int)f_lseek(&file, (DWORD)pos);
}


int FatfsIJFW::filesize() {
	return (int)f_size(&file);
}


void FatfsIJFW::timerproc() {
	if (timerCount) {
		timerCount--;
	}
}


void FatfsIJFW::deselect() {
	cs->write(1);
	spi->write(0xFF);
}


int FatfsIJFW::select() {
	cs->write(0);
	spi->write(0xFF);

	// Wait for card is ready
	if (waitReady(500)) {
		return 1;
	}

	// Timerout
	deselect();
	return 0;
}


int FatfsIJFW::waitReady(int wait) {
	BYTE res;

	timerCount = wait;
	do {
		res = spi->write(0xFF);
	} while (res != 0xFF && timerCount);

	return (res == 0xFF);
}


char FatfsIJFW::sendCommand(BYTE cmd, DWORD arg) {
	char res;

	// cmd is ACMD<n>
	if (cmd & 0x80) {
		cmd &= 0x7F;
		res = sendCommand(CMD55, 0);
		if (res > 1) {
			return res;
		}
	}

	// Select the card and wait for ready except to stop multiple block read
	if (cmd != CMD12) {
		deselect();
		if (!select()) {
			return 0xFF;
		}
	}

	// Send command packet
	spi->write(0x40 | cmd);
	spi->write((uint8_t)(arg >> 24));
	spi->write((uint8_t)(arg >> 16));
	spi->write((uint8_t)(arg >> 8));
	spi->write((uint8_t)arg);

	// Send CRC packet
	BYTE crc;
	if (cmd == CMD0) {
		crc = 0x95;
	} else if (cmd == CMD8) {
		crc = 0x87;
	} else {
		crc = 0x01;
	}
	spi->write(crc);

	// Diacard following one byte when CMD12
	if (cmd == CMD12) {
		spi->write(0xFF);
	}

	// Wait for response
	for (int i = 0; i < 10; i++) {
		res = spi->write(0xFF);
		if (!(res & 0x80)) {
			break;
		}
	}

	return res;
}


int FatfsIJFW::rcvDataBlock(BYTE *buff, UINT btr) {
	BYTE token;
	timerCount = 200;

	do {
		token = spi->write(0xFF);
	} while ((token == 0xFF) && timerCount);

	if(token != 0xFE) {		// if invalid token or timeout
		return 0;
	}

	// Receive the data block
	WORD data;
	spi->format(16, 0);		// 16bit mode

	for (int i = 0; i < btr; i += 2) {
		data = spi->write(0xFFFF);
		buff[i] = data >> 8;
		buff[i + 1] = data;
	}

	spi->write(0xFFFF);		// CRC
	spi->format(8, 0);		// 8bit mkode

	return 1;
}


int FatfsIJFW::sendDataBlock(const BYTE *buff, BYTE token) {
	// Wait for card is ready
	if (!waitReady(500)) {
		return 0;		
	}
	
	spi->write(token);
	if (token != 0xFD) {		// if token is not StopTran
		// Send the data block
		spi->format(16, 0);		// 16bit mode

		for (int i = 0; i < 512; i += 2) {
			unsigned short data = (buff[i] << 8) | buff[i + 1];
			spi->write(data);
		}
		spi->write(0xFFFF);		// CRC
		spi->format(8, 0);		// 8bit mode

		BYTE res = spi->write(0xFF);	// Receive data response
		if ((res & 0x1F) != 0x05) {		// if the data packet was not accepted
			return 0;
		}
	}

	return 1;
}


DSTATUS FatfsIJFW::disk_initialize(BYTE drv) {
	if (drv) {		// drive 0 only
		return STA_NOINIT;
	}

	// Set spi slow clock
	spi->frequency(400000);
    cs->write(1);

	// Send 80 dummy clocks
    for (int i = 0; i < 10; i++) {
		spi->write(0xFF);
	}

	CardType = 0;
	if (sendCommand(CMD0, 0) == 1) {	// reset card
		timerCount = 1000;				// Timeout is 1ms

		if (sendCommand(CMD8, 0x1AA) == 1) {
			// Get value of R7 response
			int ocr[4];
			for (int i = 0; i < 4; i++) {
				ocr[i] = spi->write(0xFF);
			}

			if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
				// Wait for end of initialization
				while (timerCount > 0 && sendCommand(ACMD41, 1UL << 30));

				if (timerCount && sendCommand(CMD58, 0) == 0) {
					// Check CCS bit
					for (int i = 0; i < 4; i++) {
						ocr[i] = spi->write(0xFF);
					}
					if (ocr[0] & 0x40) {
						CardType = 0x04 | 0x08;
					} else {
						CardType = 0x04;
					}
				}
			}
		} else {
			if (sendCommand(ACMD41, 0) <= 1) {
				CardType = 0x02;
				while (timerCount > 0 && sendCommand(ACMD41, 0));	// initialization
			} else {
				CardType = 0x01;
				while (timerCount > 0 && sendCommand(CMD1, 0));	// initialization
			}

			if (timerCount == 0 || sendCommand(CMD16, 512) != 0) {
				CardType = 0;
			}
		}
	}

	deselect();

	if (CardType) {
		spi->frequency(1000000);	// Set spi 1MHz
		Stat &= ~STA_NOINIT;		// Clear STA_NOINIT flag
	} else {
		select();
		deselect();
		Stat = STA_NOINIT;
	}

	return Stat;
}


DSTATUS FatfsIJFW::disk_status(BYTE drv) {
	return RES_OK;
}


DRESULT FatfsIJFW::disk_read(BYTE drv, BYTE *buff, DWORD sector, UINT count) {
	// if drive is not ready
	if (Stat & STA_NOINIT) {
		return RES_NOTRDY;
	}

	if (drv || !count) {
		return RES_PARERR;
	}

	// if byte address is BA
	if (!(CardType & 0x08)) {
		sector *= 512;
	}

	// Read a single block or multiple blocks
	BYTE cmd = count > 1 ? CMD18 : CMD17;
	if (sendCommand(cmd, sector) == 0) {
		while (count > 0) {
			if (!rcvDataBlock(buff, 512)) {
				if (cmd == CMD18) {
					sendCommand(CMD12, 0);
				}
				break;
			}
			buff += 512;
			count--;
		}
	}
	deselect();

	return count ? RES_ERROR : RES_OK;
}


DRESULT FatfsIJFW::disk_write(BYTE drv, const BYTE *buff, DWORD sector, UINT count) {
	// if drive is not ready
	if (Stat & STA_NOINIT) {
		return RES_NOTRDY;
	}
	
	if (drv || !count) {
		return RES_PARERR;
	}

	// if byte address is BA
	if (!(CardType & 0x08)) {
		sector *= 512;
	}

	// Write a single block or multiple blocks
	DRESULT res = RES_ERROR;
	if (count > 1) {
		if (CardType & 0x06) {
			sendCommand(ACMD23, count);		// Send predefine number of sectors
		}
		if (sendCommand(CMD25, sector) == 0) {
			while (count > 0) {
				if (!sendDataBlock(buff, 0xFC)) {
					break;
				}
				buff += 512;
				count--;
			}
			if (sendDataBlock(0, 0xFD)) {
				res = RES_OK;
			}
		}
	} else {
		if (sendCommand(CMD24, sector) == 0) {
			if (sendDataBlock(buff, 0xFE)) {
				res = RES_OK;
			}
		}
	}
	deselect();

	return res;
}


DRESULT FatfsIJFW::disk_ioctl(BYTE drv, BYTE cmd, void* buff) {
	// ioctl is not supported
	return RES_ERROR;
}