Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

COFILOS SD Card Driver

5.00/5 (5 votes)
4 Apr 2017CDDL2 min read 9.1K  
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.

It 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 (took the lion’s share of time to implement) and is shown here:

C++
/*!
* \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.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)