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