[TUT][C] Simple FAT and SD tutorial

Last post
37 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

After coming across quite many SD tutorials that are based on Petit FS or FatFS and guide the user through modifying these libraries, I thought it would be interesting to make one that would really start from the fundamentals of reading FAT file systems, so you'd actually understand all of the code, not just the pins you connected to your MCU.

The first part of this effort is now available, I'm planning a similar 3-5 part layout as my previous V-USB tutorial I posted here a while ago:

http://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/

The second part covers reading longer files using the file allocation table, and converts everything into a compact and portable library:

http://codeandlife.com/2012/04/07/simple-fat-and-sd-tutorial-part-2/

The third part introduces the hardware built around ATmega88 and communicating with the SD card using SPI.

http://codeandlife.com/2012/04/25/simple-fat-and-sd-tutorial-part-3/

The last part combines the information in parts 2 and 3 to create a simple FAT16 library for reading any 1-2 GB card.

http://codeandlife.com/2012/04/27/simple-fat-and-sd-tutorial-part-4/

I might also do additional parts covering FAT32, SDHC or file writing, depending on reader interest. All and any feedback is appreciated, if you have some specific things you'd like me to cover in the future, please let me know!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Excellent communications skills and very well organised. Look forward to parts 2 and 3.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks! I do like writing tutorials a lot, it's a great way to organize my own thoughts on a given subject.

The part 2 has been published, ending up with a FAT16 library that should be easily ported to AVR (doing that in part 3). I ended up with a memory footprint of 51 bytes instead of the 44 in Petit FatFS. On the other hand, I prefer my version for code readability. :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jokkebk wrote:
The part 2 has been published, [...]
FYI, the updated project ZIP file that you reference in part 2 seems to be missing from your website.

Thank you for writing these tutorials. I haven't had a chance yet to thoroughly read them and put them to proper use with some hardware, but playing around with SD cards and the FAT filesystem is on my AVR MCU "to do" list. Your tutorials appear to be a great guide for when I do finally get around to trying that. Nice work!

Regards,
Bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

meteor wrote:
jokkebk wrote:
The part 2 has been published, [...]
FYI, the updated project ZIP file that you reference in part 2 seems to be missing from your website.

Whoops, fixed that. And thanks for the kind words!

I'll look in to the hardware part this weekend (I already succeeded in communicating with SD card using bus pirate and setting up ATtiny as a SPI slave so I'm halfway there), so the part 3 might even get finished this Sunday, or at least in the beginning of next week.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Part 3 is now available. I also suggested readers to post questions to this forum instead of just to my blog, that way there's more audience for both the questions and answers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

just about all what I need..

thanks dude
very appreciate that...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Awesome tutorial!

Quote:
I might also do additional parts covering FAT32, SDHC or file writing, depending on reader interest
highly interested!
Currently I am wrapping it into a C++ singleton, but I still have to write the writing routines...

EDIT: I totally forgot to say that I was able to write too a long time ago with the help of this and the Bus Pirate tutorial.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is a great tutorial ! I agree, most of the resources available for interfacing SD cards are just instructions to get the FatFS libraries working.
Thanks a lot for this!

(I also love the way the tut is presented)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is now a fourth part at

http://codeandlife.com/2012/04/27/simple-fat-and-sd-tutorial-part-4/

however it is still sector based.

Does anyone know of something a litle higher level?

Specifically, I want to

CheckFileExists
OpenFile
fgets() // read text lines, each lind ending with \n
CloseFil

I also want to be able to write text files a line at a time ....

all of the standard fopen(), fgetc() fgets(), fprintf(), fclose() stuff

If you can help, please reply to my thread at http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=125733

menawhile, I will continue to google

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jamawg wrote:
There is now a fourth part at

http://codeandlife.com/2012/04/27/simple-fat-and-sd-tutorial-part-4/

however it is still sector based.

Does anyone know of something a litle higher level?

Specifically, I want to

CheckFileExists
OpenFile
fgets() // read text lines, each lind ending with \n
CloseFil

Short answer also here, in hopes that it will help other readers of the tutorial...

The reading part of your question should be trivial to implement. The fat16_open_file() returns error value if file is not found, so CheckFileExists is almost 1:1 covered (you can make a wrapper function if you don't mind calling the exact same function again after you've made sure that file exists). Of course it doesn't understand complete paths (say CheckFileExists("some_dir/subdir/somefile.txt"), but writing a recursive function to handle those is quite easy.

Separate fopen() and fclose() are not needed because your AVR has no concept of file handles unless you implement them yourself. Only reason to do that would be so that you can have multiple files open - in that case you're right and some refactoring of the code would be needed (make a file handle structure to store sector and offset of open file, and create reading function that uses the data in handle instead of global library variables).

fgets() is basically reading the file byte at a time, and stopping when you get to the end or encounter '\n':

char line[20];
int len = 0;

while(len<20 && fat16_state.file_left) {
  if(fat16_read_file(1)) {
    line[len++] = *fat16_buffer;
    if(*fat16_buffer == '\n')
      break;
  } else break; // EOF or some other error
}

if(len >= 20) // too long line
  deal_with_error();
else
  line[len] = NULL; // terminate string

Quite easy, right? Note that most AVRs don't have enough memory to read long lines of text at one go. Due to memory limitations, higher level file functions are usually not feasible.

I might write an additional tutorial on writing files this month. :)

Joonas

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you *very* mcuh for such a quick response - and for the code sample.

Glad that FileExists() is so simple.

I am new to such smallprocessors, though I can appreciate what you are saying. Personally, I use a UC3 which is 32-bit so things might be easier for me. But I guess that I won't find an existign library to make things look just liek a larger processor and standard C system calls(?)

Just to show you my mindset, I was wanting to process an INI file. I can see that I might have to rethink that ...

I look forward to the next part of your tutorial.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

But I guess that I won't find an existign library to make things look just liek a larger processor and standard C system calls(?)
Eh? Surely FatFs does exactly that?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

But I guess that I won't find an existing library to make things look just like a larger processor and standard C system calls(?)
Eh? Surely FatFs does exactly that?

Thanks very much for replying. Obviously, I am missing something.

Sorry to be such a n00b, but can you point me at the FatFs you speak of? With a URL?

All that I could find was http://asf.atmel.com/docs/latest/uc3a/html/group__group__avr32__services__fs__fat.html

Is that the one? Can I use it open a file? To get a line with fgets() or even fscanf() & similar for writing? It doesn't seem so.

I am sure that this is do-able, but can't find where. This is my first Atmel project - I guess we all have to start somewhere.

Thanks again for any help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I only said "fatfs" because I know for a fact that if you type nothing but that one word into Google it's the top hit.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I learnt a lot from the tutorial. I am quite new to embedded programming and trying to communicate with SD card using atmega32. I flashed the SPI.c provided on the http://codeandlife.com/2012/04/25/simple-fat-and-sd-tutorial-part-3/ but I am getting response CMD 40 FF FF FF FF FF FF FF FF
CMD 41 FF FF FF FF FF FF FF FF

I am not sure where I am going wrong. I have connected my circuit like https://dlnmh9ip6v2uc.cloudfront.net/images/tutorials/Sensor-Interfacing/ST-3.jpg

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I didn't get any response from SPI, anyone has idea ?

Here's the code for ATMEGA128
Yes Usart is working, I tested on the first time program running, I used ATMEGA128
main :


#define SD_CS_ASSERT PORTB &= ~0x01
#define SD_CS_DEASSERT PORTB |= 0x01
#define CS (1<<PB0)
#define MOSI (1<<PB2)
#define MISO (1<<PB3)
#define SCK (1<<PB1)
#define CS_DDR DDRB
#define CS_ENABLE() (PORTB &= ~CS)
#define CS_DISABLE() (PORTB |= CS)

void port_init(void)
{
CS_DDR |= CS; // SD card circuit select as output
DDRB |= MOSI + SCK; // MOSI and SCK as outputs
PORTB |= MISO; // pullup in MISO, might not be needed

}

void spi_init(void)
{
SPCR = _BV(SPE)|_BV(MSTR)|_BV(SPR1)|_BV(SPR0);
SPSR &= ~_BV(SPI2X);
}

unsigned char SPI_transmit(unsigned char data)
{
// Start transmission
SPDR = data;

// Wait for transmission complete
while(!(SPSR & (1<<SPIF)));

return SPDR;
}
int main (void)
{
unsigned char option;
port_init();
spi_init();
usart_init(BAUD_PRESCALE);

usart_pstr("USART READY!");
while(1)
{
option = usart_receive();
switch(option)
{
case '1':
usart_pstr("1 command");
SD_command(0x40, 0x00000000, 0x95, 8);
break;
case '2':
SD_command(0x41, 0x00000000, 0xFF, 8);
usart_pstr("2 command");
break;
case '3':
SD_command(0x50, 0x00000200, 0xFF, 8);
usart_pstr("3 command");
break;
}

}
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, Thanks For the Tutorial. 

 

I have a problem in understanding a few stuffs,

 

I have a 2 GB uSD Card and data is written into it in .txt extension and used HXD editor to read the SD Card as in the http://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/. Resulting in Partition Table type as 0x07.(What does it mean?) NTFS?? If Yes, what is the address of the partition table???? Please help me to understand....

 

Regards,

 

Ibrahim

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not all FAT devices have a partition table. The very first sector on a device may be a "Master Boot record" (MBR) in which case, yes, at offset 0x1BE there is a four entry table with details of each partition. You can usually make assumptions if it is a "simple" device that in this case the first entry is the only entry. What's more of the 16 bytes in the partition table the only 4 byte entry of interest is at offset +0x08 from the start of the table entry - that is a 32bit holding the LBA (sector number) of the first sector in the partition.

 

Alternatively if the layout is even simpler (or if you get to it using that value at 0x1BE+0x08) then the first sector will be a boot/BIOS Parameter Block sector. This is actually the opening meta-data of the FAT12/FAT16/FAT32 layout.

 

So I assume you are reading sector 0 and looking at the "partition type" byte at 0x1C2? You say it is 0x07. The list of values is here:

 

http://en.wikipedia.org/wiki/Partition_type#List_of_partition_IDs

 

The likelihood, looking at that, is that it is NTFS not FAT! However I suspect your sector 0 is a boot/BPB not an MBR.

 

For more on all this read these three things:

 

http://en.wikipedia.org/wiki/Master_boot_record

http://en.wikipedia.org/wiki/FAT_BPB

http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/fatgen103.doc

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Clawson ....

 

Thanks for ur reply ....

 

But i am still not clear :( ...

 

I  tried with 0X08 + 0X1be =  0X1c6 ... i am getting partion type as FF ..... then i tried with  only 0X08 its giving partition type as 02 ....( i dont think these are the partion types ) ....??

Am i doing right ???  Is there any simplest way to find the  partion type .....( I think once i am clear with partion type and start of boot sector then  my  work becomes easy .. .. kindly help )  :(

 

i have  one more doubt ...when  i am using  HXD to take my sd card image( drive F:)  and try to save my image i get an error not enough space on disk ....but when i open i the folder where i saved it shows an image with disk on it .. ...( is this an correct image or its not complete ) .....

 

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just get a copy of WinHex from Xways (the read only version is free) and actually copy the editor display of the sector you are looking at - I'll tell you whether it's an MBR, BPB and the details of what it is showing.

 

As for imaging the disk. If it's a 2GB card then do you have 2GB of space on whatever you are copying the image to? Also why bother imaging anyway? Winhex will show you a hex view (decode as MBR/BPB even) "in place".

 

If I ever do want a "file copy" of an SD card I usually only copy the first few megabytes (usually enough to include the metadata, FATs and root). I use "dd" in Linux which makes this very easy indeed. (I think there's a Windows version called "Windd")

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi ...clawson

 

I am trying to  copy the editor display of the sector but is allowed only for licensed version ..... I can provide u the following details which is present on the right side of Winhex window ....

 

File system : Fat16

Cluster No : 2

                 NEWFILE ( my file name as given )

Physical Sector no :641

Logical Sector no : 508

Bytes per Cluster : 32,768

Free Cluster :60,660

Total cluster :60 , 661

 

Bytes per sector :512

First data sector :508

Bytes per page :496

 

I gave clt+f to find my data : I found at an offset address : 0x3f800 : 52 65 66 72 65 73 68 65 ...........etc  (Thisis my data on file >>>>>>" Refreshed Again ....end")without quotes ...

 

can u help with this information ...???

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you edit a drive in WinHex (Tools-Open Disk...) then highlight a sector full of data so it looks like this:

 

Then on the Edit menu you can use this:

 

 

and it will copy a block of text such as this to the clipboard...

 

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000010   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000020   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000030   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000040   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000050   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000060   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000070   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000080   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000090   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
000000A0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
000000B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
000000C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
000000D0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
000000E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
000000F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000100   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000110   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000120   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000130   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000140   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000150   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000160   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000170   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000180   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
00000190   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
000001A0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
000001B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 82                  �
000001C0   03 00 0C FE FF FF 00 20  00 00 00 AC DA 01 00 00      ���     ��   
000001D0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
000001E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
000001F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 AA                 U�

Can you do that for the sector you think is the partition tabvle (which should be 00000000 - that is sector 0)?

 

BTW your "52 65 66 72 65 73 68 65" sequence is the word "Refreshed" in ASCII character values.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Hello Clawson ,

 

Thanks for ur support ,

 

I have attached a file with noname. bin extension ...Kindly check ....will this work ....( I am not able to find where is clip board data once I pasted my sector .... :( so I went with this bin image )...

 

Kindly help ....and provide me steps if  this doesn't work .....to copy to clipboard....

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK so what that contains is this...

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   EB 3C 90 4D 53 44 4F 53  35 2E 30 00 02 40 02 00   ë< MSDOS5.0  @  
00000010   02 00 02 00 00 F8 ED 00  3F 00 FF 00 85 00 00 00        øí ? ÿ …   
00000020   7B 3F 3B 00 80 00 29 50  5B 16 16 4E 4F 20 4E 41   {?; € )P[  NO NA
00000030   4D 45 20 20 20 20 46 41  54 31 36 20 20 20 33 C9   ME    FAT16   3É
00000040   8E D1 BC F0 7B 8E D9 B8  00 20 8E C0 FC BD 00 7C   ŽÑ¼ð{ŽÙ¸  ŽÀü½ |
00000050   38 4E 24 7D 24 8B C1 99  E8 3C 01 72 1C 83 EB 3A   8N$}$‹Á™è< r ƒë:
00000060   66 A1 1C 7C 26 66 3B 07  26 8A 57 FC 75 06 80 CA   f¡ |&f; &ŠWüu €Ê
00000070   02 88 56 02 80 C3 10 73  EB 33 C9 8A 46 10 98 F7    ˆV €Ã së3ÉŠF ˜÷
00000080   66 16 03 46 1C 13 56 1E  03 46 0E 13 D1 8B 76 11   f  F  V  F  Ñ‹v 
00000090   60 89 46 FC 89 56 FE B8  20 00 F7 E6 8B 5E 0B 03   `‰Fü‰Vþ¸  ÷æ‹^  
000000A0   C3 48 F7 F3 01 46 FC 11  4E FE 61 BF 00 00 E8 E6   ÃH÷ó Fü Nþa¿  èæ
000000B0   00 72 39 26 38 2D 74 17  60 B1 0B BE A1 7D F3 A6    r9&8-t `± ¾¡}ó¦
000000C0   61 74 32 4E 74 09 83 C7  20 3B FB 72 E6 EB DC A0   at2Nt ƒÇ ;ûræëÜ 
000000D0   FB 7D B4 7D 8B F0 AC 98  40 74 0C 48 74 13 B4 0E   û}´}‹ð¬˜@t Ht ´ 
000000E0   BB 07 00 CD 10 EB EF A0  FD 7D EB E6 A0 FC 7D EB   »  Í ëï ý}ëæ ü}ë
000000F0   E1 CD 16 CD 19 26 8B 55  1A 52 B0 01 BB 00 00 E8   áÍ Í &‹U R° »  è
00000100   3B 00 72 E8 5B 8A 56 24  BE 0B 7C 8B FC C7 46 F0   ; rè[ŠV$¾ |‹üÇFð
00000110   3D 7D C7 46 F4 29 7D 8C  D9 89 4E F2 89 4E F6 C6   =}ÇFô)}ŒÙ‰Nò‰NöÆ
00000120   06 96 7D CB EA 03 00 00  20 0F B6 C8 66 8B 46 F8    –}Ëê     ¶Èf‹Fø
00000130   66 03 46 1C 66 8B D0 66  C1 EA 10 EB 5E 0F B6 C8   f F f‹ÐfÁê ë^ ¶È
00000140   4A 4A 8A 46 0D 32 E4 F7  E2 03 46 FC 13 56 FE EB   JJŠF 2ä÷â Fü Vþë
00000150   4A 52 50 06 53 6A 01 6A  10 91 8B 46 18 96 92 33   JRP Sj j ‘‹F –’3
00000160   D2 F7 F6 91 F7 F6 42 87  CA F7 76 1A 8A F2 8A E8   Ò÷ö‘÷öB‡Ê÷v ŠòŠè
00000170   C0 CC 02 0A CC B8 01 02  80 7E 02 0E 75 04 B4 42   ÀÌ  ̸  €~  u ´B
00000180   8B F4 8A 56 24 CD 13 61  61 72 0B 40 75 01 42 03   ‹ôŠV$Í aar @u B 
00000190   5E 0B 49 75 06 F8 C3 41  BB 00 00 60 66 6A 00 EB   ^ Iu øÃA»  `fj ë
000001A0   B0 4E 54 4C 44 52 20 20  20 20 20 20 0D 0A 52 65   °NTLDR        Re
000001B0   6D 6F 76 65 20 64 69 73  6B 73 20 6F 72 20 6F 74   move disks or ot
000001C0   68 65 72 20 6D 65 64 69  61 2E FF 0D 0A 44 69 73   her media.ÿ  Dis
000001D0   6B 20 65 72 72 6F 72 FF  0D 0A 50 72 65 73 73 20   k errorÿ  Press 
000001E0   61 6E 79 20 6B 65 79 20  74 6F 20 72 65 73 74 61   any key to resta
000001F0   72 74 0D 0A 00 00 00 00  00 00 00 AC CB D8 55 AA   rt         ¬ËØUª

That is not an MBR sector it is a boot/BPB sector. In Winhex if you ensure the cursor is at 0x0000 at the start of that then go to the View menu and select "Template Manager..." you will see there are Templates for "Boot sector FAT" and Boot sector FAT32". It may not be obvious which of these it is so try each in turn (make sure it's highlighted then [Apply!]). Ignore any warning about "template not applicable" and you should see something like this (turns out it's FAT (that is FAT12/FAT16) not FAT32)...

 

 

That tells you pretty much all you need to know about the BPB.

 

The entry at 0xE tells you there are 2 reserved sectors. One of those is this boot/BPB sector so there will then be one more reserved sector and then the FATs will start. The entry at 0x10 tells you there are two FATs and the entry at 016 tells you that each is 237 sectors. So the data area will start at sector 476. In FAT16 this will be the location of the root directory and as the data says there are 512 root entries, each is 32 bytes so it spans 32 sectors.

 

BTW using the template manager you can get that same sector decoded as if it were an MBR containing a partition table in which case you see this...

 

 

But that's all non-sensical - it clearly is not an MBR.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Clawson ....

 

Thanks for  u time and support ..............!!!

 

Couple of question's .........

 

1>  What does it mean when it says . ROOT ENTRIES = 512 ...??? Does it mean we  can create 512 Directories? (Folders as we say in Windows ).???

 

2> To Read Root Directory .. According to tutorial we have :

fseek(in, (bs.reserved_sectors-1 + bs.fat_size_sectors * bs.number_of_fats) *
      bs.sector_size, SEEK_CUR);

when i use this  = (2 -1 + 237 * 2 )*512 = 3b800 >>>>>>>>>>>>>should i mutiply with 512 ( bs.sector_size ) .........???????i am not getting my file name it just shows [.] >>>>>>no name

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

FAT file systems have a fixed number of entries in the Root directory.  e.g. /

All subdirectories can contain an unlimited number of files (or other directories)

 

I am sure that Wikipedia will explain this.

 

In practice,   it is convenient to to have say 32 entries in the Root.   e.g. 32 x 32bytes per entry = 1024 bytes (two 512byte sectors or one 2-sector cluster)

Even big Unix file systems don't have many directories in the Root.

 

But the sub-directories may contain 1000s of files and further sub-directories.

 

Of course,   the typical Windoze user can't cope with the idea of directories.    So puts every file in the C:/ root directory.

So you find that 'bigger' FAT file systems have large-ish fixed size root directories.

 

I am sure that these details are contained in the 'Boot/MPB' sector.    

You can create FAT file systems with massive Root directories if you really want.

 

Bear in mind that 'custom' FAT filesystems may be perfectly legal,    but software often makes assumptions that you are using the 'standard' MSDOS selection.     It is far safer to stick with convention.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

FAT file systems have a fixed number of entries in the Root directory.  e.g. /

Not quite. FAT12 and FAT16 were limited to one cluster of root entries. But in FAT32 they made "/" an entry in the FAT that can be chained just like any other file or directory so "/" can grow as large as you like EXCEPT that there is a further limit you hit because FAt uses a uint16 somewhere "inside" and that is that you cannot have more than 65536 files/directories in a directory (actually because of "." and ".." I think it might be 65534 in fact).

 

When working on embedded systems I tend to just put everything in "/" but I also tend to use FAT32 formatted cards. It just makes the code simpler to stick to "/" (and 8.3 names) and often it means you can "turn off" some of the FAT support code as you don't need "chdir" and the like.

 

As to point 2> above - don't do the *512 is you are working sectors and not bytes. So find the root at sector "(2 -1 + 237 * 2 )" except that personally I never know order of precedence so I would probably use "(2 -1 + (237 * 2) )" to be sure (2 lots of 237 sectors for each of the two FATs).

 

Also another way to find root is just to create a file in it like FOOBAR12.345 then just search the sectors for "FOOBAR12345". When you find it you have found the root. Just use this to verify the arithmetic (from BPB values) you would otherwise be using to get there.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Thanks for previous mail ...

 

Can u guide me where i am going wrong in my code .???

 HI ...

 

CAN U PLEASE CHECK THE CODE AND TELL WHY I AM NOT ABLE TO READ MY FILE NAME FROM THE ROOT DIRECTORY ??????( ARE MY  CALCULATIONS CORRECT TO JUMP TO ROOT DIRECTORY ) .....???????????????????( HIGHLIGHTED IN BLUE COLOR )

 

 

include <stdio.h>
#include <stdlib.h>

typedef struct {
    unsigned char first_byte;
    unsigned char start_chs[3];
    unsigned char partition_type;
    unsigned char end_chs[3];
    unsigned long start_sector;
    unsigned long length_sectors;
} __attribute((packed)) PartitionTable;

 struct Fat16Entry {
    unsigned char filename[8];
    unsigned char ext[3];
    unsigned char attributes;
    unsigned char reserved[10];
    unsigned short modify_time;
    unsigned short modify_date;
    unsigned short starting_cluster;
    unsigned long file_size;
};

typedef struct {
    unsigned char jmp[3];
    char oem[8];
    unsigned short sector_size;
    unsigned char sectors_per_cluster;
    unsigned short reserved_sectors;
    unsigned char number_of_fats;
    unsigned short root_dir_entries;
    unsigned short total_sectors_short; // if zero, later field is used
    unsigned char media_descriptor;
    unsigned short fat_size_sectors;
    unsigned short sectors_per_track;
    unsigned short number_of_heads;
    unsigned long hidden_sectors;
    unsigned long total_sectors_long;

    unsigned char drive_number;
    unsigned char current_head;
    unsigned char boot_signature;
    unsigned long volume_id;
    char volume_label[11];
    char fs_type[8];
    char boot_code[448];
    unsigned short boot_sector_signature;
} __attribute((packed)) Fat16BootSector;

int main() {
    FILE * in = fopen("latest.img", "rb");
    int i;
    PartitionTable pt[4];
    Fat16BootSector bs;
    struct    Fat16Entry entry ;

    fseek(in, 0x00, SEEK_SET); // go to partition table start

    fread(&bs, sizeof(Fat16BootSector), 1, in);

    printf("  Jump code: %02X:%02X:%02X\n", bs.jmp[0], bs.jmp[1], bs.jmp[2]);
    printf("  OEM code: [%.8s]\n", bs.oem);
    printf("  sector_size: %d\n", bs.sector_size);
    printf("  sectors_per_cluster: %d\n", bs.sectors_per_cluster);
    printf("  reserved_sectors: %d\n", bs.reserved_sectors);
    printf("  number_of_fats: %d\n", bs.number_of_fats);
    printf("  root_dir_entries: %d\n", bs.root_dir_entries);
    printf("  total_sectors_short: %d\n", bs.total_sectors_short);
    printf("  media_descriptor: 0x%02X\n", bs.media_descriptor);
    printf("  fat_size_sectors: %d\n", bs.fat_size_sectors);
    printf("  sectors_per_track: %d\n", bs.sectors_per_track);
    printf("  number_of_heads: %d\n", bs.number_of_heads);
    printf("  hidden_sectors: %d\n", bs.hidden_sectors);
    printf("  total_sectors_long: %d\n", bs.total_sectors_long);
    printf("  drive_number: 0x%02X\n", bs.drive_number);
    printf("  current_head: 0x%02X\n", bs.current_head);
    printf("  boot_signature: 0x%02X\n", bs.boot_signature);
    printf("  volume_id: 0x%08X\n", bs.volume_id);
    printf("  Volume label: [%.11s]\n", bs.volume_label);
    printf("  Filesystem type: [%.8s]\n", bs.fs_type);
    printf("  Boot sector signature: 0x%04X\n", bs.boot_sector_signature);

   /* jumping to Root Directory */

    fseek(in, (bs.reserved_sectors-1 + bs.fat_size_sectors * bs.number_of_fats) *bs.sector_size, SEEK_CUR);

     printf("File: [%.8s.%.3s]\n", entry.filename, entry.ext);
    fclose(in);
    return 0;
}
         

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi clawson ...( is it DPB ...Disk parameter block ) wht u r saying as bpb ????

 

 

I recalculated by changing my formuale . ( no. of reseverd sectors are = 6 -1 ) +( 237 *2 ) =1df ... i made the changes in my code its not reading properly ... :(

 

.but when i check using winhex as there is an option of root directory frame is shows at an offset of )x3c000  ....FILE NAME : KHAN36.TXT...

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I made some changes to your code but I'm hampered by the fact that I don't currently have a FAT16 card to hand. However this code at least found the BPB and printed the correct details on a FAT 32 card that even had an MBR so it had to go indirect via the parition table to find the BPB

 

(and yes I use "BPB" to mean BIOS PARAMETER BLOCK. Others might call this DISK Parameter Block instead (I wouldn't necessarily agree!))

 

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	uint8_t padding[446];
	struct {
	  uint8_t status;
	  uint8_t chs_first[3];
	  uint8_t part_type;
	  uint8_t chs_abs[3];
	  uint32_t first_lba;
	  uint32_t sec_in_part;
	} parts[4];
	uint16_t sig;
} __attribute__((packed)) MBR;

typedef struct {
    uint8_t first_byte;
    uint8_t start_chs[3];
    uint8_t partition_type;
    uint8_t end_chs[3];
    uint32_t start_sector;
    uint32_t length_sectors;
} __attribute((packed)) PartitionTable;

 struct Fat16Entry {
    uint8_t filename[8];
    uint8_t ext[3];
    uint8_t attributes;
    uint8_t reserved[10];
    uint16_t modify_time;
    uint16_t modify_date;
    uint16_t starting_cluster;
    uint32_t file_size;
};

typedef struct {
    uint8_t jmp[3];
    char    oem[8];
    uint16_t sector_size;
    uint8_t sectors_per_cluster;
    uint16_t reserved_sectors;
    uint8_t number_of_fats;
    uint16_t root_dir_entries;
    uint16_t total_sectors_short; // if zero, later field is used
    uint8_t media_descriptor;
    uint16_t fat_size_sectors;
    uint16_t sectors_per_track;
    uint16_t number_of_heads;
    uint32_t hidden_sectors;
    uint32_t total_sectors_long;

    uint8_t drive_number;
    uint8_t current_head;
    uint8_t boot_signature;
    uint32_t volume_id;
    char volume_label[11];
    char fs_type[8];
    uint8_t boot_code[448];
    uint16_t boot_sector_signature;
} __attribute((packed)) Fat16BootSector;

int main(int argc, char * argv[]) {
    FILE * in = fopen(argv[1], "rb");
    if (!in) {
      printf("%s could not be opened\n", argv[1]);
      exit(1);
    }
    int i;
    uint8_t sector[512];
    PartitionTable pt[4];
    Fat16BootSector * bs;
    MBR * mbr;
    struct    Fat16Entry entry ;

    fseek(in, 0x00, SEEK_SET); // go to partition table start

    fread(sector, 1, 512, in);
    bs = sector;
    if ((bs->fs_type[0] == 'F') && (bs->fs_type[1] == 'A') && (bs->fs_type[2] == 'T')) {
      // do nothing as we already found a BPB
    }
    else {
	mbr = sector;
	
    	fseek(in, mbr->parts[0].first_lba * 512, SEEK_SET); // go to partition table start
        fread(sector, 1, 512, in);
	bs = sector;
    }

    printf("  Jump code: %02X:%02X:%02X\n", bs->jmp[0], bs->jmp[1], bs->jmp[2]);
    printf("  OEM code: [%.8s]\n", bs->oem);
    printf("  sector_size: %d\n", bs->sector_size);
    printf("  sectors_per_cluster: %d\n", bs->sectors_per_cluster);
    printf("  reserved_sectors: %d\n", bs->reserved_sectors);
    printf("  number_of_fats: %d\n", bs->number_of_fats);
    printf("  root_dir_entries: %d\n", bs->root_dir_entries);
    printf("  total_sectors_short: %d\n", bs->total_sectors_short);
    printf("  media_descriptor: 0x%02X\n", bs->media_descriptor);
    printf("  fat_size_sectors: %d\n", bs->fat_size_sectors);
    printf("  sectors_per_track: %d\n", bs->sectors_per_track);
    printf("  number_of_heads: %d\n", bs->number_of_heads);
    printf("  hidden_sectors: %d\n", bs->hidden_sectors);
    printf("  total_sectors_long: %u\n", bs->total_sectors_long);
    printf("  drive_number: 0x%02X\n", bs->drive_number);
    printf("  current_head: 0x%02X\n", bs->current_head);
    printf("  boot_signature: 0x%02X\n", bs->boot_signature);
    printf("  volume_id: 0x%08X\n", bs->volume_id);
    printf("  Volume label: [%.11s]\n", bs->volume_label);
    printf("  Filesystem type: [%.8s]\n", bs->fs_type);
    printf("  Boot sector signature: 0x%04X\n", bs->boot_sector_signature);

   /* jumping to Root Directory */

    fseek(in, (bs->reserved_sectors-1 + (bs->fat_size_sectors * bs->number_of_fats)) *bs->sector_size, SEEK_CUR);

    fread(&entry, 1, sizeof(entry), in);
    printf("File: [%.8s.%.3s]\n", entry.filename, entry.ext);
    fclose(in);
    return 0;
}

That output:

$ ./fat sdimg.bin
  Jump code: EB:00:90
  OEM code: [        ]
  sector_size: 512
  sectors_per_cluster: 64
  reserved_sectors: 598
  number_of_fats: 2
  root_dir_entries: 0
  total_sectors_short: 0
  media_descriptor: 0xF8
  fat_size_sectors: 0
  sectors_per_track: 63
  number_of_heads: 255
  hidden_sectors: 8192
  total_sectors_long: 31108096
  drive_number: 0xD5
  current_head: 0x0E
  boot_signature: 0x00
  volume_id: 0x00000000
  Volume label: []
  Filesystem type: []
  Boot sector signature: 0xAA55

But as I say this was looking at a FAT32 not a FAT16 card.

 

Winhex had this to say for the same thing...

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Clawson ...

 

THANKS FOR UR CODE ...

My Mistake ...........i missed fread and was directly trying to print ......anyways i read my FILE NAME and other structure parameters ... but i have another probelm ....now

after reading the ROOT DIrectory parameters i used ftell = 0X3c020 ....now if i calculate 64 sectors per cluster and sector size is 512  = 64 *512 = 0x8000 +3c020 = 44020.......this is where my data sholuld sit ????   right ????

 

but when i use winhex it shows my data first character at location = 0X40000 .....(  my calculation are not matching ).....below i have pasted my code output  ..........can u point out my mistake .

 

solipsys@solipsys-OptiPlex-745:~/kh/sd$ ./a.out
  Jump code: EB:3C:90
  OEM code: [MSDOS5.0]
  sector_size: 512
  sectors_per_cluster: 64
  reserved_sectors: 6
  number_of_fats: 2
  root_dir_entries: 512
  total_sectors_short: 0
  media_descriptor: 0xF8
  fat_size_sectors: 237
  sectors_per_track: 63
  number_of_heads: 255
  hidden_sectors: 133
  total_sectors_long: 3882875
  drive_number: 0x80
  current_head: 0x00
  boot_signature: 0x29
  volume_id: 0xB0511703
  Volume label: [NO NAME    ]
  Filesystem type: [FAT16   ]
  Boot sector signature: 0xAA55
0x3c000
File: [KHAN36  .TXT]
  Modified: 2014-09-29 20:53.03    Start: [0002]    Size: 25

ftell : 0x3c020

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Without seeing your code I cannot guess. The fact is that Fatgen103.doc is VERY clear about this. I assume you have read it. It shows exactly how calculate the position of a cluster. Just do what fatgen103 says and it will work.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi clawson ,

I read faten103 ....thanks for link ...

 

But my calculations  are no matching WINHEX software ....

 

From fatgen103  .... First we calculate :  the count of sectors occupied by the root directory =>>>>>  RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec – 1)) / BPB_BytsPerSec;

                                                                                                                                                                                                                = ( (512*32)                          +  (512-1 )   )/ 512

                                                                                                                                                                                                                 =  32.9   ~=32

 

 

Next we calculate                                                                  First data sector                            =>>>>>     FirstDataSector = BPB_ResvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors;

                                                                                                                                                                                                          =                        6           + ( 2                 * 237   )         +  32

                                                                                                                                                                                                          =  512

 

now what value shold be added with 512 so that i can reach my first block of data ??????

 

i have attached my code and image ....pls help its getting late for my project submissio .:(

                      

 

 

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My data image ... ( my data on sd card is ............>>>>>Refreshed Again ....end)

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is the point of attaching the contents of the file? What's interesting on your card is the metadata. That is MBR (if one), BPB, FATs, root dir and some of the data area.

 

Also search here for "fatdat" - it's a utility I wrote and posted previously that can read and analyze FAT images.

 

EDIT: the search on this site is terrible but I found some of my "fatdat" stuff here:

 

http://www.avrfreaks.net/forum/sd-card-fat32-few-questions

 

As you can see I use the same names as in fatgen103 and the same formulae - so it works.

 

EDIT2: Ah ha - found it - it's in this thread - attached as "myprog.c":

 

http://www.avrfreaks.net/forum/sd-card-interface-atmega16-schematic