Sign In

Don't have a Xilinx account yet?

  • Choose to receive important news and product information
  • Gain access to special content
  • Personalize your web experience on Xilinx.com

Create Account

Username

Password

Forgot your password?
XClose Panel
Xilinx Home
Reply
Regular Visitor
chrisfisch
Posts: 28
Registered: ‎08-25-2011
0
Accepted Solution

XPS SPI Core and SD Card

Hi,

 

my objective is to write data to a sd card via Microblaze and its SPI Core.

 

Is it possible to handle the individual sd init ?

 

Are there any examples ? The only i found was the SP605 Example - CF Card.

 

 

Super Contributor
lockiegrogan
Posts: 217
Registered: ‎01-25-2008
0

Re: XPS SPI Core and SD Card

Hi,

 

Its really easy to communicate to a SD card in SPI module using the XPS-SPI Core.

 

On the SD Card the following pin mapping is used:

 

Pin 1 -> SPI SS

Pin 2 -> SPI MOSI

Pin 5 -> SPI CLK

Pin 7 -> SPI MISO

 

There is a difference between SD cards using SD V1.0, SD V2.0 and the SHDC specifications.  You should get a copy of the spec from google.  It is also important that if you want to have the SD card used in SPI mode then you'll need to keep pin 1 low during POR (from memory).  Otherwise the card will operate in SD-1bit mode.

 

As for the code to init the card and read/write sectors... you should use google.  There are lots of projects on the web for this.

 

Cheers

Lachlan.

 

 

Regular Visitor
chrisfisch
Posts: 28
Registered: ‎08-25-2011
0

Re: XPS SPI Core and SD Card

Hi lockiegrogan,

 

thanks for reply !

 

I read the the specification v. 3.0 and im familiar with the sdc init routine. I also found a initialization flow like this :

 

http://elm-chan.org/docs/mmc/sdinit.png

 

What i mainly wanted to know is how the spi-core muste be set/ intialized to communicate with an sd-card.

 

The datasheet from the LogiCore IP XPS SPI v2.02a only gives information about the registers but not about the functions

 

included via xspi.h and xspil.h.

 

Its essential that the SPI Core and furthermore the xspi.h functions supports multibyte transfer because one SD "Command" exists of 6 Bytes.

 

Greets

Christian

Regular Visitor
chrisfisch
Posts: 28
Registered: ‎08-25-2011
0

Re: XPS SPI Core and SD Card

[ Edited ]

In other words :)

 

I would init the SPI Core like this (Only first CMD Send):

 

This Code is from the example of the SP605 Board -> Write/Read to a STM Flash

 

#include "xparameters.h"	/* EDK generated parameters */
#include "xspi.h"		/* SPI device driver */
#include "mb_interface.h"	/* Microblaze interface */

/*
 * The following constants map to the XPAR parameters created in the
 * xparameters.h file. They are defined here such that a user can easily
 * change all the needed parameters in one place.
 */
#define SPI_DEVICE_ID			XPAR_SPI_0_DEVICE_ID

/*
 * The following constant defines the slave select signal that is used to
 * to select the Flash device on the SPI bus, this signal is typically
 * connected to the chip select of the device.
 */
#define STM_SPI_SELECT 0x01 // 0x01 for Port 1 CS ? 
/*
 * The instances to support the device drivers are global such that they
 * are initialized to zero each time the program runs. They could be local
 * but should at least be static so they are zeroed.
 */
static XSpi Spi;

	/*
	 * Initialize the SPI driver so that it's ready to use,
	 * specify the device ID that is generated in xparameters.h.
	 */
	Status = XSpi_Initialize(&Spi, SPI_DEVICE_ID);
	if(Status != XST_SUCCESS) {\
   		xil_printf("Failure INIT\r\n");
		return XST_FAILURE;
	}
	/*
	 * Set the SPI device as a master and in manual slave select mode such
	 * that the slave select signal does not toggle for every byte of a
	 * transfer, this must be done before the slave select is set.
	 */
	Status = XSpi_SetOptions(&Spi, XSP_MASTER_OPTION |
						XSP_MANUAL_SSELECT_OPTION);
	if(Status != XST_SUCCESS) {
   		xil_printf("Failure Options\r\n");
		return XST_FAILURE;
	}
	/*
	 * Select the device on the SPI bus, so that it can be
	 * read and written using the SPI bus.
	 */
	Status = XSpi_SetSlaveSelect(&Spi, STM_SPI_SELECT);
	if(Status != XST_SUCCESS) {
   		xil_printf("Failure Slave Select\r\n");
		return XST_FAILURE;
	}
	/*
	 * Start the SPI driver so that the device are enabled.
	 */
	XSpi_Start(&Spi);

// ****************** SD INIT ***********************
	/*
	 * Prepare the WriteBuffer.
	 */
WriteBuffer[BYTE1] = CMD 0;
WriteBuffer[BYTE2] = Argument Byte 1/4;
WriteBuffer[BYTE3] = Argument Byte 2/4;
WriteBuffer[BYTE4] = Argument Byte 3/4;
WriteBuffer[BYTE5] = Argument Byte 4/4;
WriteBuffer[BYTE6] = CRC BYTE


/*
	 * Initiate the Transfer.
	 */
	TransferInProgress = TRUE;
	Status = XSpi_Transfer(SpiPtr, WriteBuffer, NULL,6);
 	if(Status != XST_SUCCESS) {
		xil_printf("Error in transfer\r\n");
		return XST_FAILURE;
	}

 

Super Contributor
lockiegrogan
Posts: 217
Registered: ‎01-25-2008
0

Re: XPS SPI Core and SD Card

[ Edited ]
Hi,
I used the xps spi core in the first version of a product supporting sdhc cards. I made a local copy of the ip and added the ability to support two clocks, one at 400khz and one at 25mhz as per spec. I used a spare bit in the register to control the clock speed cos as u know you'll need to start at 400k, query the card and change freq later on.

As for the code, if you manually control the slave select pin then you can do multibyte. What I have never done is used the xilinx drivers, I have always written directly to the registers using XIo_... Commands. It took a few hrs of extra coding, but well worth it. If you want any speed when reading and writing blocks from the card then you can consume another bit in the control register and rather than readin 8bits, read 32bits at a time. This way you make the most of the bus transfer to your micro blaze. In my tests if you don't have the software overhead of setting and clearing the slave select pin on each transfer and use full bus width transfers from the card you will be able to stream video from the card....
Cheers

Lachlan
Regular Visitor
chrisfisch
Posts: 28
Registered: ‎08-25-2011
0

Re: XPS SPI Core and SD Card

[ Edited ]

Hi lockiegrogan,

 

were do you find those Xlo.. commands ? As far as i know, the 400khz initialization speed is only for mmc cards and not for sd cards.

 

Anyway, are there any code examples you found ? I dont feel certain with those initialization:

 

several commands for sd init

  -> one command : 6 bytes

      -> send one byte per SPI then the others and so on...

      -> send one byte :

static void spi_write_byte(XSpi *SpiPtr,uint8_t byte)
{
	XSpi_Transfer(SpiPtr, byte, NULL,1);

}

 ?

 

greets

Regular Visitor
chrisfisch
Posts: 28
Registered: ‎08-25-2011
0

Re: XPS SPI Core and SD Card

Perhaps i can send with selecting manual slave select mode the whole cmd (and receive the response) like this? :

 

/*
	 * Prepare the WriteBuffer.
	 */
	WriteBuffer[BYTE1] = CMD;
	WriteBuffer[BYTE2] = Argument[1]; //Argument[31..24]
	WriteBuffer[BYTE3] = Argument[2]; //Argument[23..16]
	WriteBuffer[BYTE4] = Argument[3]; //Argument[15..8]
        WriteBuffer[BYTE5] = Argument[4];   //Argument[7..0] 
        WriteBuffer[BYTE6] = CRC;

XSpi_Transfer(SpiPtr, WriteBuffer, Response,6);


 The Bytecount is equal to the written bytes?


Greets

Super Contributor
lockiegrogan
Posts: 217
Registered: ‎01-25-2008
0

Re: XPS SPI Core and SD Card

Hi,

 

Here is how I would write one byte out the SPI using XIo_Out32...

 

u8SDSPI_SendByte(Xuint8 u8OutGoing)
{
    Xuint32 u32Return;
    Xuint32 u32Read;
    
    //Set the SSO bit (force chipselect)
    u32_SDLowLevel_ControlRegister |= C_SDCON_CONTROL_REG_SLAVE_SELECT_MASK;
    XIo_Out32((C_SDCON_CONTROLLER_BASE_ADDRESS) +SDCON_SDCON_SLAVE_WRITE_CONTROL, u32_SDLowLevel_ControlRegister);

        //load our byte
	//This will also force a start of the write and read
	XIo_Out32((C_SDCON_CONTROLLER_BASE_ADDRESS) +SDCON_SDCON_SLAVE_WRITE_DATA, (Xuint32)u8OutGoing);

	//wait until done
	u32Read = XIo_In32((C_SDCON_CONTROLLER_BASE_ADDRESS) +SDCON_SDCON_SLAVE_READ_STATUS);
	while((u32Read & C_SDCON_STATUS_REG_BUSY_MASK) == C_SDCON_STATUS_REG_BUSY_MASK)
	{
		u32Read = XIo_In32((C_SDCON_CONTROLLER_BASE_ADDRESS) +SDCON_SDCON_SLAVE_READ_STATUS);
	}

    
      //read the returned data
     u32Return = XIo_In32((C_SDCON_CONTROLLER_BASE_ADDRESS) +SDCON_SDCON_SLAVE_READ_DATA);
 
    //write to the control register and disable the chip select
    u32_SDLowLevel_ControlRegister &= C_SDCON_CONTROL_REG_SLAVE_SELECT_MASK_CLEAR;       XIo_Out32((C_SDCON_CONTROLLER_BASE_ADDRESS) +SDCON_SDCON_SLAVE_WRITE_CONTROL, u32_SDLowLevel_ControlRegister);
 
    return (Xuint8)u32Return;

}

 

You can just as easily send that byte without using the chip select.

 

And when you want to send a command, call the IssueCommand function:

 

void vSDSPI_IssueCommand(Xuint8 u8Command, Xuint16 u16ParameterX, Xuint16 u16ParameterY)
{
    Xuint8 u8Return;
    Xuint8 u8CRC;
    
    //send 8 clocks
    u8Return = u8SDCON_SPI_SendByte(0xFF);
    
    //send the command
    u8Return = u8SDSPI_SendByte(0x40 | u8Command);
    
    //send the MSB of ParamX
    u8Return = u8SDSPI_SendByte((Xuint8) (u16ParameterX >> 8));
    //send the LSB of ParamX
    u8Return = u8SDSPI_SendByte((Xuint8) (u16ParameterX));
    //send the MSB of ParamY
    u8Return = u8SDSPI_SendByte((Xuint8) (u16ParameterY >> 8));
    //send the LSB of ParamY
    u8Return = u8SDSPI_SendByte((Xuint8) (u16ParameterY));

	#if C_LOCALDEF__SDCON__CRC_SOFTWARE == 1	
		u8CRC = 0x00;
		u8CRC = u8SDNPI_CRC__7Bit_Add(u8CRC, 0x40 | u8Command);
		u8CRC = u8SDNPI_CRC__7Bit_Add(u8CRC, (Xuint8) (u16ParameterX >> 8));
		u8CRC = u8SDNPI_CRC__7Bit_Add(u8CRC, (Xuint8) (u16ParameterX));
		u8CRC = u8SDNPI_CRC__7Bit_Add(u8CRC, (Xuint8) (u16ParameterY >> 8));
		u8CRC = u8SDNPI_CRC__7Bit_Add(u8CRC, (Xuint8) (u16ParameterY));
		u8CRC = u8SDNPI_CRC__7Bit_Finalise(u8CRC);
		u8CRC <<= 1;
		//always add the 0x01 as this is the stop bit.
		u8CRC |= 0x01;
	#endif //C_LOCALDEF__SDCON__CRC_SOFTWARE ==1

	#if C_LOCALDEF__SDCON__CRC_DISABLE == 1
    //send the checksum
    switch(u8Command)
    {
    	case C_SDCARDCOMMAND__SEND_IF_COND__CMD8:
	    	//when sending the IF cond (CMD8) iwe must always send
	    	//the correct CRC
	    	u8Return = u8SDSPI_SendByte(0x87);
    		break;
    	
    	case C_SDCARDCOMMAND__GO_IDLE__CMD0:
	    	//CMD0 = 0x95 CRC
			u8Return = u8SDSPI_SendByte(0x95);
    		break;
    	
    	default:
	    	//for all other commands, CRC is ignored in SPI mode.
			u8Return = u8SDSPI_SendByte(0xFF);
    		break;
    }
    #elif C_LOCALDEF__SDCON__CRC_SOFTWARE == 1
    	u8Return = u8SDSPI_SendByte(u8CRC);
    #else
    	#error
    #endif
    //send some more clocks to eat the empty command slot.
    //u8Return = u8SDSPI_SendByte(0xFF);

}

 

Does that help a bit?

Lachlan.

 

 

Regular Visitor
chrisfisch
Posts: 28
Registered: ‎08-25-2011
0

Re: XPS SPI Core and SD Card

[ Edited ]

 

 

Hi Lachlan,

 

yes this helps me alot.

 

Am i right that those Xio_.. Commands are lower lvl Drivers from the Xilinx Library ?

 

Or were do you find them ?

 

Ive got a Problem with the Bustimings e.g with the 8x Clock delay after a CS High -> Low switch.

 

So im very interested in your solution.

 

My works like this :

 

int sd_send_cmd(uint8_t cmd, uint32_t arg,XSpi *SpiPtr)
{
	uint8_t n;
	int Dummy;
	/*
	* Prepare the WriteBuffer.
	*/
		WriteBuffer[BYTE1] = (0x40 | cmd);		        // Command
		WriteBuffer[BYTE2] = ( (uint8_t)(arg >> 24) );       // Argument[31..24]
		WriteBuffer[BYTE3] = ( (uint8_t)(arg >> 16) ); 	// Argument[23..16]
		WriteBuffer[BYTE4] = ( (uint8_t)(arg >> 8) ) ; 	// Argument[15..8]
	    WriteBuffer[BYTE5] = ( (uint8_t)arg ); 			// Argument[7..0]
	    n = 0x01;								// Dummy CRC + Stop
	    	if (cmd == CMD0) n = 0x95;				// Valid CRC for CMD0(0)
	    	if (cmd == CMD8) n = 0x87;				// Valid CRC for CMD8(0x1AA)
	    WriteBuffer[BYTE6] = n;

	XSpi_Transfer(SpiPtr, WriteBuffer, ReadBuffer,6);
	return Dummy;
}

 But yours seems more elegant :)

 

 

Perhaps you can send me your Makro definition ? Easier to understand ;)

 

Super Contributor
lockiegrogan
Posts: 217
Registered: ‎01-25-2008
0

Re: XPS SPI Core and SD Card

Hi,

Check XIo.h This file has the prototypes for the XIo_In / Out calls.

Again with your code, you are still using the xilinx drivers. It will be very hard to ensure accurate and efficient timing without lower level control over your hardware. You really need explicit control over the chip select line.

Cheers.
Lachlan