Small SD card code?

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() {}