COFILOS SD Card Driver

SD Card Driver Development on COFILOS

Last year I completed a major milestone for COFILOS and the Perseus board. I completed a fully TDD written SD Card Driver.

You may check the following video: COFILOS SD Card

I was a big pain to write it, even though I had guide code from the internet and enough relevant information. The outcome is a very readable and organized code.

The initialization phase is very complex (and took the lion’s share of time to implement) is shown here:



/*!
* \brief Device Start (Stub)
* @param pstDriver_ : Pointer to Device structure
* @return 0 (SUCCESS), 1: Fail (Could not open SPI driver)
*/
BOOL f_DriverSD_Start(void *pstDriver_ )
{
type_stDRIVERSD *pst_SD;
INT8U v_error;
INT8U v_PhysDevID;
BOOL v_retval;

pst_SD = (type_stDRIVERSD *) pstDriver_;

if (pst_SD->stDriver.eState != DRIVER_CLOSE) return 1;
if (pst_SD->f_HAL_HookCardPresent == NULL) return 1;
if (pst_SD->f_HAL_HookCardPresent() == 0) return 255;

v_retval = 1; /* default is failure */

f_DriverSDHelper_InitSPI(pst_SD);

f_DriverCoFILOS_Open((PINT8) "SPI.Driver", 0xFF, (DRIVER_STRUCT *) &pst_SD->st_SPI);

Driver_Start((DRIVER_STRUCT *) &pst_SD->st_SPI);

/* Acquire Mutex, we need uninterrupted SPI access */
Driver_Control((DRIVER_STRUCT *) &pst_SD->st_SPI, CMD_SET_SPIMUTEX_ON, NULL);

f_DriverSDHelper_ResetSD(pst_SD);

/* set default value for Unknown Card Type */
/* Assuming initially that all is good */
v_PhysDevID = pst_SD->v_PhyVolumeID;
st_SDPhysical[v_PhysDevID].v_UnknownCard = eSDUn_None;
pst_SD->v_CardType = eSDCardUnknown;

/* set CS, clocked */
f_DriverSDHelper_SelectDevice(pst_SD);

v_error = f_DriverSDHelper_SendCMD0(pst_SD);
if (v_error == 0xFE)
{
  /* Unknown Card */
  /* TODO: Unknown Card */
  f_DriverSDHelper_UnknownDevice(pst_SD->v_PhyVolumeID, eSDUn_CMD0);
}
else
{
  v_error = f_DriverSDHelper_SendCMD8(pst_SD);

  if (v_error == 0) /* SDV2 Byte or Block */
  {
    v_error = f_DriverSDHelper_SendACMD41(pst_SD, 0x40000000);
    v_error = f_DriverSDHelper_SendCMD58(pst_SD);

    if (pst_SD->v_CardType == eSDCardSDv2Byte)
    {
      v_error = f_DriverSDHelper_SendCMD16(pst_SD);
      st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv2Byte;
    }
    else
    {
      st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv2Block;
    }
  }
  else if (v_error == 0xFE) /* SDv1 or MMCv3 or Unknown ? */
 {
   st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv1;
   v_error = f_DriverSDHelper_SendACMD41(pst_SD, 0x00000000);
   if (v_error != 0)
   {
     /* MMCv3 ? */
     v_error = f_DriverSDHelper_SendCMD1(pst_SD);
     if (v_error == 0)
    {
      st_SDPhysical[v_PhysDevID].v_CardType = eSDCardMMCv3;
    }
    else
    {
      f_DriverSDHelper_UnknownDevice(pst_SD->v_PhyVolumeID, eSDUn_CMD1);
    }
   }

   if (v_error == 0)
   {
     v_error = f_DriverSDHelper_SendCMD16(pst_SD);
   }
  }
  else
  {
    /* Unknown card */
    f_DriverSDHelper_UnknownDevice(pst_SD->v_PhyVolumeID, eSDUn_CMD8);
  }
}

/* if Card is SDv1 or better then acquire CSD/CID */
switch(st_SDPhysical[v_PhysDevID].v_CardType)
{
   case eSDCardSDv1:
   case eSDCardSDv2Block:
   case eSDCardSDv2Byte:
     f_DriverSDHelper_SendCMD9(pst_SD);
     f_DriverSDHelper_SendCMD10(pst_SD);
     break;
  default:;
}

/* Deassert CS, clocked */
f_DriverSDHelper_DeSelectDevice(pst_SD);

/* Release Mutex, finished exclusive SPI access */
Driver_Control((DRIVER_STRUCT *) &pst_SD->st_SPI, CMD_SET_SPIMUTEX_OFF, NULL);

if (st_SDPhysical[v_PhysDevID].v_CardType == eSDCardUnknown)
{
  v_retval = 1;
}
else
{
  st_SDPhysical[v_PhysDevID].v_DriverState = eSD_Started;
  v_retval = 0;
  pst_SD->st_SPI.v_ConfigReg = 0xA002; /* increase clock to 24MHz */
}
st_SDPhysical[v_PhysDevID].v_OpenCnt = 0;

return v_retval;

}

After completing the TDD phase on my host (PC) I needed to run my tests on the actual target (ColdFire) microcontroller.

In the next picture you may see the hardware setup along with my programmer. I used a micro-SD of 8GB from Kingston for test.

sd_board

I had already captured the sectors (and image) of the SD card and saved them on my hard-drive. I wanted to be able to compare what the microcontroller would read with the actual data stored.

After having my target run the code, I opened a PuTTY terminal to connect with my virtual COM on my target. Then through the CLI (Command Line Interpreter) I mounted the SD card. I issued the SDInfo command and the data provided the SD card “geometry”.

sd_cli_init

I then read sector zero (or MBR for FAT file system). I compared the data with the image capture to confirm reading of the same piece of information.

sd_cli_rdsec0000

 

The first FAT sector (0x2000) was also read and verified. I had to check it as I needed to run a FAT File System Handler to read the SD Card at a later stage (not very later, though).

sd_cli_rdsec2000

Then the write tests started. My write command in CLI supports a single 32-bit value write currently. This is sufficient at the moment to verify a sector write operation. So I used sector 1 which is unused to write the value 0x1234567

sd_cli_wrsec0001

Next a read on sector 1 is performed to check that the sector was written. And voila! Notice the Big-Endian write (0x78 at byte 0, instead of 0x12).

sd_cli_wrsec0001b

 

At the same time during debugging I used my Tektronix TDS3012B Digital Oscilloscope and a Python script to capture and decode the SD SPI data.

spi_capture

The script outputs also a VCD format file suitable for use with GTKWave. Thus I could see and analyse the data, both analog and digital.

spi_sd

Due to the TDD methods used the debugging phase on the target was very fast. With a few iterations and the help of the scope and the Python scripting tool the SD card driver worked well.