Monday, August 21, 2017

MMC/SD Card driver for CCS PIC C compiler


 SD Card driver for CCS C compiler
Basically the CCS C compiler has a driver for MMC (Multi Media Card) and SD (Secure Digital) cards, but many users face some problems with that driver especially with the new versions of the SD cards SDSC (Secure Digital Standard Capacity or just SD) and SDHC (Secure Digital High Capacity).
The maximum capacity for the SD (SDSC) card is 2 GB and the SDHC is 32 GB. There is an other type of the SD card which is SDXC with a capacity > 32 GB.
This topic gives an other driver for the SD cards (SDSC and SDHC) as well as the MMC cards. This driver works with any microcontroller no matter how many RAM it has. Yes, with this small driver we can use the microcontroller PIC16F84A which has only 68 bytes of data RAM to read the SD card sectors and send it via RS232.
This driver is tested with many types of SD cards including the SD and SDHC (8 GB, 4GB, 2GB, 1GB ......).
This driver works with hardware SPI and software SPI (hardware SPI is more faster than the software SPI).
The block (sector) length is fixed to 512 bytes.
Connection of the SD card with PIC microcontroller:
As mentioned above this driver can use hardware SPI or software SPI and always the SD card is connected to the microcontroller via 4 wires:
SDI (Serial Data In)
SDO (Serial Data Out)
SCK or SCL (Serial Clock)
CS (Chip Select)
The SDI may be called MISO (Master In Slave Out) and the SDO may also be called as MOSI (Master Out Slave In). Here, the master device is the microcontroller and the slave device is the SD card.
To use the hardware SPI we've to tell the compiler that the SD card uses the hardware SPI module of the microcontroller by defining 'SDCARD_SPI_HW'. Without that definition the compiler will automatically use the software SPI which requires the definitions of SDI, SDO and SCL pins. The CS pin must always be defined.
Examples:
Hardware SPI: the following example shows an SD card connected to a PIC microcontroller hardware SPI module pins and the CS pin is connected to pin RB3:
// SD Card module connections
#define   SDCARD_SPI_HW
#define   SDCARD_PIN_SELECT  PIN_B3
// End of SD card module connections
Software SPI: here the SD card communicates with the microcontroller via soft SPI:
// SD Card module connections
#define   SDCARD_PIN_SDI     PIN_B0
#define   SDCARD_PIN_SDO     PIN_B1
#define   SDCARD_PIN_SCL     PIN_B2
#define   SDCARD_PIN_SELECT  PIN_B3
// End of SD card module connections
MMC/SD card CCS C driver functions:
sdcard_init();
sdcard_read_byte(int32 addr, int8* data);
sdcard_read_sector(int32 sector_number, int8* data);
sdcard_read_data(int32 addr, int16 size, int8* data);
The following 3 functions need more than 512 bytes of data RAM and to use them we've to define 'SDCARD_WRITE' in the project code page:
sdcard_write_byte(int32 addr, int8 &data);
sdcard_write_sector(int32 sector_number, int8* data);
sdcard_write_data(int32 addr, int16 size, int8 *data);

sdcard_init():
Initializes the media (MMC/SD card). Returns 0 if OK, non-zero if error.
This function needs to be called before using other functions of this driver.
Example:
The following code initializes a SD card, if the variable i is zero the SD card has been successfully initialized, if i is not zero there was a problem with the initialization of the SD card.
i = sdcard_init();            // MMC/SD card initialize
sdcard_read_byte(int32 addr, int8* data):
This function reads only one byte where addr is the byte address and data is the pointer (1 byte). This function return 0 if reading was successful and non-zero if there was a problem.
Example: this example reads byte number 200 and save its value into variable j.
...
int8 i, j;
int32 byte_address = 200;
...
void main(){
  ...
  i = sdcard_init();
  if(i == 0){
    i = sdcard_read_byte(byte_address, &j);
    if(i == 0)
      printf("%X", j);      // Print the value of 'j' in hexadecimal format
  }
  ...
}
sdcard_read_sector(int32 sector_number, int8* data):
Reads one sector (block) of 512 bytes from the SD card. Sector_number is the SD card sector number starting from 0 and data is 512 bytes or higher of data storage array. This function needs more than 512 bytes of data RAM. Returns 0 if reading was successful and non-zero if there was a problem.
Example:
Read sector number 4 and save its data (512 bytes) into sector_data array.
...
int8 i, sector_data[512];
int32 sector_no = 4;
...
void main(){
  ...
  i = sdcard_init();
  if(i == 0){
    i = sdcard_read_sector(sector_no, sector_data);
  }
  ...
}
sdcard_read_data(int32 addr, int16 size, int8* data):
Reads data with size bytes starting from address addr and store that data into array data. in this function more size needs more RAM space. Returns 0 if reading was successful and non-zero if there was a problem.
Example:
Reads data of size 0x200 bytes starting from address 0x500 and save the data into buffer.
...
int8  i, buffer[0x200];
int16 read_size = 0x200;
int32 start_address = 0x500;
...
void main(){
  ...
  i = sdcard_init();
  if(i == 0){
    i = sdcard_read_data(start_address, read_size, buffer);
  }
  ...
}
sdcard_write_byte(int32 addr, int8 &data);
Writes only one byte of data where addr is byte address and data is byte value. Returns 0 if reading was successful and non-zero if there was a problem.
Example:
Write 0x4F to byte of address = 200.
...
int8 i, j = 0x4F;
int32 byte_address = 200;
...
void main(){
  ...
  i = sdcard_init();
  if(i == 0){
    i = sdcard_write_byte(byte_address, j);
  }
  ...
}
 
sdcard_write_sector(int32 sector_number, int8* data):
Writes data of 512 bytes to one SD card sector where sector_number is the sector number to be written to, and data is an array of 512 bytes. Returns 0 if reading was successful and non-zero if there was a problem.
Example:
Write data to sector 4.
...
int8  i, buffer[512];
int32 sector_number = 4;
...
void main(){
  ...
  i = sdcard_init();
  if(i == 0){
    i = sdcard_write_sector(sector_number, buffer);
  }
  ...
}
sdcard_write_data(int32 addr, int16 size, int8 *data):
Writes data of size bytes from pointer data starting from address addr. Returns 0 if reading was successful and non-zero if there was a problem.
Example: writes data from buffer of size = 512 bytes into the SD card starting from address 0x500.
...
int8  i, buffer[0x200];
int16 write_size = 0x200;
int32 start_address = 0x500;
...
void main(){
  ...
  i = sdcard_init();
  if(i == 0){
    i = sdcard_write_data(start_address, write_size, buffer);
  }
  ...
}
The functions: sdcard_read_sector , sdcard_write_sector , sdcard_write_byte and sdcard_write_data need more than 512 bytes of RAM. The last three functions need a 'SDCARD_WRITE' definition ( #define  SDCARD_WRITE ).

Driver C code download:
sdcard.c

Type specifiers used are the default types of the CCS C compiler where:
int8:  unsigned byte
int16: unsigned word (2 bytes)
int32: unsigned dword (4 bytes)
char: unsigned byte
MMC/SD Card driver for CCS PIC C compiler examples:
Read functions with hardware SPI:
MMC/SD Card raw data read using PIC16F877A microcontroller
Read and write functions with hardware SPI:
SD Card byte/sector read and write with PIC18F4550 microcontroller