As you know, this forum there are many scattered versions, on this subject.
I went back again in need of it and basically had to re-join all parts ...
For future reference of all, here I leave it all together and modified.
This library "mmc sd.c" and "fat.c" works perfectly with the "ex_fat.c"

Just create a file with the same name, with all the code there and apply in your project.

mmcsd.c

Code:

/////////////////////////////////////////////////////////////////////////
//// MMCSD.c ////
//// ////
//// This is a low-level driver for MMC and SD cards. ////
//// ////
//// --User Functions-- ////
//// ////
//// mmcsd_init(): Initializes the media. ////
//// ////
//// mmcsd_read_byte(a, p) ////
//// Reads a byte from the MMC/SD card at location a, saves to ////
//// pointer p. Returns 0 if OK, non-zero if error. ////
//// ////
//// mmcsd_read_data(a, n, p) ////
//// Reads n bytes of data from the MMC/SD card starting at address ////
//// a, saves result to pointer p. Returns 0 if OK, non-zero if ////
//// error. ////
//// ////
//// mmcsd_flush_buffer() ////
//// The two user write functions (mmcsd_write_byte() and ////
//// mmcsd_write_data()) maintain a buffer to speed up the writing ////
//// process. Whenever a read or write is performed, the write ////
//// buffer is loaded with the specified page and only the ////
//// contents of this buffer is changed. If any future writes ////
//// cross a page boundary then the buffer in RAM is written ////
//// to the MMC/SD and then the next page is loaded into the ////
//// buffer. mmcsd_flush_buffer() forces the contents in RAM ////
//// to the MMC/SD card. Returns 0 if OK, non-zero if errror. ////
//// ////
//// mmcsd_write_byte(a, d) ////
//// Writes data byte d to the MMC/SD address a. Intelligently ////
//// manages a write buffer, therefore you may need to call ////
//// mmcsd_flush_buffer() to flush the buffer. ////
//// ////
//// mmcsd_write_data(a, n, p) ////
//// Writes n bytes of data from pointer p to the MMC/SD card ////
//// starting at address a. This function intelligently manages ////
//// a write buffer, therefore if you may need to call ////
//// mmcsd_flush_buffer() to flush any buffered characters. ////
//// returns 0 if OK, non-zero if error. ////
//// ////
//// mmcsd_read_block(a, s, p) ////
//// Reads an entire page from the SD/MMC. Keep in mind that the ////
//// start of the read has to be aligned to a block ////
//// (Address % 512 = 0). Therefore s must be evenly divisible by ////
//// 512. At the application level it is much more effecient to ////
//// to use mmcsd_read_data() or mmcsd_read_byte(). Returns 0 ////
//// if successful, non-zero if error. ////
//// ////
//// mmcsd_write_block(a, s, p): ////
//// Writes an entire page to the SD/MMC. This will write an ////
//// entire page to the SD/MMC, so the address and size must be ////
//// evenly divisble by 512. At the application level it is much ////
//// more effecient to use mmcsd_write_data() or mmcsd_write_byte().////
//// Returns 0 if successful, non-zero if error. ////
//// ////
//// mmcsd_print_cid(): Displays all data in the Card Identification ////
//// Register. Note this only works on SD cards. ////
//// ////
//// mmcsd_print_csd(): Displays all data in the Card Specific Data ////
//// Register. Note this only works on SD cards. ////
//// ////
//// ////
//// --Non-User Functions-- ////
//// ////
//// mmcsd_go_idle_state(): Sends the GO_IDLE_STATE command to the ////
//// SD/MMC. ////
//// mmcsd_send_op_cond(): Sends the SEND_OP_COND command to the ////
//// SD. Note this command only works on SD. ////
//// mmcsd_send_if_cond(): Sends the SEND_IF_COND command to the ////
//// SD. Note this command only works on SD. ////
//// mmcsd_sd_status(): Sends the SD_STATUS command to the SD. Note ////
//// This command only works on SD cards. ////
//// mmcsd_send_status(): Sends the SEND_STATUS command to the ////
//// SD/MMC. ////
//// mmcsd_set_blocklen(): Sends the SET_BLOCKLEN command along with ////
//// the desired block length. ////
//// mmcsd_app_cmd(): Sends the APP_CMD command to the SD. This only ////
//// works on SD cards and is used just before any ////
//// SD-only command (e.g. send_op_cond()). ////
//// mmcsd_read_ocr(): Sends the READ_OCR command to the SD/MMC. ////
//// mmcsd_crc_on_off(): Sends the CRC_ON_OFF command to the SD/MMC ////
//// along with a bit to turn the CRC on/off. ////
//// mmcsd_send_cmd(): Sends a command and argument to the SD/MMC. ////
//// mmcsd_get_r1(): Waits for an R1 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_get_r2(): Waits for an R2 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_get_r3(): Waits for an R3 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_get_r7(): Waits for an R7 response from the SD/MMC and ////
//// then saves the response to a buffer. ////
//// mmcsd_wait_for_token(): Waits for a specified token from the ////
//// SD/MMC. ////
//// mmcsd_crc7(): Generates a CRC7 using a pointer to some data, ////
//// and how many bytes long the data is. ////
//// mmcsd_crc16(): Generates a CRC16 using a pointer to some data, ////
//// and how many bytes long the data is. ////
//// ////
/////////////////////////////////////////////////////////////////////////
//// (C) Copyright 2007 Custom Computer Services ////
//// This source code may only be used by licensed users of the CCS ////
//// C compiler. This source code may only be distributed to other ////
//// licensed users of the CCS C compiler. No other use, ////
//// reproduction or distribution is permitted without written ////
//// permission. Derivative programs created using this software ////
//// in object code form are not restricted in any way. ////
/////////////////////////////////////////////////////////////////////////

/// this would be a good time to set a higher clock speed, 20MHz
#if defined(MMCSD_SPI_HW)
#if (getenv("CLOCK") <= 80000000)
#define MMC_SPI_CLK_DIV SPI_CLK_DIV_4
#else
#if defined(SPI_CLK_DIV_8)
#define MMC_SPI_CLK_DIV SPI_CLK_DIV_8
#else
#define MMC_SPI_CLK_DIV SPI_CLK_DIV_16
#endif
#endif
#error/warning the next line will only work if using SPI1
setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_XMIT_L_TO_H | MMC_SPI_CLK_DIV);
#endif

MMCSD_err mmcsd_get_r1(void)
{
uint8_t
response = 0, // place to hold the response coming back from the SPI line
timeout = 0xFF; // maximum amount loops to wait for idle before getting impatient and leaving the function with an error code

// loop until timeout == 0
while(timeout)
{
// read what's on the SPI line
// the SD/MMC requires that you leave the line high when you're waiting for data from it
response = MMCSD_SPI_XFER(0xFF);
//response = MMCSD_SPI_XFER(0x00);//leave the line idle

// check to see if we got a response
if(response != 0xFF)
{
// fill in the response that we got and leave the function
return response;
}

// wait for a little bit longer
timeout--;
}

// for some reason, we didn't get a response back from the card
// return the proper error codes
return RESP_TIMEOUT;
}

Last edited by electr0dave on Mon Apr 13, 2015 7:24 am; edited 1 time in total

electr0dave

Joined: 10 Aug 2014Posts: 24

Posted: Fri Apr 10, 2015 3:13 pm

fat.c - part1

Quote:

/////////////////////////////////////////////////////////////////////////
//// FAT_PIC.C ////
//// ////
//// Driver/Library for a FAT filesystem with a PIC ////
//// ////
//// This Library was designed to resemble standard ANSI C I/O as ////
//// much as possible. There are, however, some caveats to this. ////
//// Please read the comments to make sure the inputs and outputs ////
//// to each function are understood before using anything in ////
//// this library. ////
//// ////
//// This library supports FAT16 and FAT32, but not both at the same ////
//// time (this is a compile option, see options below). It is ////
//// recommended to use FAT32, FAT32 also has been tested more. ////
//// ////
//// Any function with an argument taking in a file name must be in ////
//// the form of... ////
//// "/filename.fil" for a file in the root directory ////
//// "/Directory/filename.fil" for a file in a subdirectory of root ////
//// "/Directory/Subdirectory/filename.fil" and so on... ////
//// ////
//// Any function with an argument taking in a directory name must ////
//// be in the form of... ////
//// "/Dirname/" for a directory in the root directory ////
//// "/Dirname/Subdirname/" for a directory in a subdirectory of ////
//// root and so on... ////
//// ////
//// A compatable media library must be provided. This is ////
//// documented after the User Functions. ////
//// ////
//// -- User Functions -- ////
//// ////
//// fat_init() ////
//// Initializes the FAT library, also initializes the media. ////
//// ////
//// fatopen(char *name, char *mode, FILE *fstream) ////
//// Opens up a FILE stream to a specified file with the specified ////
//// permission mode: ////
//// Permissions: "r" = read ////
//// "w" = write ////
//// "a" = append ////
//// "rb" = read binarily ////
//// "w" will erase all of the data in the file upon ////
//// the opening of the file. ////
//// "a" will tack on all of the data to the end of the ////
//// file. ////
//// "r" will keep on reading until the stream ////
//// hits an '\0' ////
//// "rb" will keep on reading until the amount of ////
//// bytes read equals the size of the file. ////
//// ////
//// Unlike standard C fopen(), this does not malloc a FILE - ////
//// instead the caller will have to have allready allocated a ////
//// a FILE and pass a pointer to it. ////
//// ////
//// fatreopen(char *name, char *mode, FILE *fstream) ////
//// Closes a FILE stream, then reopens the stream with a new file ////
//// and new permissions. ////
//// ////
//// fatclose(FILE *fstream) ////
//// Closes a FILE stream. It is very important to call this ////
//// function when you're done reading or writing to a file. //// ////
//// ////
//// fatgetc(FILE *fstream) ////
//// Gets a character from a stream. An EOF will be returned at ////
//// different times depending on whether or not the stream is ////
//// reading binarily. If not reading binarily: EOF when the ////
//// stream reads a '\0'. If reading binarily: EOF when the amount ////
//// of bytes read equals the size of the file (end of file). ////
//// ////
//// fatputc(char c, FILE *fstream) ////
//// Puts a character into a stream (write to the file). ////
//// Writes are buffered, so the media may not be written to until ////
//// a fatclose(). ////
//// ////
//// char* fatgets(char* str, int num, FILE *fstream) ////
//// Gets characters from a stream until either a '\r', EOF, or ////
//// num - 1 is hit. ////
//// ////
//// fatputs(char* str, FILE *fstream) ////
//// Puts a string into a stream (write a string to the file). ////
//// ////
//// fatprintf(FILE *stream): Printfs the entire stream. ////
//// printf()'s the entire stream (printf()'s the contents of the
//// file).
//// ////
//// fatgetpos(FILE *fstream, fatpos_t *pos) ////
//// Gets the current position of the stream/file, saves to pos. ////
//// ////
//// fatsetpos(FILE *fstream, fatpos_t *pos) ////
//// Sets the current position of the stream/file. ////
//// ////
//// fatseek(FILE *fstream, int32 offset, int origin) ////
//// Sets the current position of the stream according to the ////
//// origin parameter: ////
//// SEEK_CUR: Set position relative to the ////
//// current stream position. ////
//// SEEK_END: Set position relative to the ////
//// end of the stream. ////
//// SEEK_SET: Set position relative to the ////
//// beginning of the stream. ////
//// ////
//// fateof(FILE *fstream) ////
//// Returns non-zero if the stream/file position is at EOF, ////
//// non-zero if there are still data left in the stream. ////
//// ////
//// faterror(FILE *fstream): ////
//// Returns non-zero if there have been errors with the stream, ////
//// zero if the stream has been operating correctly since it has ////
//// been opened. ////
//// ////
//// fatread(void* buffer, int size, int32 num, FILE* fstream) ////
//// Reads size*num chars from the stream, saves to buffer. ////
//// ////
//// fatwrite(void* buffer, int size, int32 num, FILE* fstream) ////
//// Writes size*num chars from buffer to the stream. ////
//// ////
//// fatflush(FILE *fstream) ////
//// Flushes the buffer in a stream. ////
//// ////
//// clearerr(FILE *fstream) ////
//// Clears any error flags in the stream. ////
//// ////
//// rewind(FILE *fstream) ////
//// Send the stream back to the beginning of the file. ////
//// ////
//// fatpos_t fattell(FILE *fstream) ////
//// Returns the current position of the stream. ////
//// ////
//// rm_file(char *fname) ////
//// Removes a file. ////
//// ////
//// rm_dir(char *dirname) ////
//// Removes a directory. ////
//// ////
//// mk_file(char *fname) ////
//// Makes a file, file will be blank. ////
//// ////
//// mk_dir(char *dirname) ////
//// Makes a directory. ////
//// ////
//// format(int32 mediaSize) ////
//// Formats the media into a FAT32 or FAT16 file system. ////
//// If you specify a mediaSize larger than the actual media bad ////
//// things will happen. If you specify a mediaSize smaller than ////
//// the actual media size will simply limit the filesystem from ////
//// using 0 to mediaSize-1. Anything after mediaSize can be used ////
//// by the application (perhaps as a general purpose EEPROM?) ////
//// NOTE: Windows thinks the filesystem is RAW. ////
//// NOTE: This may be a little buggy. ////
//// ////
/////////////////////////////////////////////////////////////////////////
//// ////
//// This library was written to use CCS's MMC/SD library as the ////
//// media source. If you want to use a different media source, ////
//// you must provide the following 4 functions: ////
//// ////
//// int8 mmcsd_init(void); ////
//// Initializes the media. This will be called by fat_init(). ////
//// ////
//// int8 mmcsd_read_bytes(int32 a, int16 s, char *p); ////
//// Read s bytes from p to the media starting at address a. ////
//// ////
//// int8 mmcsd_write_data(int32 a, int16 s, char *p); ////
//// Write s bytes from p to the media starting at address a. ////
//// To maximize throughput on some medias, it's a good idea to ////
//// buffer writes in this function. ////
//// ////
//// int8 mmcsd_flush_buffer(void); ////
//// If your write function is buffering writes, this will flush ////
//// the buffer and write it to the media. ////
//// ////
//// All four functions should return 0 if OK, non-zero if error. ////
//// ////
/////////////////////////////////////////////////////////////////////////
//// (C) Copyright 2007 Custom Computer Services ////
//// This source code may only be used by licensed users of the CCS ////
//// C compiler. This source code may only be distributed to other ////
//// licensed users of the CCS C compiler. No other use, ////
//// reproduction or distribution is permitted without written ////
//// permission. Derivative programs created using this software ////
//// in object code form are not restricted in any way. ////
/////////////////////////////////////////////////////////////////////////

// NOTE This library has no concept of what time and date it currently is.
// All files and folders created or modified using this library
// will have invalid/inaccurate timestamps and datestamps.

// NOTE To save on ROM and RAM space, the user of this library will have to
// define what type of FAT they will be working with. The defines are
// in the Useful Defines section below.

// NOTE For faster writing or appending for an application such as a logger,
// uncomment #FAST_FAT below. This will make the FAT library assume
// there is one file on the card to write or append to, thereby
// making writing and appending much faster. Reading is impossible in
// this mode.
// THIS IS NOT TESTED VERY WELL YET!

// NOTE The current maximum file name length (full path) is 32 characters
// long. If longer file names are desired, change the
// MAX_FILE_NAME_LENGTH define below. Creating a file whose full path
// is longer than MAX_FILE_NAME_LENGTH may lead to weird operation. Keep
// in mind that making this define larger will make your RAM usage go
// up.

struct iobuf
{
fatpos_t
Bytes_Until_EOF, // how many bytes until the stream's end of file
Cur_Char, // the current byte that the stream is pointing at
Entry_Addr, // the entry address of the file that is associated with the stream
Parent_Start_Addr, // the parent's start adddress of the file that is associated with the stream
Size, // the size of the file that is associated with the stream
Start_Addr; // the beginning of the data in the file that is associated with the stream

enum filetype File_Type; // the type of file that is associated with the stream

enum ioflags Flags; // any associated input/output flag

int Buf[STREAM_BUF_SIZE]; // this is a buffer so that during fatputc() or fatgetc()
// the media won't have to be read at every character
};
typedef struct iobuf FILE;

/*
signed int fatopen(char fname[], char mode[], FILE* stream)
Summary: This will open up a file stream for reading, writing, or appending.
Param fname: The full path of the file to open.
Param mode: The mode to open up the stream into.
"r" = Read
"w" = Write
"a" = Append
"rb", "wb", "ab" = Read, Write, or Append in Binary mode
Param stream: The stream to open up.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: fname must be in the form of /filename.fil for a file in the root directory
/Directory/filename.fil for a file in a subdirectory of root
/Directory/Subdirectory/filename.fil and so on...
Note: Standard C will make a file in case a file isn't found,
however due to recursion this is not possible in CCSC.
*/
signed int fatopen(char fname[], char mode[], FILE* stream)
{
int fname_parse_pos = 1; // the current index of the fname character

// figure out how deep we have to go, count how many '/' we have in the string
while(fname[fname_parse_pos] != '\0')
{
if(fname[fname_parse_pos] == '/')
depth++;
fname_parse_pos += 1;
}

// start the fname index at 1 to skip over the '/'
fname_parse_pos = 1;

// open up to the subdirectory, if possible
while(depth > 0)
{
// find the name of our next target directory
target_file_parse_pos = 0;
while(fname[fname_parse_pos] != '/')
{
// check to make sure that we're not at the end of a poorly formatted string
if(fname[fname_parse_pos] == '\0')
return EOF;

// check to see if we're trying to open just a directory
if(fname[fname_parse_pos] == '\0')
{
*stream = cur_stream;
return GOODEC;
}

// now that we have the location of the subdirectory that the file is in, attempt to open the file
target_file_parse_pos = 0;
while(fname[fname_parse_pos] != '\0')
{
// fill up the buffer and increment the indexes
target_file[target_file_parse_pos] = fname[fname_parse_pos];
fname_parse_pos += 1;
target_file_parse_pos += 1;
}

// tack on a \0 to the end of the target file to terminate the string
target_file[target_file_parse_pos] = '\0';

/*
signed int fatreopen(char fname[], char mode[], FILE* old_stream, FILE* new_stream)
Summary: This will close a stream and then reopen it using new parameters.
Param fname: The full path of the file to open.
Param mode: The mode to open up the stream into.
"r" = Read
"w" = Write
"a" = Append
"rb", "wb", "ab" = Read, Write, or Append in Binary mode
Param stream: The stream to close and reopen.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: fname must be in the form of /filename.fil for a file in the root directory
/Directory/filename.fil for a file in a subdirectory of root
/Directory/Subdirectory/filename.fil and so on...
Note: Standard C will make a file in case a file isn't found,
however due to recursion this is not possible in CCSC.
*/
signed int fatreopen(char fname[], char mode[], FILE* stream)
{
// close the old stream
if(fatclose(stream) == EOF)
return EOF;

/*
signed int fatclose(FILE* stream)
Summary: Closes a stream and commits any changes done to the file.
Param: The stream to close.
Returns: EOF if there was a problem, 0 if everything went okay.
*/
signed int fatclose(FILE* stream)
{
int ec = 0;

/*
signed int fatgetc(FILE* stream)
Summary: Gets a character from a stream.
Param: The stream to get a character from.
Returns: The character that was gotten from the stream,
EOF if the stream has reached the end of the file or doesn't have permissions to read,
*/
signed int fatgetc(FILE* stream)
{
char ch; // character read in

// check to see if the stream has proper permissions to read
if(stream->Flags & Read)
{
// when the number of bytes until eof hit zero, we know we are at the end of any file
if(stream->Bytes_Until_EOF == 0)
{
stream->Flags |= EOF_Reached;
return EOF;
}

// read in the next byte in the buffer
if(read_buffer(stream, &ch) == EOF)
return EOF;

/*
char* fatgets(char* str, int num, FILE* stream)
Summary: Reads characters from a stream into a string.
Param str: A pointer to the beginning of the string to put characters into.
Param num: The number of characters to put into the string - 1.
Param stream: The stream to read from.
Returns: A pointer to the most recently added character, or NULL if there was an error.
Note: If a newline is read from the stream, then str will be terminated with a newline.
If num - 1 or EOF is reached, then str will be null terminated.
*/
char* fatgets(char* str, int num, FILE* stream)
{
int i; // counter for loops

/*
signed int fatputs(char* str, FILE* stream)
Summary: Writes characters from a string into a stream.
Param str: A pointer to the beginning of the string to write into the stream.
Param stream: The stream to write into.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int fatputs(char* str, FILE* stream)
{
int i = 0; // counter for loops

/*
signed int fatprintf(FILE* stream)
Summary: This will print off the entire contents of the stream to the console.
Param: The stream to print off.
Returns: The last character printed off to the console.
*/
signed int fatprintf(FILE* stream)
{
signed int ch; // character read in

// keep on printf any characters read in as long as we don't run into an end of file or a media error
do
{
ch = fatgetc(stream);
printf("%c", ch);
} while(ch != EOF);

return ch;
}

/*
signed int fatgetpos(FILE* stream, fatpos_t* position)
Summary: Returns the current position of where the stream is pointing to relative to the beginning of the stream.
Param stream: The stream to get the position of.
Param position: A pointer to a variable put the current position of the pointer into.
Returns: 0 on success.
*/
signed int fatgetpos(FILE* stream, fatpos_t* position)
{
*position = stream->Size - stream->Bytes_Until_EOF;
return 0;
}

/*
signed int fatsetpos(FILE* stream, fatpos_t* position)
Summary: Sets the current position of where the stream is pointing to in memory relative to the beginning of the stream.
Param stream: The stream to set the position of.
Param position: A pointer the a variable that has the value of the new position.
Returns: 0 on success, or EOF if there was error.
*/
signed int fatsetpos(FILE* stream, fatpos_t* position)
{
#ifndef FAST_FAT
#ifdef FAT32
int32 cur_cluster; // the current cluster we're pointing to
#else // FAT16
int16 cur_cluster; // the current cluster we're pointing to
#endif // #ifdef FAT32
int32 i; // pointer to memory
#endif // #ifndef FAST_FAT

// check to see if we want to just rewind the file
if(*position == 0)
{
rewind(stream);
return GOODEC;
}

// this whole process is much different and easier if we're writing or appending at a spot after EOF
// this will essentially write null characters to the file from EOF to the desired position
if(((stream->Flags & Write) || (stream->Flags & Append)) && (stream->Size < *position))
{
while(stream->Size < *position)
if(fatputc('\0', stream) == EOF)
return EOF;

return 0;
}

#ifdef FAST_FAT
stream->Cur_Char = stream->Start_Addr + *position;
#else // NO FAST_FAT
// figure out how many clusters into the file the position is to be set to
i = *position / Bytes_Per_Cluster;
cur_cluster = addr_to_cluster(stream->Start_Addr);

/*
signed int fatseek(FILE* stream, int32 offset, int origin)
Summary: This will set the position of the file stream according to the input. The EOF flag will also be cleared.
Param stream: The stream to set the position of.
Param offset: How many bytes relative of origin the file stream position will be set.
Param origin: This will be one of 3 values...
SEEK_CUR: Set position relative to the current stream position.
SEEK_END: Set position relative to the end of the stream.
SEEK_SET: Set position relative to the beginning of the stream.
Returns: 0 on success, or EOF if there was error.
*/
signed int fatseek(FILE* stream, int32 offset, int origin)
{
int32 myoffset; // since fatsetpos requires a pointer to a variable, we need this here

/*
signed int fateof(FILE* stream)
Summary: Determines whether or not the stream is at the end of the file.
Param: The stream to query for EOF.
Returns: A non-zero value if the file is at EOF,
0 if the file is not at EOF.
*/
signed int fateof(FILE* stream)
{
return stream->Flags & EOF_Reached;
}

/*
signed int fatread(void* buffer, int size, int32 num, FILE* stream)
Summary: Fills up an array with data from a stream.
Param buffer: A pointer to the beginning of an array of any type.
Param size: How many bytes each element in the array is.
Param num: How many elements to fill in the array.
Param stream: The stream to read from.
Returns: How many values were written to the array.
*/
signed int fatread(void* buffer, int size, int32 num, FILE* stream)
{
int32 i; // counter for loop

/*
signed int fatwrite(void* buffer, int size, int32 count, FILE* stream )
Summary: Fills up a stream with data from an array
Param buffer: A pointer to the beginning of an array of any type.
Param size: How many bytes each element in the array is.
Param num: How many elements to write to the stream.
Param stream: The stream to write to.
Returns: How many values were written to the stream.
*/
signed int fatwrite(void* buffer, int size, int32 count, FILE* stream )
{
int32 i; // counter for loop

/*
signed int remove(char* fname)
Summary: Removes a file from disk.
Param: The full path of the file to remove.
Returns: 0 on success, or EOF if there was error.
Note: This function does not work for removing directories, use rm_dir instead.
Note: fname must be in the form of /filename.fil for a file in the root directory
/Directory/filename.fil for a file in a subdirectory of root
/Directory/Subdirectory/filename.fil and so on...
*/
signed int remove(char* fname)
{
if(rm_file(fname) == EOF)
return EOF;

return 0;
}

/*
signed int faterror(FILE* stream)
Summary: Checks for an error in a given stream.
Param: The stream to check for an error in.
Returns: A non-zero value of there is an error in the stream,
0 if there is no error in the stream
*/
signed int faterror(FILE* stream)
{
return stream->Flags & 0xF0;
}

/*
void rewind(FILE* stream)
Summary: Sets the stream to point back to the beginning of a file.
Param: The stream to rewind.
Returns: Nothing.
*/
void rewind(FILE* stream)
{
// set the stream back to the beginning
stream->Cur_Char = stream->Start_Addr;
stream->Bytes_Until_EOF = stream->Size;
}

/*
fatpos_t fattell(FILE* stream)
Summary: Returns the current position of where the stream is pointing to relative to the beginning of the stream.
Param: The stream to return the position of.
Returns: The position of where the stream is pointing to relative to the beginning of the stream, or 0 if there was a problem.
*/
fatpos_t fattell(FILE* stream)
{
fatpos_t retval;

if(fatgetpos(stream, &retval) != 0)
return 0;

return retval;
}

/// Non-Standard C Functions ///

/*
signed int rm_file(char fname[])
Summary: Deletes a file.
Param: The full path of the file to delete.
Returns: GOODEC if everything went okay, EOF if there was a problem.
Note: fname must be in the form of /filename.fil for a file in the root directory
/Directory/filename.fil for a file in a subdirectory of root
/Directory/Subdirectory/filename.fil and so on...
*/
signed int rm_file(char fname[])
{
int
order,
ulinked_entry = 0xE5; // 0xE5 is put into the file's entry to indicate unlinking

// we need to un-link the file's clusters from the FAT if there are clusters allocated
if(stream.Start_Addr > Root_Dir)
{
if(dealloc_clusters(addr_to_cluster(stream.Start_Addr)) == EOF)
return EOF;
}
// get rid of the first entry
i = stream.Entry_Addr;
if(mmcsd_write_data(i, 1, &ulinked_entry) == EOF)
return EOF;

// check to see if there is a long file name
get_prev_entry(&i);
if(mmcsd_read_data(i + 11, 1, &order) == EOF)
return EOF;

// get rid of all of the long file name entries if they're there
while(order == 0x0F)
{
if(mmcsd_write_data(i, 1, &ulinked_entry) == EOF)
return EOF;

if(get_prev_entry(&i) == EOF)
return EOF;

if(mmcsd_read_data(i + 11, 1, &order) == EOF)
return EOF;
}

if(fatclose(&stream) == EOF)
return EOF;

return GOODEC;
}

/*
signed int rm_dir(char dname[])
Summary: Deletes a directory.
Param: The full path of the directory to delete.
Returns: GOODEC if everything went okay, EOF if there was a problem.
Note: dname must be in the form of /Dirname/ for a directory in the root directory
/Dirname/Subdirname/ for a directory in a subdirectory of root and so on...
Note: This function cannot remove all of the files
and subdirectories of the directory. Manually remove all subdirectories
and files before calling this command.
*/
signed int rm_dir(char dname[])
{
char mode[] = "r"; // r is the safest mode to remove files with

// jump over the . and .. entries in the directory
stream.Entry_Addr = stream.Start_Addr + 64;

// check to make sure that there isn't stuff in this directory
if(get_next_file(&stream) != EOF)
return EOF;

// since removing files is a lot like removing directories, we
// can just call rm_file
if(rm_file(dname) == EOF)
return EOF;

return GOODEC;
}

/*
signed int mk_file(char fname[])
Summary: Creates a file.
Param: The full path of the file to create.
Returns: GOODEC if everything went okay, EOF if there was a problem.
Note: This function will not create directories if parent directories don't exist.
Note: fname must be in the form of /filename.fil for a file in the root directory
/Directory/filename.fil for a file in a subdirectory of root
/Directory/Subdirectory/filename.fil and so on...
*/
signed int mk_file(char fname[])
{
char
filename[MAX_FILE_NAME_LENGTH], // the file name we're trying to make
mode[] = "r"; // reading is the safest mode to work in

int
buf, // buffer to hold values
entire_entry[0x20],// entire first entry
filename_pos = 0, // the current parse position of the file name we're trying to create
fname_pos; // the current parse position of the input the the function

int32 i; // pointer to memory

FILE stream; // the stream that we'll be working with

// attempt to open up to the directory
if(fatopen(fname, mode, &stream) == GOODEC)
return EOF; // we shouldn't get an GOODEC back from fatopen()

// check to see if the file is already there.
if(!(stream.Flags & File_Not_Found))
return EOF;

/*
signed int mk_dir(char dname[])
Summary: Creates a directory.
Param: The full path of the directory to create.
Returns: GOODEC if everything went okay, EOF if there was a problem.
Note: This function will not create directories if parent directories don't exist.
Note: dname must be in the form of /Dirname/ for a directory in the root directory
/Dirname/Subdirname/ for a directory in a subdirectory of root and so on...
*/
signed int mk_dir(char dname[])
{
char
dirname[MAX_FILE_NAME_LENGTH], // the directory name we're trying to make
entire_entry[0x20], // used to hold the link entries (. and ..) to the directory and its parent
mode[] = "r"; // reading is the safest mode to work in

int
dirname_pos = 0, // the current parse position of the directory name we're trying to create
dname_pos, // the current parse position of the input the the function
j; // counter for loops

int32 i; // pointer to memory

FILE stream; // the stream that we'll be working with

// attempt to open up to the directory
if(fatopen(dname, mode, &stream) == GOODEC)
return EOF; // we shouldn't get an GOODEC back from fatopen()

// check to see if the directory is already there.
if(!(stream.Flags & File_Not_Found))
return EOF;

/// Functions' Utility Functions ///
/// NOTE: A library user should not need to use any of the functions in this section ///

/*
signed int set_file(char fname[], int attrib, FILE* stream)
Summary: This will set the stream to point to the specified file.
Param fname: The file name to search for.
Param attrib: The file attributes to search for. 0x10 is a directory, 0x20 is a file.
Param stream: The stream to set.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: The stream has to be pointing to the parent directory's start address when coming in to this function.
*/
signed int set_file(char fname[], int attrib, FILE* stream)
{
int
cur_attrib, // the attribute of the most recently read entry
cur_state, // the state of the most recently read entry
ec = 0; // error checking byte

// check to make sure that this entry exists and the entry is the type we're looking for
if((cur_state != 0xE5) && (cur_attrib == attrib))
{
#ifndef FAST_FAT
// get the long file name of the current entry
if(get_file_name(i, name_buffer) == EOF)
return EOF;

// if the target entry and the long file name are equal, strcmp will return a zero
if(strcmp(fname, name_buffer) == 0)
#endif // #ifndef FAST_FAT
{
// we have found our target entry, set the current entry and break out of this function
// set stream's parent address
stream->Parent_Start_Addr = stream->Start_Addr;

// check to make sure that the next iteration will give us a contiguous cluster
if(get_next_entry(&i) == EOF)
return EOF;

} while(cur_state != 0x00);

// if we reach this point, we know that the file won't be found
stream->Flags |= File_Not_Found;
return EOF;
}

/*
signed int get_file_name(int32 file_entry_addr, char name[])
Summary: This will get a name of a file.
Param file_entry_addr: The position in memory that the file's entry is.
Param name[]: The place to put the name of the file into.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int get_file_name(int32 file_entry_addr, char name[])
{
int
j, // counter for loops
k = 0, // current character in array
order, // byte to hold the current long file name order
type; // the type of entry that was just read in

int32 i; // pointer for memory

// the long file name isn't fragmented across clusters
i = file_entry_addr;

// check to make sure that this file has a long file name
if(mmcsd_read_data(i - 0x15, 1, &type) != GOODEC)
return EOF;

/*
signed int set_file_name(int32 parent_dir_addr, int32 entry_addr, char name[])
Summary: Creates both a short and long file name at the first free entry in a directory.
Param parent_dir_addr: The address of the parent directory to create the short file name in.
Param entry_addr: The address the function put the short file name entry.
Param name: The full file name.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int set_file_name(int32 parent_dir_addr, int32* entry_addr, char name[])
{
char sname[12]; // place to hold the short file name

signed int name_pos = 0; // the current parse position of name[]

int
chksum, // the long file name checksum
entire_entry[0x20], // the entire entry to put write onto the media
entry_pos, // the current position inside of entire_entry
long_entry = 1; // the current long entry number we're on

/*
signed int make_short_file_name(int32 parent_dir_addr, char fname[], char sname[])
Summary: Creates a unique short file name in a directory.
Param parent_dir_addr: The address of the parent directory to create the short file name in.
Param fname: The full file name.
Param sname: Character array that will hold the short file name upon function completion.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int make_short_file_name(int32 parent_dir_addr, char fname[], char sname[])
{
char
val[12] = " ",
cur_fname[12] = " ",
cur_fnum[7] = " ";

int
buf,
ext_pos,
fname_parse_pos = 0,
val_parse_pos = 0;

int32
fnum = 0,
i;

// figure out where the extension position is
ext_pos = strchr(fname, '.');

// now that we've got the short file name, we need to make it unique
i = parent_dir_addr;
if(mmcsd_read_data(i + 0x0B, 1, &buf) != GOODEC)
return EOF;

// keep reading until we hit empty space
while(buf != 0x00)
{
// check to see if this is a short file name entry
if((buf == 0x20) || (buf == 0x10))
{
// read in the short file name that we're currently pointing at
if(mmcsd_read_data(i, 11, cur_fname) != GOODEC)
return EOF;

cur_fname[11] = '\0';

// strcmp will return a 0 if the file name we're currently pointing at and the file name that we created above are the same
if(strcmp(cur_fname, val) == 0)
{
// we now need to create a unique file name
// increment the unique file number by one
fnum += 1;

// convert the unique file number to a string
sprintf(cur_fnum, "%lu", fnum);

// put the unique file number, along with a '~' into our short file name
fname_parse_pos = 0;

// find out the last posiiton of a space
val_parse_pos = strchr(val, ' ');
if(val_parse_pos == 0)
// if there isn't a space, then we're going to have to put the ~x at the end of the short name
val_parse_pos = 7 - strlen(cur_fnum);
else
// if there is a space, then put the ~x there
val_parse_pos -= val + 2;

// copy the short name into the input buffer
for(i = 0; i < 12; i += 1)
sname[i] = val[i];

return GOODEC;
}

/*
int long_name_chksum (int* FcbName)
Summary: Returns an unsigned byte checksum computed on an unsigned byte
array. The array must be 11 bytes long and is assumed to contain
a name stored in the format of a MS-DOS directory entry.
Param: Pointer to an unsigned byte array assumed to be 11 bytes long.
Returns: Sum An 8-bit unsigned checksum of the array pointed to by pFcbName.
*/
int long_name_chksum (int* pFcbName)
{
int
FcbNameLen,
Sum = 0;

/*
signed int check_invalid_char(char fname[])
Summary: Checks the filename for any invalid characters.
Param: The name of the file to check.
Returns: EOF if an invalid character was found, GOODEC otherwise.
*/
signed int check_invalid_char(char fname[])
{
int fname_pos;

/*
signed int get_next_free_cluster(int16* my_cluster)
Summary: Will go through the FAT and find the first unallocated cluster.
Param: Pointer to a variable that will the the starting cluster of the serach.
When a free cluster is found, the cluster number will be put into this variable.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: This gets a little slow when dealing with a card with lots of stuff on it; sorry about that.
*/
#ifdef FAT32
signed int get_next_free_cluster(int32* my_cluster)
#else
signed int get_next_free_cluster(int16* my_cluster)
#endif
{
#ifdef FAST_FAT
*my_cluster += 1;
return GOODEC;
#else // NO FAST_FAT

#endif // #ifdef FAT32
// if we reach this point, we are out of disk space
return EOF;
#endif // #ifdef FAST_FAT
}

/*
signed int get_next_file(FILE* stream)
Summary: Will point the stream to the next file in the directory.
Param: The stream to move.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: This will not set the Buf or Flag parameters.
*/
signed int get_next_file(FILE* stream)
{
int32
cluster,
cur_addr,
size;

/*
signed int get_next_file(FILE* stream)
Summary: Will point the stream to the previous file in the directory.
Param: The stream to move.
Returns: EOF if there was a problem, GOODEC if everything went okay.
Note: This will not set the Buf or Flag parameters.
*/
signed int get_prev_file(FILE* stream)
{
int32
cluster,
cur_addr,
size;

/*
signed int get_next_free_addr(int32* my_addr)
Summary: Finds the next unallocated address.
Param: Pointer to a variable that will the the starting address of the serach.
When a free address is found, the address will be put into this variable.
Returns: EOF if there was a problem, GOODEC if everything went okay.
*/
signed int get_next_free_addr(int32* my_addr)
{
int val; // buffer to hold values

int32 cur_addr; // pointer to memory

// make a copy of *my_addr
cur_addr = *my_addr;

// keep on getting addresses until we hit a free one
do
{
if(mmcsd_read_data(cur_addr, 1, &val) != GOODEC)
return EOF;

if(get_next_addr(&cur_addr) == EOF)
return EOF;
} while(val != 0);

*my_addr = cur_addr;

return GOODEC;
}

/*
signed int get_next_entry(int32* start_addr)
Summary: Gets the next entry in a directory.
Param: The address to start looking for an entry.
If an entry is found, it will be put into this variable.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int get_next_entry(int32* start_addr)
{
int32 i;

i = *start_addr;

i += 0x1F;

if(get_next_addr(&i) == EOF)
return EOF;

*start_addr = i;

return GOODEC;
}

/*
signed int get_prev_entry(int32* start_addr)
Summary: Finds the next free entry in a directory.
Param: The address to start looking for a free entry.
If a free entry is found, it will be put into this variable.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int get_prev_entry(int32* start_addr)
{
int32 i;

i = *start_addr;

i -= 0x1F;

if(get_prev_addr(&i) == EOF)
return EOF;

*start_addr = i;

return GOODEC;
}

/*
signed int get_next_free_entry(int32* start_addr)
Summary: Finds the next free entry in a directory.
Param: The address to start looking for a free entry.
If a free entry is found, it will be put into this variable.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int get_next_free_entry(int32* start_addr)
{
int buf;

// erase all of the data in the newly linked cluster
if(clear_cluster(next_cluster) == EOF)
return EOF;

// put the current character to this position
*new_cluster_addr = cluster_to_addr(next_cluster);

return GOODEC;
}

/*
signed int dealloc_clusters(int16 start_cluster)
Summary: De-allocates linked clusters from the FAT.
Param: The starting cluster to deallocate.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
#ifdef FAT32
signed int dealloc_clusters(int32 start_cluster)
#else
signed int dealloc_clusters(int16 start_cluster)
#endif
{
#ifdef FAT32
int32
cur_cluster, // the current cluster we're pointing to
next_cluster; // the next cluster we're going to point to
#else // FAT16
int16
cur_cluster, // the current cluster we're pointing to
next_cluster; // the next cluster we're going to point to
#endif // #ifdef FAT32

// figure out where the first cluster is
next_cluster = cur_cluster = start_cluster;
do
{
// get the next cluster
if(get_next_cluster(&next_cluster) == EOF)
return EOF;

/*
signed int clear_cluster(int16 cluster)
Summary: Clears out all of the data in a given cluster.
Param: The cluster to clear out.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
#ifdef FAT32
signed int clear_cluster(int32 cluster)
#else
signed int clear_cluster(int16 cluster)
#endif
{
int
clear_entry[0x20],
j;

/*
signed int write_fat(int32 cluster, int32 data)
Summary: Writes specified data about a cluster to the FAT.
Param cluster: The cluster to modify the in the FAT.
Param data: The data about the cluster to put into the FAT.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
#ifdef FAT32
signed int write_fat(int32 cluster, int32 data)
{
if(mmcsd_write_data((cluster << 2) + FAT_Start, 4, &data) != GOODEC)
return EOF;

/*
signed int fat_init()
Summary: Initializes global variables that are essential for this library working
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
Note: This must be called before any other function calls in this library.
*/
signed int fat_init()
{
int ec = 0;

/*
signed int get_next_cluster(int16* my_cluster)
Summary: Gets the next linked cluster from the FAT.
Param: A pointer to a variable that holds a cluster.
This variable will then have the next linked cluster when the function returns.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
#ifdef FAT32
signed int get_next_cluster(int32* my_cluster)
#else
signed int get_next_cluster(int16* my_cluster)
#endif
{
// convert the current cluster into the address of where information about
// the cluster is stored in the FAT, and put this value into the current cluster
#ifdef FAT32
if(mmcsd_read_data((*my_cluster << 2) + FAT_Start, 4, my_cluster) != GOODEC)
return EOF;
#else // FAT16
if(mmcsd_read_data((*my_cluster << 1) + FAT_Start, 2, my_cluster) != GOODEC)
return EOF;
#endif // #ifdef FAT32
return GOODEC;
}

/*
signed int get_next_addr(int32* my_addr)
Summary: Get the next linked address.
Param: A pointer to a variable that holds an address.
This variable will then have the next linked address when the function returns.
Returns: EOF if there was a problem with the media or we've reached the last linked cluster in the FAT, GOODEC if everything went okay.
*/
signed int8 get_next_addr(int32* my_addr)
{
int32 temp;
#ifdef FAT32
int32 c;
#else
int16 c;
#endif

/*
signed int get_prev_addr(int32* my_addr)
Summary: Get the next linked address.
Param: A pointer to a variable that holds an address.
This variable will then have the next linked address when the function returns.
Returns: EOF if there was a problem with the media or we've reached the last linked cluster in the FAT, GOODEC if everything went okay.
*/
signed int8 get_prev_addr(int32* my_addr)
{
int32 temp;
#ifdef FAT32
int32 c;
#else
int16 c;
#endif

temp = *my_addr;
// if we're trying to go backwards one entry from the beginning of the root,
// we won't be able to...
if(temp <= Root_Dir)
return GOODEC;

// we're going to have to re-initialize the FAT, a bunch of parameters probably just changed
fat_init();

return GOODEC;
}

/// Debugging Utility Functions ///

/*
signed int disp_folder_contents(char foldername[])
Summary: Displays the contents of a folder.
Param: The folder to display the contents of.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int disp_folder_contents(char foldername[])
{
char filename[MAX_FILE_NAME_LENGTH]; // a place to hold a file name

FILE stream; // the stream that we're going to be working with

char mode[] = "r";

if(fatopen(foldername, mode, &stream) != GOODEC)
return EOF;

// printf off a header
printf("\r\n--%s--", foldername);

// start off at the root directory
stream.Entry_Addr = stream.Start_Addr;

while(get_next_file(&stream) != EOF)
{
// get the name of the file that we are at
if(get_file_name(stream.Entry_Addr, filename) != GOODEC)
return EOF;

/*
signed int dump_addr(int32 from, int32 to)
Summary: Display a series of addresses in a hex editor type fashion.
Param from: The beginning address to display.
Param to: The end address to display.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int dump_addr(int32 from, int32 to)
{
int
j, // counter for loops
val[0x10]; // buffer to hold values

// note that the to and from values are being rounded up and down
// this makes a nice "block" map in case someone inputs a number that
// isn't evenly divisible by 0x10
for(i = (from - (from % 0x10)); i <= (to + (to % 0x10)); i += 0x10)
{
// printf memory block
printf("\r\n%lX ", i);

/*
signed int dump_clusters(int32 from, int32 to)
Summary: Display a series of clusters in a memory map.
Param from: The beginning clusters to display.
Param to: The end clusters to display.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int dump_clusters(int32 from, int32 to)
{
// convert the clusters to addresses and dump
if(dump_addr(cluster_to_addr(from), cluster_to_addr(to)) != GOODEC)
return EOF;
}

/*
signed int fatprintfinfo(FILE* stream)
Summary: Display essential statistics about the file that a stream is pointing to.
Param: The stream to print off information about.
Returns: EOF if there was a problem with the media, GOODEC if everything went okay.
*/
signed int fatprintfinfo(FILE* stream)
{
int ec = 0;

Hi electr0dave,
I'm very new in SD CARD. I've already read a lot but I can't start with SD CARD.
Can you send me an example of a small software where I can read a simple switch (or any other thing) and record the reading in the SD CARD (2Gb).
I'm using 18F4520.
I have made many tries but nothing works.
thank you

byakuya

Joined: 09 Feb 2015Posts: 4Location: Mexico

Re: first steps on SD CARD

Posted: Tue Aug 11, 2015 5:28 pm

Hello friend tu as able to work with the SD but not alone is this source partner ._________________Cuando venga la inspiracion que me encuentre trabajando

soonc

Joined: 03 Dec 2013Posts: 153

Question and Note!

Posted: Tue Aug 22, 2017 10:18 pm

I tried using your modified version of the mmcsd.c but cannot get it to initialize a 2GB SD card.
I looked at this code

A voltage level translator is needed to interface a 5V device with the SD card. There are some different ways to implement the voltage level translator, for example using: 2 x 74VHCT125, or 2 x 74LVXC3245, or 74LVC125 and 74HCT125 ...

Actually I made some SD card projects using only voltage dividers (using resistors) with several types of PICs. I wrote a small driver for the SD card and a small FAT16/FAT32 library, here are some examples: