Pages: [1] 2   Go Down
Author Topic: Small SD card code?  (Read 2795 times)
0 Members and 1 Guest are viewing this topic.
SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 135
Posts: 6782
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.)
Logged

Leighton Buzzard, UK
Offline Offline
Edison Member
*
Karma: 21
Posts: 1339
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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]
Logged

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

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 135
Posts: 6782
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Leighton Buzzard, UK
Offline Offline
Edison Member
*
Karma: 21
Posts: 1339
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ah!
Logged

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

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 129
Posts: 8583
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I for one would think a boot from SD feature would be good, it would be a great way to do field upgrades.

______
Rob
« Last Edit: January 30, 2012, 07:27:36 am by Graynomad » Logged

Rob Gray aka the GRAYnomad www.robgray.com

Leighton Buzzard, UK
Offline Offline
Edison Member
*
Karma: 21
Posts: 1339
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

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

Seattle, WA
Offline Offline
God Member
*****
Karma: 11
Posts: 673
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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


0
Offline Offline
Edison Member
*
Karma: 67
Posts: 1652
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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:
#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:
#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() {}
« Last Edit: January 31, 2012, 01:23:01 pm by fat16lib » Logged

0
Offline Offline
Edison Member
*
Karma: 67
Posts: 1652
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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:
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.

* SdBoot20120201.zip (5.62 KB - downloaded 16 times.)
Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 135
Posts: 6782
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?

Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 544
Posts: 27352
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

0
Offline Offline
Edison Member
*
Karma: 67
Posts: 1652
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

westfw,

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

Leighton Buzzard, UK
Offline Offline
Edison Member
*
Karma: 21
Posts: 1339
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@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!
« Last Edit: February 01, 2012, 05:43:10 pm by mmcp42 » Logged

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

0
Offline Offline
Edison Member
*
Karma: 67
Posts: 1652
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
  if (!sd.init(4)) {
To run readTest edit SdBoot.cpp and change the chip select defs like this:
Code:
// 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.
Logged

Leighton Buzzard, UK
Offline Offline
Edison Member
*
Karma: 21
Posts: 1339
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks I'll try it first thing in the morning
Cheers
Mike
Logged

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

Pages: [1] 2   Go Up
Jump to: