Watch the video Tutorial: Connection

Memory Cards

A memory card (also called a flash memory card) is a solid-state electronic data storage device used for storing digital information. They are commonly used in many electronic devices, including digital cameras, mobile phones, laptop computers, MP3 players etc.
They are small, re-writable and are able to retain data without power.
This ability to retain data and the ability to re-write many times is the key for flash memory card applications, for example, in digital cameras, where the saved pictures are not lost after the memory card is removed from the camera. There are many different types of memory cards available in the market today. Some of the most commonly known memory cards are:

  • Smart media (SM) card
  • Multimedia card (MMC)
  • Compact flash (CF) card
  • Memory stick (MS) card
  • Microdrive
  • xD card
  • Secure digital (SD) card: SD cards are the most widely used memory cards today. They are based on MMC and in terms of functionality they are the same ( except that the SD has an optional encryption feature). The SD are physically thicker than MMCs and would not fit into MMC slots. The MMC, on the other hand, can be easily inserted into SD card slots.

SD cards are available in three different sizes: normal SD, miniSD, and microSD as shown on figure 1 below:

SD Cards                      

SD Card                            MiniSD                      MicroSD
Figure 1: SD Cards

Table 1: Technical Properties of Memory Cards

Note: The new SDXC Cards have capacity up to 2TB.

Most SD cards and MMCs particularly those sized below 2 gigabytes (GBs), use the FAT16 standard.
The FAT32 standard can be used to address memory sized between 2 gigabytes and 2 terabytes.
SD Card Pin Configuration
The card has nine pins, as shown in the figure 2 below, and a write-protect switch to enable/disable writing onto the card.

SD Card pins

Figure 2: SD Card pins

A standard SD card can be operated in two modes: the SD Bus mode and the SPI Bus mode.
In SD Bus mode, all the pins of the card are used, data is transferred using four pins (D0–D3), a clock (CLK) pin, and a command line (CMD).
In SPI Bus mode using a chip select (CS) and a CLK line. The following pins are used in SPI Bus mode:

  • Chip select: Pin 1
  • Data in: Pin 2
  • Clock: Pin 5
  • Data out: Pin 7
  • Positive: Pin 4
  • Ground: Pin 3 and 6

The Card operates with 3.3V supply voltage and these are the logic levels:
Maximum logic 0 output voltage, VOL = 0.4125 V
Minimum required logic 1 input voltage, VIH = 2.0625 V
Maximum logic 1 input voltage = 3.6 V
Maximum required logic 0 input voltage, VIL = 0.825 V
When connected to a PIC microcontroller, the output voltage (2.475 V) of the SD card to the PIC is enough to drive the input circuit of the microcontroller, but the typical logic 1 output voltage of a PIC microcontroller pin is 4.3 V, and this is too to apply to the card, where the maximum voltage should not exceed 3.6 V. As a result of this, it is required to use resistors at the inputs of the SD card to lower the input voltage.

SD cards can be interfaced to microcontrollers using two different protocols: The SD card protocol and the SPI protocol. The SPI protocol is the most commonly used protocol at the moment. In this tutorial, we will use the SPI protocol.
Figure 3 below shows a typical SD card interface to a PIC microcontroller in SPI mode. In this figure, 2.2K and 3.3K resistors are used as a potential divider circuit to lower the SD card input voltage to approximately 2.48 V, as shown below.

SD card input voltage = 4.3 V × 3.3 K / (2.2 K + 3.3 K) = 2.48 V.

SD card connected in SPI mode to Port C of PIC Microcontroller

Figure 3: SD card connected in SPI mode to Port C of PIC Microcontroller.

Read more on student Companion:

PIC Microcontroller Communication with SPI Bus

SD cards can consume up to 100–200 mA while reading or writing onto the card. This is usually a high current, and an appropriate voltage regulator capable of supplying the required current must be used in the design.
The card consumes approximately 150 μA in sleep (the card goes automatically in sleep mode if it doesn’t receive any command in 5ms). Figure 4 below shows an SD Card connected to PIC18F45K22

Interfacing SD Card to PIC18F45K22

Figure 4: Interfacing SD Card with PIC18F45K22

Chan’s FatFs Library 

Reading and writing onto SD cards is very complex and requires some complex functions and procedures to handle the card Input/Output operations correctly. For those who don’t want to fully understand the internal operation of SD cards, there are many SD card libraries that one can use. Microchip Libraries for Applications File I/O (Memory Disk Drive) is no longer supported for 8-bit PIC microcontrollers (PIC16F & PIC18F) at the moment version 1.02. This version is intended for 16-bit PIC microcontrollers (PIC24 & dsPIC33). The last 8-bit supported version (MDD v1.44) was in Microchip Libraries for Applications v2013-06-15. The problem this library files were made for the old MPLAB 8 compiler and it is a hassle to make them work with the new MPLAB X. In this article, we are going to use the popular Chan’s FatFs Generic FAT File System Module.

Watch the video Tutorial: FatFs Library

The FatFs is a generic FAT file system, is a fully free FAT implementation in the form of a library and application interface module, destined to small embedded systems. The FatFs module is written in compliance with ANSI C (C89) and completely separated from the disk I/O layer. Therefore it is independent of the platform. It can be incorporated into small microcontrollers with limited resource, such as 8051, PIC, AVR, ARM, Z80, 78K and etc. Also there is Petit FatFs module for tiny microcontrollers (RAM: 44 bytes work area + certain stack, code size: 2K-4K bytes).  Chan’s FatFs Generic FAT File System Module Website, has plenty resources that can help you understand this library: Examples for different microcontrollers, help files, functions description etc.

Description of Some FatFs functions

This function Register/Unregister a work area (mount SD Card). This function must always be called before any other file functions except for f_fdisk function
File system object: Pointer to the file system object to be registered and cleared. Null pointer unregisters the registered file system object. example: FATFS FatFs
Logical drive number: Pointer to the null-terminated string that specifies the logical drive. The string without drive number means the default drive. example: “” for default driver.
Initialization option: 0: Do not mount now (to be mounted later), 1: Force mounted the volume to check if the FAT volume is ready to work.
Return Values: When a function succeeded, it returns zero, otherwise returns non-zero value that indicates type of error. FR_OK (0: The function succeeded.), FR_INVALID_DRIVE, FR_DISK_ERR, FR_NOT_READY, FR_NO_FILESYSTEM

FATFS FatFs; // FatFs work area needed for each volume
if (f_mount(&FatFs, "", 1) == FR_OK) { // Mount SD
//then do some thing...

This function Open/Create a file.
File object structure:Pointer to the blank file object structure. Example: FIL Fil
File name: Pointer to the null-terminated string that specifies the file name to open or create. Example: “test.txt”
Mode flags: specifies the type of access and open method for the file. It is specified by a combination of following flags:

  • FA_OPEN_ALWAYS: Opens the file if it is existing. If not, a new file will be created.
  • FA_READ: Data can be read from the file.
  • FA_WRITE: Data can be written to the file. Combine with FA_READ for read-write access.
  • FA_CREATE_NEW: Creates a new file. The function fails if the file is existing.
  • FA_CREATE_ALWAYS: Creates a new file. If the file is existing, it will be truncated and overwritten.
  • FA_OPEN_EXISTING: Opens the file. The function fails if the file is not existing. (Default)

Return Values: When a function succeeded, it returns zero, otherwise returns non-zero value that indicates type of error. FR_OK (0: The function succeeded.)

// Open or create a file named "test.txt" with read/write access.
FIL Fil; // File object needed for each open file 
if (f_open(&Fil, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE) == FR_OK) { 
// Then do something

This function reads data from a file
Parameters: FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br);
File object: Pointer to the open file object.
buffer: Pointer to the buffer to store read data.
btr: Number of bytes to read in range of UINT type.
br: Pointer to the UINT variable to return number of bytes read. The value is always valid after the function call regardless of the result.
Return Values: When a function succeeded, it returns zero, otherwise returns non-zero value that indicates type of error. FR_OK (0: The function succeeded.)
This function writes data to a file.
Parameters: FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw);
fp: Pointer to the open file object structure.
buff: Pointer to the data to be written.
btw: Specifies number of bytes to write in range of UINT type.
bw: Pointer to the UINT variable to return the number of bytes written. The value is always valid after the function call regardless of the result.

// Open the file "test.txt" and Write "Hello world!" 
FATFS FatFs; // FatFs work area needed for each volume 
FIL Fil; // File object needed for each open file 
UINT bw;
if (f_mount(&FatFs, "", 1) == FR_OK) { // Mount SD 
if (f_open(&Fil, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE) == FR_OK) { // Open or create a file 
f_write(&Fil, "Hello world!\r\n", 14, &bw); // Write data to test.txt file

This function moves the file read/write pointer of an open file object. It can also be used to expand the file size (cluster pre-allocation).
Parameters: FRESULT f_lseek (FIL* fp, DWORD ofs);
fp: Pointer to the open file object.
ofs: Byte offset from top of the file.
This function reads a string from the file.
TCHAR* f_gets (
TCHAR* buff, /* [OUT] Read buffer */
int len, /* [IN] Size of the read buffer */
FIL* fp /* [IN] File object */
This funciton puts a character to the file.
int f_putc (
TCHAR chr, /* [IN] A character to put */
FIL* fp /* [IN] File object */
This function writes a string to the file.
int f_puts (
const TCHAR* str, /* [IN] String */
FIL* fp /* [IN] File object */
This function writes formatted string to the file.
int f_printf (
FIL* fp, /* [IN] File object */
const TCHAR* fmt, /* [IN] Format stirng */

This function gets the size of a file.
DWORD f_size (
FIL* fp /* [IN] File object */
This function closes an open file.
FRESULT f_close (
FIL* fp /* [IN] Pointer to the file object */
More details can be obtained from the  popular Chan’s FatFs Generic FAT File System Module. website.

The Disk I/O Interface

Since the FatFs module is completely separated from the disk I/O layer, it requires at least the following
functions to access the physical media:
• disk_initialize – Initialize disk drive
• disk_status – Get disk status
• disk_read – Read sector(s)
• disk_write – Write sector(s)
• disk_ioctl – Control device dependent features
• get_fattime – Get current time
The low level disk I/O module is not a part of FatFs module so that it must be provided by user these are the SPI bus communication routines. These are configured in diskio.h file using SPI functions generated by MPLAB Code Configurator.

MPLAB Code Configurator

The code below is the modified diskio.h file:


#ifdef __cplusplus
extern "C" {

#define _USE_WRITE	1	/* 1: Enable disk_write function */
#define _USE_IOCTL	1	/* 1: Enable disk_ioctl fucntion */

#define _SD_SPI 1   //we are selecting to use SPI 1
/* This option defines the SPI port to be used.
/   0: Default port (for devices with a single MSSP)
/   1: SPI 1
/   2: SPI 2 */

#include "integer.h"

/* Status of Disk Functions */

/* Results of Disk Functions */
typedef enum {
	RES_OK = 0,		/* 0: Successful */
	RES_ERROR,		/* 1: R/W Error */
	RES_WRPRT,		/* 2: Write Protected */
	RES_NOTRDY,		/* 3: Not Ready */
	RES_PARERR		/* 4: Invalid Parameter */

/* Prototypes for disk control functions */

DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);

/* Disk Status Bits (DSTATUS) */

#define STA_NOINIT		0x01	/* Drive not initialized */
#define STA_NODISK		0x02	/* No medium in the drive */
#define STA_PROTECT		0x04	/* Write protected */

/* Command code for disk_ioctrl fucntion */

/* Generic command (Used by FatFs) */
#define CTRL_SYNC			0	/* Complete pending write process (needed at _FS_READONLY == 0) */
#define GET_SECTOR_COUNT	1	/* Get media size (needed at _USE_MKFS == 1) */
#define GET_SECTOR_SIZE		2	/* Get sector size (needed at _MAX_SS != _MIN_SS) */
#define GET_BLOCK_SIZE		3	/* Get erase block size (needed at _USE_MKFS == 1) */
#define CTRL_TRIM			4	/* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */

/* Generic command (Not used by FatFs) */
#define CTRL_POWER			5	/* Get/Set power status */
#define CTRL_LOCK			6	/* Lock/Unlock media removal */
#define CTRL_EJECT			7	/* Eject media */
#define CTRL_FORMAT			8	/* Create physical format on the media */

/* MMC/SDC specific ioctl command */
#define MMC_GET_TYPE		10	/* Get card type */
#define MMC_GET_CSD			11	/* Get CSD */
#define MMC_GET_CID			12	/* Get CID */
#define MMC_GET_OCR			13	/* Get OCR */
#define MMC_GET_SDSTAT		14	/* Get SD status */

/* ATA/CF specific ioctl command */
#define ATA_GET_REV			20	/* Get F/W revision */
#define ATA_GET_MODEL		21	/* Get model name */
#define ATA_GET_SN			22	/* Get serial number */

/* PIC18 SPI definitions and functions */

/* Note: sd_init() must open SPI at a speed between 100-400 KHz */
 //These #define represent the MCC generated functions. If you are using PIC18F Peripheral libraries then you can replace these functions with OpenSPI(), WriteSPI(), ReadSPI() depending on the PIC you are using. You can define your own functions as well. 
#if _SD_SPI == 1

#define sd_init()	SPI1_Initialize() 
#define sd_open()	SPI1_Open()
#define sd_tx(d)	SPI1_Exchange8bit(d)
#define sd_rx()		SPI1_Exchange8bit(0xFF)

#elif _SD_SPI == 2

#define sd_init()	SPI2_Initialize()
#define sd_open()	SPI2_Open()
#define sd_tx(d)	SPI2_Exchange8bit(d)
#define sd_rx()		SPI2_Exchange8bit(0xFF)


#define sd_init()	SPI_Initialize()
#define sd_open()	SPI_Open()
#define sd_tx(d)	SPI_Exchange8bit(d)
#define sd_rx()		SPI_Exchange8bit(0xFF)


#ifdef __cplusplus


There are many options to configure the functions of FatFs for each project. The configuration options are defined in the ffconf.h.

Watch the video Tutorial: Code

The code below is main.c file. In this example, a file “test.txt” is created, then open and a text message is written into it: “Hello world! This is text message written to sd card” .

#include "mcc_generated_files/mcc.h"
#include "ff.h"

FATFS FatFs; /* FatFs work area needed for each volume */
FIL Fil; /* File object needed for each open file */

Main application
void main(void) {
UINT bw;

// Initialize the device

if (f_mount(&FatFs, "", 1) == FR_OK) { /* Mount SD */

if (f_open(&Fil, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE) == FR_OK) { /* Open or create a file */

if ((Fil.fsize != 0) && (f_lseek(&Fil, Fil.fsize) != FR_OK)) goto endSD; /* jump to end of the file to append data */

f_write(&Fil, "Hello world! This is text message written to sd card\r\n", 54, &bw); /* Write data to the file */

endSD: f_close(&Fil); /* Close the file */

while (1) {
// Add your application code

You can download the full project files (MPLAB XC8 source code and Proteus Schematic design) below here. All the files are zipped, you will need to unzip them (Download a free version of the Winzip utility to unzip files).

Download Mplab X Project: SD_Card_FatFs