►SPI & Time Functions


A. Transferring & Receiving single byte over SPI Bus

Transferring a single byte over the SPI bus is the most fundamental function. This function is used throughout this tutorial, and is how we send and receive information over the bus. The upsd3334D microcontroller has a full-duplex interface bus, so when you send a byte, you receive a byte as well. This function performs the necessary handshaking to send and receive a single byte.

BYTE SPI_Byte( BYTE ThisByte )
{
   while( !(SPISTAT&TISF) );
   while( (SPISTAT&BUSY) );
   SPITDR = ThisByte;
   while( !(SPISTAT&RISF) );
   while( (SPISTAT&BUSY) );
   return( SPIRDR );
}


B. SPI Chip Select

Another primitive function is enabling the CS line to the SD card. Since the SD card has a negative CS (the signal is low to select the card), we want to write a 0 when selecting the card, and a ‘1’ when we’re not selecting the card. The following two functions perform this process, but again – they are specific to the hardware design, and will be different than this (most probably).

void SPI_EnableCS()
{
   P4 &= 0x7F;       /* enable /CS line to select device  */
}

void SPI_DisableCS()
{
   P4 |= 0xF0;       // disable /CS line to select device  */
}


C. Setting frequency for SPI Clock

Setting the clock frequency for the SPI bus is accomplished by this function. Note, based on the definitions above, the maximum attainable speed for this microcontroller using the 22Mhz crystal is 5Mhz, so a call of SPI_Init( MHz10 ); will still only achieve 5Mhz.

void SPI_Init( enum SPI_FREQUENCIES ThisFrequency )
{
   SPI_DisableCS();     /* disable chip select if it's enabled...  */

   /* setup SPI control registers */
   SPICON0 = SPIEN | TE | RE;          /* Enables SPI, Tx and Rx  */
   SPICON1 = 0x00;                     // no interrupts
   switch( ThisFrequency )             // sets frequency...
   {
      case MHz10:
         SPICLKD = SPI_FREQUENCY_10MHz;
         break;
      case MHz5:
         SPICLKD = SPI_FREQUENCY_5MHz;
         break;
      case MHz1:
         SPICLKD = SPI_FREQUENCY_1MHz;
         break;
      case kHz400:
      default:
         SPICLKD = SPI_FREQUENCY_400KHz;
         break;
   }
}


D. Sending command to SD Card

Sending a single command, and reading the response from the card is the next logical building block. For a detailed description of the command structure, you must refer to the Product Specification Manual, titled "SanDisk Secure Digital Card, Product Manual, Version 1.9, Document No. 80-13-00169, December 2003". The function I use to do this is as follows:
#define CMD_GO_IDLE_STATE         0
#define CMD_SEND_OP_COND          1
#define CMD_SEND_CSD              9
#define CMD_SEND_CID              10
#define CMD_STOP_TRANSMISSION     12
#define CMD_SEND_STATUS           13
#define CMD_SET_BLOCKLEN          16
#define CMD_READ_SINGLE_BLOCK     17
#define CMD_READ_MULTIPLE_BLOCK   18
#define CMD_WRITE_SINGLE_BLOCK    24
#define CMD_WRITE_MULTIPLE_BLOCK  25
#define CMD_PROGRAM_CSD           27
#define CMD_SET_WRITE_PROT        28
#define CMD_CLR_WRITE_PROT        29
#define CMD_SEND_WRITE_PROT       30
#define CMD_TAG_SECTOR_START      32
#define CMD_TAG_SECTOR_END        33
#define CMD_UNTAG_SECTOR          34
#define CMD_TAG_ERASE_GROUP_START 35
#define CMD_TAG_ERASE_GROUP_END   36
#define CMD_UNTAG_ERASE_GROUP     37
#define CMD_ERASE                 38
#define CMD_LOCK_UNLOCK           42
#define CMD_APP_CMD               55
#define CMD_READ_OCR              58
#define CMD_CRC_ON_OFF            59
#define ACMD_SEND_OP_COND         41

typedef union
{
   BYTE Index[6];
   struct
      {
      BYTE Command;
      ULONG Argument;
      BYTE Cksum;
   } CA;
} CommandStructure;
   
typedef union
{
   BYTE b[4];
   ULONG ul;
} b_ul;

BYTE SD_Command( BYTE ThisCommand, ULONG ThisArgument )
{
   b_ul Temp;
   BYTE i;

   /* enable the device... */
   SPI_EnableCS();
   
   /* send buffer clocks to insure no operations are pending... */
   SPI_Byte( 0xFF );

   /* send command */
   SPI_Byte(0x40 | ThisCommand);
   
   /* send argument */
   Temp.ul = ThisArgument;
   for( i=0; i<4; i++ )
      SPI_Byte( Temp.b[ i ] );

   /* send CRC */
   SPI_Byte((ThisCommand == CMD_GO_IDLE_STATE)? 0x95:0xFF);

   /* send buffer clocks to insure card has finished all operations... */
   SPI_Byte( 0xFF );
   return( 0 );
}


E. Reading response from SD Card

Every command sent to the SD card invokes a response from the card. The size of the response, along with the content of the response, is dependent on the command sent. Here are two (2) functions that can be used to read response bytes from the SD card.

BYTE SD_GetR1()
{
   BYTE i, j;

   for( i=0; i<8; i++ )
   {                        /* response will be after 1-8 0xffs.. */
      j = SPI_Byte( 0xff );
      if(j != 0xff)         /* if it isn't 0xff, it is a response */
         return(j);
   }
   return(j);
}

WORD SD_GetR2()
{
   idata WORD R2;
   
   R2 = ((SD_GetR1())<< 8)&0xff00;
   R2 |= SPI_Byte( 0xff );
   return( R2 );
}


F. Delay and Time function

Finally, this tutorial requires a delay function and a time function – The function I use throughout is called Delay(), and the passed value is in milliseconds. You can achieve this in various ways, but the simplest way is to use an interrupt.

My delay function uses an interrupt that updates a system variable called “Ticker” every millisecond. To calculate delays, I wait for the ticker to count the number of milliseconds I want to wait. My Delay function looks like this:
void Delay( WORD MilSec ) 
{
   ULONG xdata DelayTickValue;

   /* calculate tick value from now until "MilSec" milliseconds later */
   DelayTickValue = Ticker + MilSec *( (float)(TICKS_PER_SECOND) / 1000.0 );

   /* wait until tick value reaches benchmark */
   while( Ticker < DelayTickValue );
}

The time function is a requirement of Chan’s library, and is used for time stamping files generated in the FAT. Since my hardware supports a real time clock, my time function looks like this:
DWORD get_fattime()
{
   RTC_CURRENT rtc;
   RTC_read( &rtc );
   return  ((DWORD)((WORD)(rtc.Year) + 20) << 25)
           | ((DWORD)rtc.Month << 21)
           | ((DWORD)rtc.Date << 16)
           | ((DWORD)rtc.Hours << 11)
           | ((DWORD)rtc.Minutes << 5)
           | ((DWORD)rtc.Seconds >
>
 1);
}



Tutorial Index
Interface to Chan’s Library of functions SD Card Initialization
Target development platform Reading and Writing a single sector
Setting up the SPI port during startup.A51 Working with diskio.c
Global type definitions and variables Pulling it all together
Basic SPI function Index Page

Downloads

Comments

Bobbyerilar
Thu Mar 28 2024, 08:08 am
pb58
Thu Mar 28 2024, 05:54 am
Clarazkafup
Thu Mar 28 2024, 02:24 am
Walterkic
Thu Mar 28 2024, 01:19 am
Davidusawn
Wed Mar 27 2024, 08:30 pm
Richardsop
Tue Mar 26 2024, 10:33 pm
Stevencog
Tue Mar 26 2024, 04:26 pm
Bernardwarge
Tue Mar 26 2024, 11:15 am