TinyFAT Library

The SDuFAT library has some basic function for file log, can not meet some more complex requirements. Now Henning Karlsen publish a TinyFAT library and it’s really more powerful than former. And it had been designed to integrate with ITDB02_Graph and ITDB02_Graph16 - so now you can easy to use this library to read out the image in the SD cards and display on ITBD02 LCD module.

The library supports FAT16 formatted SD cards up to 2GB in size. 4GB FAT16 formatted SD cards might work, but is untested. Long filenames are not supported. Keep you filenames compliant with the old 8.3 standard.

The SD card should be connected to the SPI-pins on your Arduino. The Stackable SD Card Shield is compatible with it.

Structures:

file.buffer[];
Buffer used for reading and writing SD-card sectors.
Variables:
file.buffer[0-511]: Byte-array to hold one sector of data.

file.MBR;
Master Boot Record of the SD card.
Variables:
Part1Type: Partition1 Type. Only types 0×04, 0×06 and 0×86 can be used.
part1Start: First sector of Partition1.
part1Size: Number of sectors in Partition1.

file.BS;
Boot Sector of Partition1.
Variables:

sectorsPerCluster: Number of sectors per cluster.
reservedSectors: Number of reserved sectors.
fatCopies: Number of File Allocation Tables in partition. Almost always 2.
rootDirectoryEntries: Maximum number of root directory entries.
totalFilesystemSectors: Total number of sectors available to the file system.
sectorsPerFAT: Sectors per File Allocation Table.
hiddenSectors: Number of hidden sectors preceding the partition that contains this FAT volume.
partitionSerialNum: Partition serial number.
fat1Start: First sector of primary File Allocation Table.
fat2Start : First sector of secondary File Allocation Table.
partitionSize: Size of partition in MB.

file.DE;
Directory Entry structure. Used by findFirstFile() and findNextFile().
Variables:

filename: Char array containing the file’s name.
fileext: Char array containing the file’s extension.
attributes: File attributes.
time: File creation time (encoded).
date: File creation date (encoded).
startCluster: First cluster of file data.
fileSize: File size in bytes.

Functions:

file.initFAT(spispeed) : Initialize the interface, and connect to the SD card.

file.findFirstFile(DEstruct) : Find information about the first file in the root directory.

file.findNextFile(DEstruct) : Find information about the next file in the root directory.

file.openFile(filename, filemode) : Open a file for reading.

file.readBinary() : Read the next sector of an open binary file.

file.readLn(buffer, bufSize) : Read the next line of text from an open text-file.

file.writeLn(text) : Append a line of text to a text-file.

file.closeFile() : Close the currently open file.

file.exists(filename) : Check if a file exists.

file.rename(from-name, to-name) : Rename a file.

file.delFile(filename) : Delete a file.

file.create(filename) : Create a new, empty file.

I am using an ITDB02S with a Mega2560. I can make it work with Henning Karlsen's libraries. Graphics and touchscreen are fine. I can access the SD card with the tinyFAT library, but only if I use the default SPI select ( pin 53 on the Mega). However, I have multiple SPI devices and I need to use a different select pin. The tinyFAT setSSpin() function is not helpful. Is there a way to do this?

Yes I would like to know that too, im using it with ethernet shield, SD works fine without the shield but when I use file.setSSpin(8); // pin 8 SD SS pin
the SD doesn't work

but when I use file.setSSpin(8); // pin 8 SD SS pin
the SD doesn't work

I don't think you can use the smiley face pin as a slave select pin.

Bugger, that's why it's not working then...

It should have read file.setSSpin();
when i want to change SD SS to pin 8 it hangs, i dont know if its the SD or duemilanove thats hanging, SD & ethernet work seperatly with SS on pin 10 but not together even if i set SS to 8 or 9, Im starting to get anoyed with it now

For the SPI to work, pin 10 (Uno, etc), or pin 53 (Mega), must be set to output, whatever pin you eventually assign to SS. If this is done, you should be able to use pins 8 or 9, as long as they are also set as outputs. As for using other pins as SS, or debugging, keep reading.

TinyFAT uses an auxiliary file called mmc.cpp. This is where the SS initialization and SPI handshaking occur. For my Mega application, I found that it was reassigning the SS pin, but not correctly setting the DDR register associated with the new pin's port. This is not very complicated. Each physical I/O pin on the Arduino belongs to a port (PORTA,PORTB,PORTC,...) and each port has a data direction register associated with it (DDRA, DDRB, ...).

mmc.cpp does its SPI protocol stuff by port manipulation. It needs to know 3 things at initialization: The SS pin, the PORT associated with the SS pin, the DDR for that port. For the most Arduinos except the Mega, this defaults to pin 10, PORTB, DDRB. Pins 8 and 9 should also be PORTB and DDRB. For the Mega, the default SS is pin 53, PORTB, DDRB. Other pins may have other ports and DDR's. But you can use any other pin available if you just make sure mmc.cpp is using the correct 3 parameters for its calls. I was able to do this for the Mega by cleaning up mmc.cpp.

Here are the relevant parts of mmc.cpp that I changed. I have set it to use pin 42 as the default SS for the Mega, but it can be set to any pin.

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
	byte SS= 7;  //using PL7 (D42) as SS
	byte SSlegacy = 0;	// bit position of chip select on the legacy SPI port
					// for Mega: PB0 (pin d53), for Uno: PB2 (pin d10) 

	volatile uint8_t *SSPORT = &PORTL;
	volatile uint8_t *SSDDR =  &DDRL;
	#define MOSI	2
	#define MISO	3
	#define SCK	1
#else
    byte SS = 2;
	byte SSlegacy = 2;
	volatile uint8_t *SSPORT = &PORTB;
	volatile uint8_t *SSDDR =  &DDRB;
	#define MOSI	3
	#define MISO	4
	#define SCK        5
#endif

byte mmc::initialize(byte speed) {
  byte  i;
  uint16_t counter;
  uint32_t answer;

  disk_state = DISK_ERROR;

  // setup SPI I/O pins

  PORTB |=  _BV(SCK)  | _BV(SSlegacy) | _BV(MISO); // set SCK, pullup on MISO
  DDRB  |=  _BV(SCK)   | _BV(SSlegacy) | _BV(MOSI); // set SCK/MOSI as output
  DDRB  &= ~_BV(MISO); 
// set MISO as input

  *SSPORT |=  _BV(SS); // set SS
  *SSDDR  |=  _BV(SS) ; // set SS as output

  // setup SPI interface:
  //   interrupts disabled, SPI enabled, MSB first, master mode,
  //   leading edge rising, sample on leading edge, clock = f/4,
  SPCR = B01010000 | speed;

  // Enable SPI double speed mode -> clock = f/8
  //  SPSR = _BV(SPI2X);

  // clear status
  i = SPSR;

  // clear recieve buffer
  i = SPDR;

  SPI_SS_HIGH();

  // Send 80 clks
  for (i=0; i<10; i++) {
    spiTransferByte(0xFF);
  }

  // Reset card
  i = sendCommand(GO_IDLE_STATE, 0, 1);
  if (i != 1) {

    return STA_NOINIT | STA_NODISK;
  }

  counter = 0xffff;
  // According to the spec READ_OCR should work at this point
  // without retries. One of my Sandisk-cards thinks otherwise.
  do {
    // Send CMD58: READ_OCR
    i = sendCommand(READ_OCR, 0, 0);
    if (i > 1) {
      // kills my Sandisk 1G which requires the retries in the first place
      // deselectCard();
    }
  } 
  while (i > 1 && counter-- > 0);

  if (counter > 0) {
    answer = spiTransferLong(0);

    // See if the card likes our supply voltage
    if (!(answer & SD_SUPPLY_VOLTAGE)) {
      // The code isn't set up to completely ignore the card,
      // but at least report it as nonworking
      deselectCard();
      return STA_NOINIT | STA_NODISK;
    }
  }

  // Keep sending CMD1 (SEND_OP_COND) command until zero response
  counter = 0xffff;
  do {
    i = sendCommand(SEND_OP_COND, 1L<<30, 1);
    counter--;
  } 
  while (i != 0 && counter > 0);

  if (counter==0) {
    return STA_NOINIT | STA_NODISK;
  }

  // Send MMC CMD16(SET_BLOCKLEN) to 512 bytes
  i = sendCommand(SET_BLOCKLEN, 512, 1);
  if (i != 0) {
    return STA_NOINIT | STA_NODISK;
  }

  // Thats it!
  disk_state = DISK_OK;
  return RES_OK;
}
void SPI_SS_HIGH(){	*SSPORT |= _BV(SS); }

void SPI_SS_LOW(){	*SSPORT &= ~_BV(SS);}

static void deselectCard(void) {
  // Send 8 clock cycles
  SPI_SS_HIGH();
  spiTransferByte(0xff);
}

byte mmc::initialize(byte speed) {
  byte  i;
  uint16_t counter;
  uint32_t answer;

  disk_state = DISK_ERROR;

  // setup SPI I/O pins

  PORTB |=  _BV(SCK)  | _BV(SSlegacy) | _BV(MISO); // set SCK, pullup on MISO
  DDRB  |=  _BV(SCK)   | _BV(SSlegacy) | _BV(MOSI); // set SCK/MOSI as output
  DDRB  &= ~_BV(MISO); 
// set MISO as input

  *SSPORT |=  _BV(SS); // set SS
  *SSDDR  |=  _BV(SS) ; // set SS as output

  // setup SPI interface:
  //   interrupts disabled, SPI enabled, MSB first, master mode,
  //   leading edge rising, sample on leading edge, clock = f/4,
  SPCR = B01010000 | speed;

  // Enable SPI double speed mode -> clock = f/8
  //  SPSR = _BV(SPI2X);

  // clear status
  i = SPSR;

  // clear recieve buffer
  i = SPDR;

  SPI_SS_HIGH();

  // Send 80 clks
  for (i=0; i<10; i++) {
    spiTransferByte(0xFF);
  }


void mmc::setSSpin(const uint8_t _pin) {
	#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
		if (_pin==53) { 
			SSPORT = &PORTB;
			SSDDR = &DDRB;
			SS=0;
		}
		else if ((_pin>=42) and (_pin<=49)) {
			SSPORT = &PORTL;
			SSDDR = &DDRL;
			SS= 49 - _pin;
		}
	#else	
		if ((_pin>=8) and (_pin<=10))	{ 
				SS=_pin-8;
				SSPORT = &PORTB; 
				SSDDR = &DDRB;
		}
	#endif
	}

In the above posted code, in the method mmc::setSSpin(pin), I am only allowing the Mega SS pin to range through PORTL (pins 42-49) and the Uno SS pin to range through PORTB pins 8-10. To use other pins outside this range for SS, just expand the conditional to assign SSPORT and SSDDR to the proper values for the pins you might want to use. You will need to look at an Arduino pin map. I reserved PORTL for SS choices for my Mega application. That gave me 8 more SS pins, so potentially 8 more SPI devices beyond the default device. So I didn't bother to code for more options.

any someone have a simulation for Stackable SD Card Shield if we use Arduino?

Sorry if this sounds like a dumb question, but I'm new to this library and to writing to SD cards in general.

It appears that using file.writeLn(text); does ONLY that ... writes text to a file. I am wanting to log data from a magnetometer and need to create a data string to be written in CSV format. As an example, I want to execute something like this...

String datastring = (valuex + "," + valuey + "," + valuez);
file.writeLn(datastring);

But the library won't allow anything except text between quotes. This to me seems to be pretty useless if I cannot pass anything but ASCII text to the function. Is there a method to create a char array containing my data and the comma seporator, and append each character to the same line, and then a CR / LF at the end of the array? This is so frustrating because I spent a great deal of time getting SPI to work with my shield and SD card. The datalogging is the only obstacle in the way of completing this project !!!

Any advice would be most appreciated...

Look at sprintf() You format a string that you can then write to the file.

Thanks, but I'm coding in the arduino IDE wiring language. I will see if there is a comparable function.

Sorry if this sounds like a dumb question

It is, since the class you are referring to doesn't even HAVE a writeLn() method.

The print() and println() methods that it DOES have KNOW how to convert floats, doubles, ints, and bazookas (just kidding) to text.

This to me seems to be pretty useless if I cannot pass anything but ASCII text to the function.

You don't like the String library? Write your own, then.

Better yet, just don't use it. You don't need it.

Is there a method to create a char array containing my data and the comma seporator, and append each character to the same line, and then a CR / LF at the end of the array?

Of course there is, and marco_c pointed it out.

Thanks, but I'm coding in the arduino IDE wiring language.

What the hell is that? The Arduino is programmed using C++, not Java, not wiring, not some proprietary language. Plain C++.

I must have struck a nerve with someone. I meant no disrespect...just that I was trying to figure out how to use tinyFat to write data not text to a file. I know the arduino is a C environment, but if you look on the official arduino page they call it a 'wiring' language.

Como estan amigos .....me podrian ayudar con un problema que tengo..... el problema es que utilizo la libreria tinyFAT y la biblioteca UTouch con un Arduino Mega2560 y una pantalla 3.2" LCD TF Card Shiel REV.B al momento de cargar el progama bien solo me funciona la libreria tinyFAT y la de UTouch no me funciona y asi mismo me funciona la UTouch y la tinyFAT no me funciona el problema radica que estas librerias utilizan el mismo MOSI, MISO y SCK y al momento de activar ya sea el pin para el touch o el pin de sd solo me funciona uno y no los dos a la ves en el mismo programa espero me puedan ayudar y de antemano estare muy agradecido...............

How are friends ..... I could help with a problem that I have ..... the problem is that I use the library and tinyFAT uTouch Mega2560 library with Arduino and a screen 3.2 "LCD TF Card Shiel REV.B to time to load the progam works well only me and the library tinyFAT uTouch not work and so I myself uTouch works and does not work tinyFAT me the problem is that these libraries use the same MOSI, MISO and SCK and when activate either touch or pin for pin sd works just me one and not to see the two in the same program and I hope I can help I will be very grateful in advance ............ ...

Estimates

It happens the same as Esllerena, combining tinyFAT library and uTouch the pictures are displayed correctly stops working but the touch part must be that both the touch part to the SDcard used the same pins MOSI, MISO and SCK , note that I’m using ARDUINO MEGA board. (separate libraries work properly)

Please show your support and fix this, or another way to handle images in raw format and can be used uTouch .. thanks for your help

I'm new here, I don't know if I have to post a new topic or just continue here, anyway the help needed is about the same library TinyFat.

I'm using a Mega 2560 R3 and a 3.2 TFT LCD which have a SD card socket and it uses this controller "ILI9486/ILI9488". The SD card is formated FAT16 as needed by the library.
I use TinyFat library to read data files from the SD card and display it to the screen with no problem.

the library didn't offer to modify data by accessing for example a line(the data I know exactly which line and column)
one text file example "data.txt" that I need to modify is like this(the data file I create first on the computer ready with some values):

Interval: 10
Quantity: 10
Repeat: 10

what I need is, to change the value interval from 10 to 5...for example
by using "file.writeLn" from TinyFat, I can append the new values, but it will be just the same lines repeated, and if I need to modify the values 10 times the file will be 33 lines, but only 3 lines from the file are useful, that is not a solution.

so what I tried is, I read all values, delete the "data.txt" and create new file with same name and then save the data again.

later when the code try to access again the file, I get the error "FILE_IS_EMPTY"
I tried to read the SD card on the computer, I get the error
"Cannot open the E:\data.txt file Make sure a disk is in the drive you specified."

I think the file is not empty but corrupted, because when I tried to change the content what I write to data.txt the size increase and decrease depending on how many lines I write there.

"file.exists", "file.delFile" are working, but the problem is after "file.create"

I tried the example "Demo_writeLn.pde" that is included with the library , and the same problem, the file is created and I can see on the computer that the size of the file is some xxKb, but when I try to open it it gives "Cannot open the E:\BIGFILE.TXT file Make sure a disk is in the drive you specified."

I can post the code if needed, but the library and examples are available here from the author's webpage: tinyFAT - Rinky-Dink Electronics

Sorry for this long post, but I tried to make my question more clear.

any suggestions or solutions?