Monday, August 21, 2017

SD Card byte/sector read and write with PIC18F4550 microcontroller


Interfacing PIC18F4550 with SD card
With the help of the small driver of the MMC/SD cards we can easily read and write any sector and any byte located in the media. First download the SD card driver from the topic below and after the download put the file sdcard.c in the project folder:
MMC/SD Card driver for CCS PIC C compiler

In this project a 8 GB SDHC card is used (SD card capacity is not important), the SD card is connected to the PIC18F4550 microcontroller via hardware SPI and the microcontroller runs with a frequency of 48MHz (8MHz with PLL). Circuit schematic is shown below.
Hardware Required:
  • PIC18F4550 microcontroller
  • SD Card
  • AMS1117 3.3V voltage regulator
  • 8MHz crystal oscillator
  • 2 x 22pF ceramic capacitor
  • 3 x 3.3K ohm resistor
  • 3 x 2.2K ohm resistor
  • 10K ohm resistor
  • 100nF ceramic capacitor
  • MAX232
  • 5 x 10uF polarized capacitor
  • Female COM port
  • 5V Power source
  • Breadboard
  • Jumper wires
Interfacing PIC18F4550 with SD card (MMC) circuit - Read and write sector data
As known the SD card voltage is 3.3V and the PIC18F4550 voltage is 5V, so AMS1117 3.3V is used to step down the 5V in order to supply our SD card. Also the outputs of the PIC18F4550 are logic 0 (0V) or logic 1 (5V) and connecting the microcontroller output pins (CS , SDO and SCK) directly to the SD card will damage it, here a voltage divider is used to get about 3V from the 5V which is enough for the SD card. The voltage divider consists of 3.3K and 2.2 K resistors. The MISO is connected directly to the SDI pin of the microcontroller because it is the data output pin of the SD card which is normally does not exceed 3.3V.
The MAX232 is used to interface our microcontroller with PC via serial port as what was done in this topic:
CCS C UART example for PIC18F4550 microcontroller
I used just one wire (RD2) to transmit data from the microcontroller to the PC (there is no need for the MCU to receive data so the receiving wire is not connected).
The chip select pin of the SD card is connected to pin RD3 (the SD card chip select pin is active low).
It is recommended to use voltage level translator (level shifter) instead of the voltage divider if it is available. The level translator translates the 5V to 3.3V for CS (SS), SDO (MOSI), and SCK and translates the 3.3V of MISO to 5V (PIC18F4550 SDI is an input with Schmitt buffer which needs 0.8VDD for high input). For example we can use chips like 74LVC04 (hex NOT) or 74LVC125A (quadruple buffer) to go from 5V to 3.3V and 74HCT245 to go from 3.3V to 5V.
For my hardware circuit I used both the level translator using 74LVC04 and74HCT245 and also the voltage divider using resistors as shown in the circuit schematic above, both circuits worked very well.

SD Card byte/sector read and write with PIC18F4550 microcontroller CCS C code:
This is our example C code, this code was tested with versions 5.051 and 5.070.
The microcontroller runs at 48MHz (8MHz + PLL).
The MMC/SD card driver file must be added as mentioned above. This example is tested in real hardware circuit as well as simulation using Proteus ISIS. After writing sectors or bytes you can check it with some softwares like: Hex Workshop, HxD Hex editor......
Before you test your hardware circuit make sure that your MMC/SD card does not contain any important files!
// Interfacing PIC18F4550 microcontroller with SD card (SDHC) CCS C code.
// This example shows read and write functions of a single byte and whole sector.
// This example does not use any file system (FAT16, FAT32 ...).
// MMC/SD card driver for CCS C compiler must be installed!
// Use at your own risk!
// http://ccspicc.blogspot.com/
// electronnote@gmail.com


// SD Card module connections
#define   SDCARD_SPI_HW
#define   SDCARD_PIN_SELECT  PIN_D3
// End SD card module connections
#define   SDCARD_WRITE                           // Enable write data to media functions

#include <18F4550.h>
#fuses NOMCLR HSPLL PLL2 CPUDIV1
#use delay(clock = 48MHz)
#use fast_io(B)
#use fast_io(D)
#use rs232(xmit = PIN_D2, rcv = PIN_D1, baud = 9600)
#include <sdcard.c>                              // SD card diver source code

int8 one_byte, sector_data[512];
int16 i;
void main(){
  set_tris_b(1);                                 // Configure RB0 pin as an input pin
  set_tris_d(0);                                 // Configure PORTD pins as outputs
  delay_ms(2000);
  printf("\n\r");                                // Start new line
  printf("*** Interfacing PIC18F4550 with SD card ***");
  printf("\n\r");                                // Start new line
  printf("Initializing the SD card...");
  printf("\n\r");                                // Start new line
  i = sdcard_init();                             // Initialize the SD card module
  if(i == 0){                                    // If the SD card has been successfully initialized
  // Read 1 single byte
  printf("Read byte with address 0:");
  printf("\n\r");                                // Start new line
  delay_ms(2000);
  if(sdcard_read_byte(0, &one_byte) == 0)
    printf("%X\n\r", one_byte);                  // Print the value of 'one_byte'
  
  // Read 1 sector
  printf("Read sector 0:");
  printf("\n\r");                                // Start new line
  delay_ms(2000);
  if(sdcard_read_sector(0, sector_data) == 0)
    for(i = 0; i < 512; i++)
      printf("%X", sector_data[i]);              // Print all sector bytes (512 bytes)
  
  // Write 1 sector
  printf("\n\r");                                // Start new line
  printf("Write sector 3:");
  printf("\n\r");                                // Start new line
  delay_ms(2000);
  if(sdcard_write_sector(3, sector_data) == 0)
    printf("Write sector OK");

  // Write 1 byte
  printf("\n\r");                                // Start new line
  printf("Write byte 0x700:");
  printf("\n\r");                                // Start new line
  delay_ms(2000);
  if(sdcard_write_byte(0x700, one_byte) == 0)
    printf("Write byte OK");
  
  // Read sector 3 (check the previous write sector and write byte functions)
  printf("\n\r");                                // Start new line
  printf("Read sector 3:");
  printf("\n\r");                                // Start new line
  delay_ms(2000);
  if(sdcard_read_sector(3, sector_data) == 0)
    for(i = 0; i < 512; i++)
      printf("%X", sector_data[i]);              // Print all sector bytes (512 bytes)
  
  // End
  printf("\n\r");                                // Start new line
  printf("*** END ***");
  }
  while(TRUE) ;                                  // Endless loop
}
After building circuit and code I get a result as shown in this video:


I opened the SD card with HxD Hex editor and I found sector 0 and sector 3 as shown below.
This is sector 0:
SD card read sector PIC18F4550 
And this is sector 3 where the underlined byte which has address 0x700 is caused by the write_byte function:
SD card write sector PIC18F4550
As we can see sector 0 and sector 3 data are the same except byte 0x700, so what I did is I read all sector 0 data (512 bytes) and then I wrote these data into sector 3 and finally I wrote byte 0x700 with the value of byte 0x00 which I read in the first function (read_byte).