Go Down

Topic: Small SD card code? (Read 3078 times) previous topic - next topic

westfw

Is there some small (flash size) FAT16/FAT32 code, suitable for use in a bootloader?  (that means it only needs to access one file, relatively slowly, and can be relatively extravagant with its use of RAM.)

mmcp42

i often wondered if one could write a nonFAT interface
just treat the SD card like so much EEPROM

you could then write to it using a 'duino and read it the same way
unless you actually need to talk to it from a PC, it would never know

alternatively, could one go further and treat it like EEPROM and write some PC software to "fill" it

[/wonder]
there are only 10 types of people
them that understands binary
and them that doesn't

westfw

Sure.  But I think the idea is to be able to plug the flash card into a PC with a standard reader, copy "arduino.boot" to it, and then have the arduino find that when it boots up with/from the same flash card.

mmcp42

there are only 10 types of people
them that understands binary
and them that doesn't

Graynomad

#4
Jan 30, 2012, 01:26 pm Last Edit: Jan 30, 2012, 01:27 pm by Graynomad Reason: 1
I for one would think a boot from SD feature would be good, it would be a great way to do field upgrades.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

mmcp42

of course,
if you format the card on your PC, then upload the boot image file
it SHOULD always start at the same address on the card
all you need to work out is the length
that ought to simplify the problem quite a bit

and if you are in control of the boot image format (and why not!)
you could even store the size at the front of the file
there are only 10 types of people
them that understands binary
and them that doesn't

Have you tried chaN fatfs?  http://elm-chan.org/fsw/ff/00index_e.html

fat16lib

#7
Jan 31, 2012, 04:08 pm Last Edit: Jan 31, 2012, 07:23 pm by fat16lib Reason: 1
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.
Code: [Select]

#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:
Code: [Select]

#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:
Code: [Select]

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

fat16lib

Here is a more complete SD read program that might be small enough to use in a boot loader.  You need to login to download the attached file.

It works with FAT16 formatted standard SD cards on 168/328 Arduinos.  It opens a file and reads it a byte at a time.

It appears to take about 1200 bytes of flash.  This sketch takes 1618 bytes on 0022.
Code: [Select]

#include <SdBoot.h>
void setup() {
  // open TEST.BIN
  fileOpen((uint8_t*)"TEST    BIN");
  // read a byte
  fileRead();
}
void loop() {}

An empty sketch takes 450 bytes:
Code: [Select]

void setup() {}
void loop() {}

Here is the readme.txt file:
Quote

Here is a small file read program.  It appears to take about 1200 bytes of flash.

It could be optimized more and the retry timeouts need adjusting.

It runs on a 168/328 Arduino with an SD module that uses pin 10 for chip select.

To change the chip select pin edit this section of SdBoot.cpp

// use pin 10 for chip select
#define CS_DDR DDRB
#define CS_PORT PORTB
uint8_t const CS_BIT = 2;

It is packaged as a library for testing. 

Just copy the SdBoot folder to your libraries folder and run the examples.

There are three examples:

writeTest.pde write a four MB test file. It requires SdFat.

readTest.pde reads the test file.

sizeTest.pde shows approximately how much flash is used.

westfw

Quote
It appears to take about 1200 bytes of flash.

Thanks!  I think 1200 bytes is definitely small enough to be interesting.  That would mean (probably) a 2k bootloader (same as the original arduino bootloader) that could do both serial and SD.

Um.  Was there supposed to be a link in that message?


CrossRoads

That is awesome! Combine 512 byte standard bootloader with 1200 byte bootload from SD card if CD signal is detected on a '1284 would be a great combo.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

fat16lib

westfw,

The library is an attachment to the message with the readme.txt quote.  You must be logged in to see it.

mmcp42

#12
Feb 01, 2012, 11:04 pm Last Edit: Feb 01, 2012, 11:43 pm by mmcp42 Reason: 1
@fat16lib
just trying it with IDE 1.0
just a couple of bytes bigger

word of advice for a dummy, please:
my datalogger shield has CS on pin D4

what should my settings be:
#define CS_DDR DDRB
#define CS_PORT PORTB
uint8_t const CS_BIT = 2;

your "tiny" sketch takes 1634 bytes with IDE 1.0, so only 16 bytes bigger!
there are only 10 types of people
them that understands binary
and them that doesn't

fat16lib

I have tested the sketches with an Enet shield which has pin 4 as SD chip select.  The sketches are designed to disable the Enet SPI part which uses pin 10 as chip select.

This code currently works only on 168/328 Arduinos.

In writeTest change init to this:
Code: [Select]

  if (!sd.init(4)) {

To run readTest edit SdBoot.cpp and change the chip select defs like this:
Code: [Select]

// use pin 4 for chip select
#define CS_DDR DDRD
#define CS_PORT PORTD
uint8_t const CS_BIT = 4;

Remember this is not well tested code.  I am trying to help westfw and hope he will pursue an SD boot loader.

File names are always eleven bytes, alpha are caps.

The first eight bytes contain the file name with blank fill.

The last three bytes contain the file extension with blank fill.

mmcp42

Thanks I'll try it first thing in the morning
Cheers
Mike
there are only 10 types of people
them that understands binary
and them that doesn't

Go Up