	Using SD and MMC cards with PICBASIC PRO
	Copyright 2008 microEngineering Labs, Inc.

The PICBASIC PRO programs included in this zip allow reading and writing to
SD (Secure Digital) and MMC (Multi Media) cards using the FAT16 file system.
The code was written based on information from the Microchip USB framework
and Jan Axelson's book, USB Mass Storage.  Details on SD and MMC cards and
the FAT file system can be found there.

The included code does not actually use the security features of SD cards.
It reads and writes to the cards using SPI in MMC mode.  While the code can
certainly be modified to take advantage of the secure digital features, a
license to the SD consortium would need to be paid.  Information about this
can be found online.

Speaking of licenses, the FAT file system is owned by Microsoft and must be
licensed from them if you intend to sell a product that uses it.  The license
fee looks to be fairly nominal.  More information can be found on Microsoft's
web site.

The Cards

SD and MMC cards run on 3.3 volts or less.  Level conversion hardware, as
well as the lower voltage power supply, needs to be included on your board to
interface between the PIC and the card, if the PIC is running at more than
3.3 volts itself.  Microchip offers a PICtail card for SD and MMC cards that
includes this level conversion.  This is what was used for development of the
code.

The SD/MMC code supports reading and writing sectors on most SD and MMC
cards.  As very few cards, out of the many that are on the market, have been
tested, you will want to try the card you have in mind to make sure it will
work with the provided code.

The included file access code supports FAT16 as used by Windows.  There are
actually several different file systems used by Windows, including FAT12,
FAT32 and NTFS.  FAT16 (also know simply as FAT under Windows) seems to be
the most common file system used on moderately sized SD/MMC cards.  The sizes
of the cards that can be used with FAT16 range from 32MB to 2GB.

If cards larger than 2GB are required, the code would need to be modified to
support FAT32.  Jan Axelson's book mentioned above provides details about
FAT32.

Directories

Subdirectories are not supported with the current FAT16 code.  All files
must reside in the root directory.  About 512 files are allowed in the
root directory.

The Compiler

The included PICBASIC PRO code requires PBP version 2.50 or later.  It uses
the PIC18 long libraries that are part of that version of the compiler.  This
also means the files only work on PIC18 parts.  Be sure to specify the long
version of the compiler, PBPL, when compiling programs using these files.

The Files

The file in this directory called SDFS.BAS contains the subroutines to read
and write to SD/MMC cards and to access files using the FAT16 file system.
The SDTEST.BAS file is an example of what a program might look like that uses
these subroutines.

As shown in the example file SDTEST.BAS, your program first needs to create
aliases to the PIC I/O pins used by the SD/MMC card.  The PIC18F4550 is used
by this example program so the pin aliases reflect this.

After this, the SDFS.BAS program is included into the main program. This
gives the main program access to the subroutines and variables used to read
and write to the cards.

        Include "SDFS.BAS"

A more compact version that does reads only is called SDFSR.BAS.

        Include "SDFSR.BAS"

After the Include is a line that tells the routines whether to use software
or the hardware SSP module on the PIC for the SPI communications to the card.
To use the hardware SSP module, the line should read:

	SDC_UseHardSPI = TRUE

To use software SPI, the line should read:

	SDC_UseHardSPI = FALSE

After these lines of code, your program can do its own initialization and
whatever else it needs to do.  Once the program is ready to start talking
to the SD/MMC card, it would use Gosubs to the subroutines listed below.

The Code

Finally, on to the actual code.  Talking to SD/MMC cards using the included
subroutines is quite simple.  The subroutines are accessed using Gosubs and
generally return an error byte, FAT_error, along with any other requested
data.  If the error value is 0, the operation completed correctly.  If it is
non-zero, an error occurred.  The error table is included in the file
SDFS.BAS.  It is the constant table near the beginning of the file with names
that all start with CE_.

Most of the subroutines in the code are used internally, i.e. used by other
subroutines.  The only ones your program should call are:

        FSInit
        FSExit
        FSfopen
        FSfclose
        FSfread
        FSfwrite
        FSremove
        FSrename
        FSfeof
        FSrewind
        FSfseek
        FSftell
        FINDfirst
        FINDnext

FSInit

The subroutine FSInit initializes the I/O pins that are used and reads the
initial data on the card, including the Master Boot Record.  It returns
FAT_error as 0 if everything went OK.  It is the first subroutine that must
be called.  It should also be called again if the SD/MMC card has been
replaced.

        Gosub FSInit
	If (FAT_error != 0) Then Stop

FSExit

The subroutine FSExit is used when the program is done talking to an SD/MMC
card and the card is to be removed.  It actually doesn't do alot other than
setting a few flags and releasing the hardware SSP port, if it was being
used.  It does not return an error code.

        Gosub FSExit

FSfopen

FSfopen is the subroutine used to open an existing file or create a new one.
A file can be opened in read mode, write mode, or append mode.  The variable
FAT_mode is used to set the mode before fopen is called.  Once the file is
opened, the variable FAT_error is set to 0 if there were no problems.  The
card must not be write protected if you intend to write or append to a file.

The file to open is specified by the byte array variable FAT_FileName.  The
file name is in the short, 8.3 format.  Any characters that are not used must
be filled with a space.  The dot in the filename is not used.  While the
comparison to match an existing name is case insensitive, the name must be
upper case to work with Windows.

	FAT_FileName[0] = "T"
	FAT_FileName[1] = "E"
	FAT_FileName[2] = "S"
	FAT_FileName[3] = "T"
	FAT_FileName[4] = " "
	FAT_FileName[5] = " "
	FAT_FileName[6] = " "
	FAT_FileName[7] = " "
	FAT_FileName[8] = "T"
	FAT_FileName[9] = "X"
	FAT_FileName[10] = "T"

	FAT_mode = "r"		' Read mode
	FAT_mode = "w"		' Write mode
	FAT_mode = "a"		' Append mode
        Gosub FSfopen
	If (FAT_error != 0) Then Stop

FSfclose

FSfclose is used to close a previously opened file.  It does not require a
file name as only one file is allowed to be open at a time.  While it is not
technically necessary to close a file that has been opened for read, any
files that are opened for write or append must be closed to make sure the
directory information gets written.

        Gosub FSfclose
	If (FAT_error != 0) Then Stop

FSfread

The subroutine FSfread is used to read a file opened with FSfopen.  The
number of bytes to be read from the file is specified by the word variable
FAT_count. The number of bytes actually read is returned in the word
variable FAT_readCount.  The bytes are put into the byte array variable
FAT_dest.  The size of this array is the same as the sector size, 512,
though this is not necessary.  The constant FAT_BUFFER_SIZE may be changed,
if desired.  This buffer memory is shared with the buffer used by FSfwrite,
FAT_src, as they are usually not used at the same time with only one file
open.  This can be changed if desired, as well.

FAT_error returns 0 is the were no issues with the read.  It returns 61 to
indicate end of file.

	FAT_mode = "r"		' Read mode
        Gosub FSfopen
	If (FAT_error != 0) Then Stop
	FAT_count = 1		' Read 1 byte to buffer at a time
        Gosub FSfread
	While (FAT_error = 0)
		Serout2 PORTC.6, 84, [FAT_dest[0]]
		FAT_count = 1	' Read 1 byte to buffer at a time
                Gosub FSfread
	Wend

FSfwrite

The subroutine FSfwrite is used to write to a file opened with FSfopen.  The
number of bytes to write to the file is specified by the word variable
FAT_count.  The bytes are taken from the byte array variable FAT_src.  The
size of this array is the same as the sector size, 512, though this is not
necessary.  The constant FAT_BUFFER_SIZE may be changed, if desired.  This
buffer memory is shared with the buffer used by fread, FAT_dest, as they are
usually not used at the same time with only one file open.  This can be
changed if desired, as well.

FAT_error returns 0 is the were no issues with the write.

	FAT_mode = "w"		' Write mode
        Gosub FSfopen
	If (FAT_error != 0) Then Stop
	FAT_src[0] = "A"
	FAT_src[1] = "B"
	FAT_src[2] = "C"
	FAT_count = 3
        Gosub FSfwrite
	If (FAT_error != 0) Then Stop
        Gosub FSfclose
	If (FAT_error != 0) Then Stop

FSremove

To delete a file, use the subroutine FSremove.  The file to remove is
specified by the byte array variable FAT_FileName as used above in FSfopen.
FAT_error equal to 0 is returned if the deletion was successful.  The card
must not be write protected if you intend to delete a file.

	FAT_FileName[0] = "T"
	FAT_FileName[1] = "E"
	FAT_FileName[2] = "S"
	FAT_FileName[3] = "T"
	FAT_FileName[4] = " "
	FAT_FileName[5] = " "
	FAT_FileName[6] = " "
	FAT_FileName[7] = " "
	FAT_FileName[8] = "T"
	FAT_FileName[9] = "X"
	FAT_FileName[10] = "T"

        Gosub FSremove
	If (FAT_error != 0) Then Stop

FSrename

To rename an existing file, use the subroutine FSrename.  The file to rename
is specified by the byte array variable FAT_FileName as used above in FSfopen.
The new name is specified by the byte array variable FAT_FileName2.

FAT_error equal to 0 is returned if FSrename was successful.  The card must
not be write protected if you intend to rename a file.

	FAT_FileName[0] = "T"
	FAT_FileName[1] = "E"
	FAT_FileName[2] = "S"
	FAT_FileName[3] = "T"
	FAT_FileName[4] = "1"
	FAT_FileName[5] = " "
	FAT_FileName[6] = " "
	FAT_FileName[7] = " "
	FAT_FileName[8] = "T"
	FAT_FileName[9] = "X"
	FAT_FileName[10] = "T"

	FAT_FileName2[0] = "T"
	FAT_FileName2[1] = "E"
	FAT_FileName2[2] = "S"
	FAT_FileName2[3] = "T"
	FAT_FileName2[4] = "2"
	FAT_FileName2[5] = " "
	FAT_FileName2[6] = " "
	FAT_FileName2[7] = " "
	FAT_FileName2[8] = "T"
	FAT_FileName2[9] = "X"
	FAT_FileName2[10] = "T"

        Gosub FSrename
	If (FAT_error != 0) Then Stop

FSfeof

FSfeof returns byte variable FAT_status of TRUE if the file is at its
end.  Otherwise FALSE is returned

        Gosub FSfeof
	If (FAT_status = TRUE) Then
		Serout2 PORTC.6, 84, ["At end of file."]
	Endif

FSfseek

FSfseek allows the pointer to the current position in the file to be set.
The long variable FAT_offset is used to set the position to seek to.

	FAT_offset = 10
        Gosub FSfseek
	If (FAT_error != 0) Then Stop

FSftell

FSftell returns the current position in the file.  The long variable
FAT_seek contains the current position.

        Gosub FSftell
	Serout2 PORTC.6, 84, [Dec FAT_seek]

FINDfirst, FINDnext

FINDfirst and FINDnext are used to read the directory of an SD/MMC card.
The first or next filename found is returned in the byte array variable
FAT_FileName.  A FAT_error of 0 indicates a file name was found.

	Gosub FINDfirst		' Find first file on card
	While (FAT_error = 0)
		Serout2 PORTC.6, 84, [Str FAT_FileName\11, $d, $a]
		Gosub FINDnext	' Find next file on card
	Wend
