Talk to an SD card via SPI?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Talk to an SD card via SPI?

2,508 Views
puddletowntom
Contributor III

I am trying to communicate with an SD card using an LPC5411 dev board based on developing my own SD card custom driver. I m basing the development on the following tutorial Porting FatFs file system to KL26 SPI SD card code  . I have the SD card connected externally to the development board. I m having a problem initialising the SD card. I have made sure of the following. I send 80 pulses of a clock signal with Chip Select high and MOSI high. After this i m setting the CS low and sending the byte values 0x40, 0x00, 0x00, 0x00, 0x00, 0x95 for CMD0. I m not receiving any byte values back on the MISO line. I was checking the scope and it appears that the CS, MOSI and CLK are working but the MISO isnt. Is there a timing issue. I have attached the screen grab of the the SPI lines. 

pastedImage_4.jpg

The code I m using the generate these pulses is as follows

uint8_t MMCInit(){
spi_master_config_t userConfig = {0};
spi_master_config_t masterConfig = {0};

uint8_t i = 0,k = 0,tmp = 0;
uint16_t cnt=0;
uint8_t buff[512];

SPI_MasterGetDefaultConfig(&userConfig);
srcFreq = EXAMPLE_SPI_MASTER_CLK_FREQ_LOW;//EXAMPLE_SPI_MASTER_CLK_FREQ;
masterConfig.sselNum = (spi_ssel_t)EXAMPLE_SPI_SSEL;
SPI_MasterInit(EXAMPLE_SPI_MASTER, &userConfig, srcFreq);

spi_transfer_t xfer = {0};
status_t xferStatus = kStatus_Success;

uint8_t ucTemp;
for (i = 0U; i < BUFFER_SIZE; i++)
{
srcBuff[i] = 0xFF;
}

xfer.txData = srcBuff;
xfer.rxData = destBuff;
xfer.dataSize = sizeof(destBuff);

// SPI_CS_Low();
xferStatus = SPI_MasterTransferBlocking(EXAMPLE_SPI_MASTER, &xfer);


//40 00 00 00 00 95 for CMD0
//48 00 00 01 AA 0F for CMD8
//7A 00 00 00 00 75

startCMD[0] = 0x40;
startCMD[1] = 0x00;
startCMD[2] = 0x00;
startCMD[3] = 0x00;
startCMD[4] = 0x00;
startCMD[5] = 0x95;
// xfer.configFlags = kSPI_FrameAssert;

xfer.txData = startCMD;
xfer.rxData = endBuff;
xfer.dataSize = 7;//sizeof(endBuff); 6 bytes and one more
SPI_CS_Low();
xferStatus = SPI_MasterTransferBlocking(EXAMPLE_SPI_MASTER, &xfer);
SPI_CS_High();

}

uint8_t MMCInit(){ spi_master_config_t userConfig = {0}; spi_master_config_t masterConfig = {0}; uint8_t i = 0,k = 0,tmp = 0; uint16_t cnt=0; uint8_t buff[512]; SPI_MasterGetDefaultConfig(&userConfig); srcFreq = EXAMPLE_SPI_MASTER_CLK_FREQ_LOW;//EXAMPLE_SPI_MASTER_CLK_FREQ; masterConfig.sselNum = (spi_ssel_t)EXAMPLE_SPI_SSEL; SPI_MasterInit(EXAMPLE_SPI_MASTER, &userConfig, srcFreq); spi_transfer_t xfer = {0}; status_t xferStatus = kStatus_Success; uint8_t ucTemp; for (i = 0U; i < BUFFER_SIZE; i++) { srcBuff[i] = 0xFF; } xfer.txData = srcBuff; xfer.rxData = destBuff; xfer.dataSize = sizeof(destBuff); // SPI_CS_Low(); xferStatus = SPI_MasterTransferBlocking(EXAMPLE_SPI_MASTER, &xfer); //40 00 00 00 00 95 for CMD0 //48 00 00 01 AA 0F for CMD8 //7A 00 00 00 00 75 startCMD[0] = 0x40; startCMD[1] = 0x00; startCMD[2] = 0x00; startCMD[3] = 0x00; startCMD[4] = 0x00; startCMD[5] = 0x95; // xfer.configFlags = kSPI_FrameAssert; xfer.txData = startCMD; xfer.rxData = endBuff; xfer.dataSize = 7;//sizeof(endBuff); 6 bytes and one more SPI_CS_Low(); xferStatus = SPI_MasterTransferBlocking(EXAMPLE_SPI_MASTER, &xfer); SPI_CS_High(); }

Labels (1)
Tags (4)
0 Kudos
4 Replies

1,569 Views
puddletowntom
Contributor III

Hi Ian,

Sorry for the late response. I was busy with other things and had parked this work aside. So slowing down the clock speed for the initialisation worked. This was something I forgot to do before. I was able to get the correct responses back for the SD commands. I m able to read and write to the SD card also so I can use it as a basic data logger, however still working on integration with the FATFS.

Thank you for the help.

Ronan

0 Kudos

1,569 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Ronan,

   Please send me your test project, I will help you to check it on my side.

  Please also tell me which pins you are using in the LPC5411X.


Have a great day,
Kerry

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

1,569 Views
puddletowntom
Contributor III

Hi Kerry,

Last Friday I made some progress. I started to see something on the MISO line. For the reset command, eg  MMCWriteCmd(CMD0,0x00,0x95);   it seemed like I was receiving the expected value back. eg the 1 value (00000001). However, I think I was receiving a decimal value of 9 for the CMD8 command. eg MMCWriteCmd( CMD8,0x1AA,0x87 ); I dont really know why this is. I have attached a screen grab to show that there is in fact something on the MISO line. However I m not sure if my clock sequence is correct. If I send a byte using the SPI_MasterTransferBlocking() command for each individual byte then I wont have a continuous clock. For the 72 clock pulses does the clock need to be continuous or can it be broken up? 

Chip Select(Green) ==> (GPIO pin) port0_pin10

Clock(Purple) ==> port0_pin19

MOSI(Yellow) ==> port0_pin20

MISO(Blue) ==> port0_pin18,

I m not certain that the way I m using the Send_Byte() function is correct. The code I last worked on is attached below. 

pastedImage_1.jpg

/*
* Copyright (c) 2016, Freescale Semiconductor, Inc.
* Copyright 2016-2017 NXP
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* o Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "board.h"
#include "fsl_debug_console.h"
#include "fsl_spi.h"

#include "pin_mux.h"
#include <stdbool.h>
#include "ff.h"
/*******************************************************************************
* Definitions
******************************************************************************/

#define EXAMPLE_SPI_MASTER SPI5
#define EXAMPLE_SPI_MASTER_IRQ FLEXCOMM5_IRQn
#define EXAMPLE_SPI_MASTER_CLK_SRC kCLOCK_Flexcomm5
//#define EXAMPLE_SPI_MASTER_CLK_FREQ CLOCK_GetFreq(kCLOCK_Flexcomm5)

#define EXAMPLE_SPI_MASTER_CLK_FREQ_LOW 20000000//20000000 //SSP0LowSpeed()
#define EXAMPLE_SPI_MASTER_CLK_FREQ_HIGH //4000000 //SSP0LowSpeed()

#define EXAMPLE_SPI_SLAVE SPI5
#define EXAMPLE_SPI_SLAVE_IRQ FLEXCOMM5_IRQn
#define EXAMPLE_SPI_SSEL 2

#define CORE_CLK_FREQ CLOCK_GetFreq(kCLOCK_CoreSysClk)

#define CMD0 0x40 //Use SPI interface
#define CMD8 0x48 //Get SD card version
#define CMD9 0x00 //Read CSD, get other information,
#define CMD12 0x00
#define CMD16 0x00 //Set SD card block size to 512Byte.
#define CMD17 0x00 //For reading the SD card send to this
#define CMD18 0x00
#define CMD24 0x00 //For writing to the SD card send to this
#define CMD41 0x69 //Activate SD card
#define CMD55 0x77 //Activate SD card
#define CMD58 0x00
#define CMD59 0x00 //Read OCR data

#define SD_TYPE_V2HC 0x04
#define SD_TYPE_V2 0x02

#define regVal1 0x00

#define BUFFER_SIZE (10)
/*******************************************************************************
* Prototypes
******************************************************************************/
uint8_t SPI5_TransmitReceive8bit(uint8_t faidjf);
uint8_t spi_oled_exchange(uint8_t *in, uint8_t *out, uint32_t size);
void SPI_CS_High(void);

uint8_t MMCWriteCmd(uint8_t cmd, uint32_t arg, uint8_t crc );
uint32_t srcFreq = 0;
/*******************************************************************************
* Variables
******************************************************************************/

//SPI variables
uint8_t SD_Type=0;
spi_master_config_t userConfig = {0};
spi_master_config_t masterConfig = {0};
spi_transfer_t xfer = {0};
status_t xferStatus = kStatus_Success;

static uint8_t srcBuff[BUFFER_SIZE]; //1
static uint8_t destBuff[BUFFER_SIZE]; //2
static uint8_t startCMD[1]; //1
static uint8_t endBuff[1]; //1

static uint8_t tempBuff1[1]; //1
static uint8_t tempBuff2[1]; //1
//static spi_slave_handle_t slaveHandle;
static volatile bool slaveFinished = false;

int testVal = 0;
//static uint8_t txData[16];
//static uint8_t rxData[16];

/*******************************************************************************
* Code
******************************************************************************/
static void slaveCallback(SPI_Type *base, spi_slave_handle_t *handle, status_t status, void *userData)
{
slaveFinished = true;
}

static void GPIO_Configuration(void)
{
CLOCK_EnableClock(kCLOCK_Gpio0);
CLOCK_EnableClock(kCLOCK_Gpio1);

const gpio_pin_config_t port0_pin2_gpio_config = {kGPIO_DigitalOutput,1,};
GPIO_PinInit(GPIO, PORT0_IDX, PIN2_IDX, &port0_pin2_gpio_config);

/*3.3V Enable*/
const gpio_pin_config_t port0_pin9_gpio_config = {kGPIO_DigitalOutput,1,};

GPIO_PinInit(GPIO, PORT0_IDX, PIN9_IDX, &port0_pin9_gpio_config);
SPI_CS_High();
//SPI_CS_Low();
}

void SPI_CS_Low(){
//Enable CS for pressure sensor.
const gpio_pin_config_t port0_pin10_gpio_config = {kGPIO_DigitalOutput,0,};
GPIO_PinInit(GPIO, PORT0_IDX, PIN10_IDX, &port0_pin10_gpio_config);
}

void SPI_CS_High(){
//Enable CS for pressure sensor.
const gpio_pin_config_t port0_pin10_gpio_config = {kGPIO_DigitalOutput,1,};
GPIO_PinInit(GPIO, PORT0_IDX, PIN10_IDX, &port0_pin10_gpio_config);
}
void timeDelay_us(uint32_t delay)
{
delay++;
/* Wait desired time */
while (delay > 0)
{
if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)
{
delay--;
}
}
}

void timeDelay(uint32_t miliseconds)
{
timeDelay_us(miliseconds * 1000);
}

void installTimeDelay()
{
/* Disable SysTick timer */
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
/* Initialize Reload value to 1us */
SysTick->LOAD = CORE_CLK_FREQ / 1000000;
/* Set clock source to processor clock */
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
/* Enable SysTick timer */
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
}

uint8_t Send_Byte(uint8_t byte_val){

uint8_t ucTemp;

tempBuff1[0] = byte_val;
xfer.txData = tempBuff1;
xfer.rxData = destBuff;
xfer.dataSize = 1;

// SPI_CS_Low();
xferStatus = SPI_MasterTransferBlocking(EXAMPLE_SPI_MASTER, &xfer);
// SPI_CS_High();

ucTemp = destBuff[0];
return ucTemp;

}

//uint8_t Send_Byte (uint8_t ucdata)
//{
// uint8_t ucTemp;
// while((SPI0_S & SPI_S_SPTEF_MASK) != SPI_S_SPTEF_MASK);
// SPI0_DL = ucdata;
//
// while((SPI0_S & SPI_S_SPRF_MASK) != SPI_S_SPRF_MASK);
// ucTemp = SPI0_DL;
// ucTemp = ucTemp;
// return ucTemp;
//}


//uint8_t Get_Byte(void)
//{
// uint8_t ucTemp;
//
// srcBuff[0] = regVal1;
// xfer.txData = srcBuff;
// xfer.rxData = destBuff;
// xfer.dataSize = sizeof(destBuff);
//
// xferStatus = SPI_MasterTransferBlocking(EXAMPLE_SPI_MASTER, &xfer);
//
// ucTemp = destBuff[0];
// return ucTemp;
//}

uint8_t MMCInit(){

uint8_t i = 0,k = 0,tmp = 0;
uint16_t cnt=0;
uint8_t buff[512];
uint8_t ucTemp;
timeDelay_us(5000);

for (i=0; i<0x0F; i++)
{
Send_Byte(0xFF); // send 72 clocks
}

do
{
tmp = MMCWriteCmd(CMD0,0x00,0x95); // CMD0
k++;
}while ((tmp != 1) && (k < 200));

if(k == 0)
{
MMCCS(1); //cs pullup, disconnect
Send_Byte(0xFF);
printf("\n SD reset fail");
return 1;//
}
//get SD card version
tmp = MMCWriteCmd( CMD8,0x1AA,0x87 );
printf( "SD_CMD8 return %d........\n\n", tmp );


// if(tmp == 1)// 2.0 card
// {
// cnt=0xffff;
//
// do
// {
// MMCWriteCmd( CMD55, 0, 0xff );
// tmp = MMCWriteCmd( CMD41,0x40000000, 0xff);//CMD41
// cnt--;
// } while ((tmp) && (cnt));
// //Get OCR information
// tmp = MMCWriteCmd(CMD58, 0, 0 );
// if ( tmp != 0x00 )
// {
// MMCCS(1); //cs pullup, SD card disconnect
// printf( "\nSD_CMD58 return %d....\n", tmp );
// return 1;//
// }
//
// for ( i = 0; i < 4; i++ )
// {
// buff[ i ] = Get_Byte();
// }
// MMCCS(1);
// printf( "OCR return: %x %x %x %x....\n\n", buff[0],buff[1],buff[2],buff[3] );
//
// if ( buff[0] & 0x40 )
// {
// SD_Type = SD_TYPE_V2HC;
// printf( "card is V2.0 SDHC.....\n\n" );
// }
// else {
// SD_Type = SD_TYPE_V2;
// printf( "card is V2.0.....\n\n" );
// }
// while(MMCWriteCmd(CMD16,512,0xff)!=0);
// MMCWriteCmd(CMD9,0,0xff);
// }
// MMCCS(1);
// return 0;
}


uint8_t sd_WaitRead( void )
{
uint32_t cnt = 0x00fffff;
uint8_t sta;
do
{
sta = Send_Byte( 0xff );;
if ( sta == 0xff ) //
{
return 0;
}
cnt--;
} while ( cnt );

return 1;

}
__inline static void sd_Disable_Select( void )
{
MMCCS(1);
Send_Byte( 0xff ); //
}

__inline static uint8_t sd_Enable_Select( void )
{
MMCCS(0);
if ( sd_WaitRead() == 0 ) // ??SD/MMC??
{
return 0;
}
sd_Disable_Select();

return 1;
}

uint8_t MMCWriteCmd(uint8_t cmd, uint32_t arg, uint8_t crc )
{
uint16_t cnt=512;
uint8_t sta;

MMCCS(1); //SEL high
Send_Byte( 0xff ); //delay
MMCCS(0); //SEL low

if (sd_Enable_Select())
{
return 0xff;
}
//40 00 00 00 00 95 for CMD0
//48 00 00 01 AA 0F for CMD8
//7A 00 00 00 00 75 for CMD58

startCMD[0] = cmd | 0x40 ;
startCMD[1] = arg>>24;
startCMD[2] = arg>>16;
startCMD[3] = arg>>8;
startCMD[4] = arg;
startCMD[5] = crc;


xfer.txData = startCMD;
xfer.rxData = endBuff;
xfer.dataSize = 7;//sizeof(endBuff); 6 bytes and one more
SPI_MasterTransferBlocking(EXAMPLE_SPI_MASTER, &xfer);

// Send_Byte( cmd | 0x40 );
// Send_Byte( (uint8_t)(arg>>24) );
// Send_Byte( (uint8_t)(arg>>16) );
// Send_Byte( (uint8_t)(arg>>8) );
// Send_Byte( (uint8_t)(arg) );
// Send_Byte( crc ); // CRC

do
{
sta = Send_Byte(0xff);
cnt--;
} while ( (cnt)&&(sta==0xff) );

return sta;
}

void MMCCS(uint8_t cs)
{
if (cs == 1)
{
SPI_CS_High(); // SS=1
}
else
{
SPI_CS_Low(); // SS=0
}
}

int main(void)
{
uint16_t i,j;
// FATFS fs;
FRESULT fr;
FIL fil;
UINT bw;
char file_name1[12]="Test.csv";
char file_name2[12]="Test.txt";
// System_init();
// spiInit(SPI0_BASE_PTR , Master);

CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
CLOCK_AttachClk(kFRO12M_to_FLEXCOMM5);
RESET_PeripheralReset(kFC5_RST_SHIFT_RSTn);
BOARD_InitPins();
BOARD_BootClockFROHF48M();
BOARD_InitDebugConsole();
GPIO_Configuration();
installTimeDelay();

SPI_MasterGetDefaultConfig(&userConfig);
srcFreq = EXAMPLE_SPI_MASTER_CLK_FREQ_LOW;//EXAMPLE_SPI_MASTER_CLK_FREQ;
//masterConfig.sselNum = (spi_ssel_t)EXAMPLE_SPI_SSEL;
SPI_MasterInit(EXAMPLE_SPI_MASTER, &userConfig, srcFreq);

while(1){
MMCInit();
//MMCInit();
for(uint32_t i = 100000; i != 0; i--);
//timeDelay(3000);
}


// fr= f_mount(&fs,file_name1,0);
// if(fr)
// {
// printf("\nError mounting file system\r\n");
// for(;;){}
// }
// // fr = f_open(&fil, file_name1, FA_WRITE | FA_OPEN_ALWAYS);//create csv file
// if(fr)
// {
// printf("\nError opening text file\r\n");
// for(;;){}
// }
// fr = f_write(&fil, "Test1 ,Test2 ,Test3 ,Test4 \r\n", 29, &bw); //write data to the excel file
// if(fr)
// {
// printf("\nError write text file\r\n");
// for(;;){}
// }
// fr = f_close(&fil);
// if(fr)
// {
// printf("\nError close text file\r\n");
// for(;;){}
// }
// fr= f_mount(&fs,file_name2,0);
// if(fr)
// {
// printf("\nError mounting file system\r\n");
// for(;;){}
// }
// fr = f_open(&fil, file_name2, FA_WRITE | FA_OPEN_ALWAYS);//create txt file
// if(fr)
// {
// printf("\nError opening text file\r\n");
// for(;;){}
// }
// fr = f_write(&fil, "Test1 ,Test2 ,Test3 ,Test4 \r\n", 29, &bw); //write data to the txt file
// if(fr)
// {
// printf("\nError write text file\r\n");
// for(;;){}
// }
// fr = f_close(&fil);
// if(fr)
// {
// printf("\nError close text file\r\n");
// for(;;){}
// }
// while(1)
// {
// for(i=0;i<10;i++) for(j=0;j<65535;j++);
// printf("\ntest_sd\n");//
// }
}

0 Kudos

1,569 Views
ianbenton
Senior Contributor I

A few thoughts having written a SD card interface in assembler on an LPC1517:

I can't exactly tell from the oscilloscope trace how fast the 80 pulses are, but they should be no quicker than 400kHz. I think you might be a bit close to the limit.

The pulses don't have to be evenly spaced. I send five 16-bit words of 0xFFFF via the SPI interface, and I use 200kHz until it responds to CMD0 then speed it up.

A reply of 9 from the card = CRC check fault.

I'm just wondering if MMCWriteCmd( CMD8,0x1AA,0x87 ) is correct. Does your MMCWriteCmd function write 8-bit values? If so, you've got a 9-bit value in there, which is probably getting truncated to 8 bits. 

You've also got //48 00 00 01 AA 0F for CMD8, when the CRC should be 0x87.

I send CMD 8 as 0x4800, 0x0001, 0xAA87

It should respond with 0x01, followed by two 16-bit words which echo the command.

Another thing, the spec says you should send 8 extra clock pulses at the end of each command for it to finish its internal workings.

0 Kudos