How much flash could be allowed for SD read?
I think raw SD read could be quit small if specialized for the avr processor.
If you limited the file to being in the root directory and contiguous, a lot of code would be eliminated.
Files are contiguous unless you have deleted other files and the SD is nearly full.
I will look at what can be done.
Edit: I did some tests and for a 2GB standard SD card formatted FAT16 It takes about 550 bytes of flash to initialize the card, read the master boot record, volume boot block, and first directory block.
Here is the raw init/read code for a 328. It is not very neat, just a prototype test.
#include <avr/io.h>
/** SEND OPERATING CONDITIONS */
uint8_t const ACMD41 = 0X29;
uint8_t const CMD0 = 0;
//uint8_t const CMD8 = 8;
/** READ_BLOCK */
uint8_t const CMD17 = 0X11;
/** APP_CMD - escape for application specific command */
uint8_t const CMD55 = 0X37;
uint8_t const R1_IDLE_STATE = 1;
uint8_t const R1_READY_STATE = 0;
// start data token for read or write
uint8_t const DATA_START_BLOCK = 0XFE;
uint8_t const SS_BIT = 2;
uint8_t const MOSI_BIT = 3;
uint8_t const MISO_BIT = 4;
uint8_t const SCK_BIT = 5;
uint8_t SdCommand(uint8_t cmd, uint32_t arg);
//------------------------------------------------------------------------------
// inline SPI functions
/** Send a byte to the card */
static void spiSend(uint8_t b) {SPDR = b; while(!(SPSR & (1 << SPIF)));}
/** Receive a byte from the card */
static uint8_t spiRec(void) {spiSend(0XFF); return SPDR;}
/** Set Slave Select high */
static void chipSelectHigh(void) {
PORTB |= (1 << SS_BIT);
}
/** Set chip select low */
static void chipSelectLow(void) {
PORTB &= ~(1 << SS_BIT);
}
//------------------------------------------------------------------------------
static bool waitForToken(uint8_t token) {
for (uint8_t i = 0; i != 0XFF; i++) {
if (spiRec() == token) return true;
}
return false;
}
//------------------------------------------------------------------------------
static uint8_t SdAcmd(uint8_t cmd, uint32_t arg) {
SdCommand(CMD55, 0);
return SdCommand(cmd, arg);
}
//------------------------------------------------------------------------------
uint8_t SdCommand(uint8_t cmd, uint32_t arg) {
uint8_t r1;
// select card
chipSelectLow();
waitForToken(0XFF);
// send command
spiSend(cmd | 0x40);
// send argument
for (int8_t s = 24; s >= 0; s -= 8) spiSend(arg >> s);
// send CRC
uint8_t crc = 0XFF;
if (cmd == CMD0) crc = 0X95; // correct crc for CMD0 with arg 0
// if (cmd == CMD8) crc = 0X87; // correct crc for CMD8 with arg 0X1AA
spiSend(crc);
// wait for response
for (uint8_t retry = 0; ((r1 = spiRec()) & 0X80) && retry != 0XFF; retry++);
return r1;
}
//------------------------------------------------------------------------------
bool SdInit() {
uint8_t r;
uint16_t retry = 0;
DDRB = (1 << SS_BIT) | (1 << MOSI_BIT) | (1 << SCK_BIT);
chipSelectHigh();
// Enable SPI, Master, clock rate f_osc/64
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1);
// must supply min of 74 clock cycles with CS high.
for (uint8_t i = 0; i < 10; i++) spiSend(0XFF);
// command to go idle in SPI mode
while ((r = SdCommand(CMD0, 0)) != R1_IDLE_STATE) {
if (++retry == 0) return false;
}
// start initialization and wait for completed initialization
while ((r = SdAcmd(ACMD41, 0)) != R1_READY_STATE) {
if (++retry == 0) return false;
}
chipSelectHigh();
return true;
}
//------------------------------------------------------------------------------
bool SdRead(uint32_t blockNumber, uint8_t* dst) {
if (SdCommand(CMD17, blockNumber << 9)) {
return false;
}
// wait for start of data
if (!waitForToken(DATA_START_BLOCK)) return false;
for (uint16_t i = 0; i < 512; i++) {
dst[i] = spiRec();
}
// first CRC byte
spiRec();
// second CRC byte
spiRec();
chipSelectHigh();
return true;
}
Here is the include to make it a library:
#include <avr/io.h>
bool SdInit();
bool SdRead(uint32_t blockNumber, uint8_t* dst);
And here is a test sketch to read the first directory block:
#include <SdBoot.h>
uint8_t block[512];
#ifdef DBG_PRINT
void printBlock() {
for( uint16_t i = 0; i < 512; i++) {
if ((i & 15) == 0){
Serial.println();
}else {
Serial.write(' ');
}
Serial.print(block[i] >> 4, HEX);
Serial.print(block[i] & 15, HEX);
}
Serial.println();
}
#endif // DBG_PRINT
//------------------------------------------------------
void setup() {
bool b;
#ifdef DBG_PRINT
Serial.begin(9600);
while (!Serial.available());
#endif // DBG_PRINT
b = SdInit();
// Serial.println(b, DEC);
// read MBR
b = SdRead(0, block);
// Serial.println(b, DEC);
// address of FAT volume
uint32_t s = *(uint32_t*)&block[454];
// read volume boot block
b = SdRead(s, block);
// FAT size
uint16_t f = *(uint16_t*)&block[22];
// address of directory
uint32_t d = s + 2*f + 1;
// read first block of directory
b = SdRead(d, block);
#ifdef DBG_PRINT
printBlock();
#endif // DBG_PRINT
}
void loop() {}