This project provides an easy to understand file system code for embedded applications with limited resources. The system was developed as an add-on for the PHOENIX Microcontroller Development Kit (PMDK) developed by IUAC for use as a data logging system, but it can be ported to just about any other embedded system.
Both SD and MMC cards support SPI mode operation, which makes their integration into embedded systems extremely easy. The overall procedure for implementing a file system is listed below. Click the subheadings for details.
1. Initialise the SPI module of the microcontroller
2. Initialise the SD/MMC card by sending the appropriate sequence of initialisation commands.
3. Read and verify the file system present on the card.
5. Append to the file as and when fresh data is available.
Initialise the SPI module of the microcontroller:
The SPI module of the microcontroller is started up with a clock rate of fosc/64 = 8MHz/64 = 125 kHz (SPI2X = 0) in Master mode (MSTR=1). As per the SD/MMC requirements, the MSB of each byte is transmitter first (DORD=0), clock polarity is natural (leading edge rising and trailing edge falling), and clock phase is also natural (Sample on leading edge and Setup on trailing edge). (CPOL = 0 and CPHA = 0).
The SD/MMC card initialisation process is very specific and is slightly different for the different types of cards available. The scheme given here has been tested on micro SD cards of 256MB and 1GB, but should work for all SD and MMC cards.
The SD/MMC card is powered up along with the controller. The microcontroller need not have the ability to turn on or off the power to the card, though this may desirable from a power savings perspective, and provides a method of causing a hard reset. To initialise the card, the host is required to keep the card deselected (\SS line high) and generate 80 clock pulses on the SCK line. This is done by sending byte 0xFF ten times via SPI. Then another 80 clock pulses are generated with the card selected (\SS pulled low). Then the host sends the software reset command.
Read and verify the file system present on the card.
Once the card initialisation has been completed successfully, the host can read, or write to, any valid sector on the card. The host examines the first sector (address 0x 00 00 00 00). This must contain the 16-bit ‘magic number’ 0x55AA in the last two byte locations. If not, the card file system is damaged in some way and the host should reject it outright.
The first sector either contains the partition table or contains the FAT volume information. The presence of bytes 0xeb, 0x58, and 0x90 in the first 3 positions of the sector indicates that this is the FAT volume information. If not, the partition table should be present at byte locations starting 0x1BE. The partition table contains 4 entries – starting at 0x1BE, 0x1CE, 0x1DE and 0x1EE; each 16 bytes long. The detailed meaning of the partition table entries is explained here and on PJRC.
Typical partition table: (From 1GB micro SD card
–manufactured by DNA Memory Products Ltd.)
Address Content
(Hexadecimal)
0x1BE 00 03 3f 06 1b db
d6 fb 00 00 00 05 3b 1e aa 00
0x1CE 00 00 00 00
00 00 00
00 00 00
00 00 00
00 00 00
0x1DE 00 00 00 00
00 00 00
00 00 00
00 00 00
00 00 00
0x1EE 00 00 00 00
00 00 00
00 00 00
00 00 00
00 00 00
0x1FE 55 aa
Initially only the 5th byte which is the partition type indication, (i.e. at offset 4) needs to be checked for a value of 0xB or 0xC which is indicative of the presence of a FAT32 partition. If none of the entries is FAT32, a failure is reported. Else the first valid FAT32 partition entry is analysed, which from the above example is at offset 0x1BE. It is important not to assume that the first entry is always the valid FAT32 entry. The 32 bit address of the first sector of the FAT32 partition is then read off from bytes 7, 8, 9, 10 from the partition entry in little-endian order (0x 00 00 00 fb in above entry). This address points to the FAT volume information sector.
A typical FAT Volume information sector looks like this:
Address Content
(Hexadecimal)
0x000 EB 59 90 4D 53 44
4F 53 35 2E 30 00 02 01 2E 07 . . .
M S D O S 5 . 0 . . . . .
0x010 02 00 00 00 00
F8 00 00 3F 00 FF 00 00 00 00 00
. . . . . . . . ? . . . . . . .
0x020 00 B4 1E 00 69 3C 00 00 00 00 00 00 02 00 00 00 . . . . E . . . . . . . . . . .
0x030 01 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 . . . . . . . . . . . . . . . .
0x040 80 00 29 22 56 E1 E6 00 4E 4F 20 4E 41 4D 45 20 . . ] “ . . . . N O N A M E
0x050 20 20 46 41 54 33 32 20 20 20 20 00 00
00 00 00
F A T 3
2 . . . . . .
... ...
0x1FE 55 aa . .
In this sector, the fields are read to make sure the partition is FAT32. Here is more info about FAT in the Microsoft knowledge base.
Before a file is created, the root directory is checked. The root directory is usually found in cluster 2. The first directory entry in the root directory is called the volume label entry. Then follow the entries for the files and directories in the root directory. If the root directory is empty, a new file entry is created in the root directory. Then both the FATs are modifies to allocate 1 sector to this new file (which is filled with 1 sector worth initial data – junk data if nothing else).
Append to the file as and when fresh data is available.
When fresh data is available to be written to the card, the data is first written to the next free cluster on the card. Then the FATs are updated, and then the root directory entry is updated to reflect the increased file size. Since the card is verified to be empty initially, there is no need for the controller to go about hunting for free sectors on the card. This is a resource intensive process, since FAT is a linked list based arrangement.
This is a brief description of the FAT access procedure used in this project. A more detailed explanation with details of the workings of FAT32 will be added soon.