This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430 not initialising microSD

Other Parts Discussed in Thread: MSP-EXP430F5529LP

HI,

I've taken the example code slaa281b and I'm not getting a response from two microSD cards (I've tried with the MSP-EXP430F5529LP

I've connected the microSD as follows

microSD                                      MSP430

Pin1(NC)                                     N/A

Pin2(CS)   <----------------------- P3.4(GPIO)

Pin3(DI)     <----------------------- P3.0(SIMO)

Pin4(Vdd)  <---------------------- 3V3

Pin5(CLK)  <---------------------- P3.2(SCLK)

Pin6(Vss)   <---------------------- GND

Pin7(DO)    ----------------------> P3.1(SOMI)

Pin8(NC)                                     N/A

 A 47K resistor is on the CS line to 3V3, a 100nF cap is between 3V3 and GND and the code has been tried with and without a 10K resistor tried high on DO.

I have a scope on all four pins (also tested on pads of microSD card), the CLK is at 200kHz and the other pins are responding as in the code and yet no output is seen on DO.  

-With the DO tied high, it remains high throughout the initialisation and GO_IDLE_STATE command

-With the pull up removed DO remains low throughout the initialisation and GO_IDLE_STATE command

Here is my complete code

#include <msp430.h>
#include <stdio.h>

#define TRUE 1
#define FALSE 0

unsigned int clkFail = 0;
unsigned int XT2Fail = 0;

#define SD_CS BIT4
#define SD_DETECT BIT5
#define SD_SIMO BIT0
#define SD_SOMI BIT1
#define SD_CLK BIT2

#define CS_LOW()    P3OUT &= ~SD_CS               												// Card Select
#define CS_HIGH()   while (!(UCB0IFG&UCTXIFG)); P3OUT |= SD_CS  								// Card Deselect

unsigned char microSD;
unsigned char  mircoSDError;

#define SD_SUCCESS 0x00
#define SD_BLOCK_SET_ERROR 0x01
#define SD_RESPONSE_ERROR 0x02
#define SD_DATA_TOKEN_ERROR 0x03
#define SD_INIT_ERROR 0x04
#define SD_CRC_ERROR 0x10
#define SD_WRITE_ERROR 0x11
#define SD_OTHER_ERROR 0x12
#define SD_TIMEOUT_ERROR 0xFF

#define DUMMY_CHAR 0xFF

#define SD_GO_IDLE_STATE 0x40 //CMD0
#define SD_SEND_OP_COND	0x41 //CMD1

#define CMD0_CRC 0x95

static void _UCSinitDev(void);
void SetVcoreUp (unsigned int level);

void _SDinit (void);
char SDidle (void);
void SDSendCmd (const char cmd, unsigned long data, const char crc);
char SDGetResponse(void);
unsigned char SDSendFrame(unsigned char* pBuffer, unsigned int size);
char SDPing(void);

int main(void)
	{
		WDTCTL = WDTPW+WDTHOLD;                 												// Stop watchdog timer

		_UCSinitDev();																				// Setup UCS

		__enable_interrupt();

		_SDinit ();
	}

/*************/
/* UCS setup */
/*************/

static void _UCSinitDev(void)
	{
		static unsigned int clkRetries,XT2Retries = 0;
		static const unsigned int CLK_RETRIES_MAX = 0xFFFF;

		P5SEL |= (BIT2 | BIT3); 																// Set P5.2/3 to XT2
		P1SEL |= BIT0;
		P1DIR |= BIT0;

		SetVcoreUp (0x01);
		SetVcoreUp (0x02);
		SetVcoreUp (0x03);

		UCSCTL6 |=(XT1OFF); 																	// Turn Off XT1
		UCSCTL6 &=~(XT2OFF); 																	// Enable XT2
		UCSCTL6 |= (XT2DRIVE_3);

		// Loop until XT1 fault flag is cleared
		do
			{
				  UCSCTL7 &= ~XT2OFFG;                  										// Clear XT1 fault flags
				  XT2Retries++;
			}
		while ((UCSCTL7&XT2OFFG) && (XT2Retries < CLK_RETRIES_MAX));               				// Test XT1 fault flag

		if(XT2Retries == 0xFFFE)
			{
				XT2Fail = 1;
			}

		//XT2 now stable, reduce drive strength
		UCSCTL6 |= (XT2DRIVE_2);

		UCSCTL3 = (SELREF__XT2CLK | FLLREFDIV__16); 											// DCO_Ref = XT2/16
																								// -> 4MHz/16 = 0.25 MHz
		__bis_SR_register(SCG0);                  												// Disable the FLL control loop
		UCSCTL1 = (DCORSEL_5);																	// Set DCORSEL to 5
		UCSCTL2 = (0x001F | FLLD__16); 															// DCOCLK = DCO_Ref x (31+1) x FLLD__#, DCOCLKDIV = DCOCLK / FLLD__#
																								// -> DCOCLK = 16 MHz, DCOCLKDIV = 1 MHz
		__bis_SR_register(SCG0);                  												// Enable the FLL control loop
		UCSCTL4 = (SELA__DCOCLKDIV | SELS__DCOCLK | SELM__DCOCLK); 								// ACLK = DCOCLK, SMCLK = MCLK = DCOCLK
		UCSCTL5 = (DIVPA__1 | DIVA__1 | DIVS__1 | DIVM__1); 									// Output pin clk = ACLK/16 = 1 MHz

		__delay_cycles(253952);

		do
			{
				UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
																								// Clear XT2,XT1,DCO fault flags
				SFRIFG1 &= ~OFIFG;                      										// Clear fault flags
				clkRetries++;
			}
		while ((SFRIFG1&OFIFG) && (clkRetries < CLK_RETRIES_MAX));                   			// Test oscillator fault flag

		if(clkRetries == 0xFFFE)
			{
				clkFail = 1;
			}
	}

void SetVcoreUp (unsigned int level)
	{
		// Open PMM registers for write
		PMMCTL0_H = PMMPW_H;
		// Set SVS/SVM high side new level
		SVSMHCTL = SVSHE + SVSHRVL0 * level + SVMHE + SVSMHRRL0 * level;
		// Set SVM low side to new level
		SVSMLCTL = SVSLE + SVMLE + SVSMLRRL0 * level;
		// Wait till SVM is settled
		while ((PMMIFG & SVSMLDLYIFG) == 0);
		// Clear already set flags
		PMMIFG &= ~(SVMLVLRIFG + SVMLIFG);
		// Set VCore to new level
		PMMCTL0_L = PMMCOREV0 * level;
		// Wait till new level reached
		if ((PMMIFG & SVMLIFG))
			while ((PMMIFG & SVMLVLRIFG) == 0);
		// Set SVS/SVM low side to new level
		SVSMLCTL = SVSLE + SVSLRVL0 * level + SVMLE + SVSMLRRL0 * level;
		// Lock PMM registers for write access
		PMMCTL0_H = 0x00;
	}

/*************/
/*  microSD  */
/*************/

void _SDinit (void)
	{
		int i;
		unsigned char SDstatus = FALSE;
		int timeout;
		unsigned char response;

		if (!SDPing())      											// No microSD card
			{
				microSD = FALSE;
			}
		else														// microSD card is inserted
			{
				while (SDstatus != TRUE)                       			// SD not initialised
					{
						// Init Port for SD (default high)
						P3OUT |= SD_SIMO + SD_CLK;
						P3DIR |= SD_SIMO + SD_CLK;

						// Chip Select
						P3OUT |= SD_CS;
						P3DIR |= SD_CS;

						// Init SPI Module
//						UCB0CTL1 |= UCSWRST;                    												// **Put state machine in reset**
						UCB0CTL0 = UCMST+UCCKPL+UCMSB+UCSYNC;     												// 3-pin, 8-bit SPI master
						UCB0CTL1 = UCSSEL__ACLK + UCSWRST;                    												// **Put state machine in reset**
//						UCB0CTL1 |= UCSSEL__ACLK;              													// ACLK
						UCB0BR0 |= 0x05;                          												// SPICLK = 400kHz
						UCB0BR1 = 0;
//						P3SEL |= SD_SIMO + SD_SOMI + SD_CLK;                									// Enable SIMO, SOMI and SCLK pins
						UCB0CTL1 &= ~UCSWRST;                     												// **Initialize USCI state machine**

						// Initialization sequence on PowerUp
						CS_HIGH();

						for(i=0;i<=20;i++)
							{
								spiSendByte(DUMMY_CHAR);
							}

						SDstatus = SDidle();

						timeout++;
						if (timeout == 150)                      		// Try 150 times till error
							{
								microSD = FALSE;
								return;
							}
					}
			microSD = TRUE;
		}
	}

char SDidle (void)
	{
		char response=0x01;
		CS_LOW();

		//Send Command 0 to put MMC in SPI mode
		SDSendCmd(SD_GO_IDLE_STATE,0,CMD0_CRC);
		//Now wait for READY RESPONSE
		if(SDGetResponse()!=0x01)
			{
				mircoSDError = SD_INIT_ERROR;
				return (FALSE);
			}
		while(response==0x01)
			{
				CS_HIGH();
				spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
				CS_LOW();
				SDSendCmd(SD_SEND_OP_COND,0x00,0xFF);
				response = SDGetResponse();
			}

		CS_HIGH();
		spiSendByte(DUMMY_CHAR);
		return (TRUE);
	}

//Send one byte via SPI
unsigned char spiSendByte(const unsigned char data)
	{
		while (!(UCB0IFG&UCTXIFG));    // wait while not ready for TX
		UCB0TXBUF = data;            // write
		while (!(UCB0IFG&UCRXIFG));    // wait for RX buffer (full)
		return (UCB0RXBUF);
	}

void SDSendCmd (const char cmd, unsigned long data, const char crc)
	{
		unsigned char frame[6];
		char temp;
		int i;

		frame[0]=(cmd|0x40);
		for(i=3;i>=0;i--)
			{
				temp = (char)(data>>(8*i));
			  	frame[4-i] = (temp);
		  	}
		frame[5]=(crc);
		SDSendFrame(frame,6);
	}

char SDGetResponse(void)
	{
		//Response comes 1-8bytes after command
		//the first bit will be a 0
		//followed by an error code
		//data will be 0xff until response
		int i=0;

		char response;

		while(i<=64)
			{
				response = spiSendByte(DUMMY_CHAR);
				if(response==0x00)break;
				if(response==0x01)break;
				i++;
			}
		return response;
	}

unsigned char SDSendFrame(unsigned char* pBuffer, unsigned int size)
	{
		unsigned long i = 0;
		// clock the actual data transfer and receive the bytes; spi_read automatically finds the Data Block
		for (i = 0; i < size; i++)
			{
				while (!(UCB0IFG&UCTXIFG));         			// USCI_B0 TX buffer ready?
				UCB0TXBUF = (0xFF);							// LSB not transfer buffer
				while (!(UCB0IFG&UCRXIFG));    				// USCI_B0 RX buffer full?
				pBuffer[i] = UCB0RXBUF;
			}
		return(0);
	}

char SDPing(void)
	{
/*		// Card Detect
		P3DIR &=  ~SD_DETECT;

 		if (!(P3IN & SD_DETECT))								//SD_DETECT Pulled Low
			{
				return (TRUE);
			}
		else													//SD_DETECT Pulled High
			{
				return (FALSE);
			}
*/		return (TRUE);
	}

 

  • I see a lot of problems so there are probably more than this:

    1) SPI port initialization is a one time thing yet it is inside of a loop.
    2) The code to reset UCB0 is commented out.
    3) The code to assign the port pins to UCB0 is commented out.
    4) The code to get a response is far too specific. Look for a value that isn't 0xff.
    5) SDSendFrame sends only 0xff.
    6) The check of TXIFG in the CS_HIGH macro is useless. TXIFG can be true while data is still being shifted out.
  • Hi, Thanks for the quick response.

    1 - I would agree with this but example slaa281b has the initialisation is in a loop, I'll edit it to see if there is any change in response.

    2 - UCB0 is reset in this line, as per the example:

    UCB0CTL1 = UCSSEL__ACLK + UCSWRST;                     // **Put state machine in reset**

    I'll edit it back to how I normally initialise SPI:

    // Init SPI Module
    UCB0CTL1 |= UCSWRST;                    // **Put state machine in reset**
    UCB0CTL0 = UCMST+UCCKPL+UCMSB+UCSYNC;   // 3-pin, 8-bit SPI master
    UCB0CTL1 |= UCSSEL__ACLK;               // ACLK
    UCB0BR0 |= 0x05;                        // SPICLK = 200kHz
    UCB0BR1 = 0;
    P3SEL |= SD_SIMO + SD_SOMI + SD_CLK;    //Enable SIMO, SOMI and SCLK pins
    UCB0CTL1 &= ~UCSWRST;                   // **Initialize USCI state machine**

    3 - P3SEL was edited out for the example, my original code included it

    4 - My scope is set to trigger on a DO falling edge, this has never occurred, DO is always 0xFF with the pull up resistor

    5 - Has been changed

    6 - Can you point me to some code which serves this purpose better? This code has always worked in all past SPI implementations

    With these changes the microSD card still doesn't respond

  • I could provide the code I am working on now for the fr5969 but it wouldn't help you much since it is written in Forth. The code I wrote for an ARM chip is available (home.earthlink.net/.../index.html) but is a little dated now since it doesn't handle SDHC. Speaking of which, slaa281 uses the antiquated MMC initialization and will not work with SDHC cards. It did work with some older SD cards. The current SDHC initialization starts with CMD0 but there is quite a bit more after that.
  • Hi David,

    Thanks for your help, with your hints I've managed to get past the first few hurdles.

    The microSD card now responds and has given the correct response to all the initialisation commands.

    However I'm now looking at the OCR register and I was expecting bit 30 to be set high as I'm using a Kingston SDHC card but it is low.  The call for the OCR register occurs at the end of my initialisation code (after ACMD41).

    Looking at the flow chart here http://elm-chan.org/docs/mmc/gfx1/sdinit.png I should be able to set the block size to 512 bytes but instead I'm getting the response 0x05 back.

    I've also tried writing to the microSD card and only get 0x05 back.

    So despite all the correct responses to the commands the card is not initialised?

    As it is SDHC the writing is still block based?

  • Did you set the HCS bit when sending ACMD41? This is how you tell the card that you support high capacity cards. I have never tried initializing a SDHC card with that bit clear so I don't know how they deal with it.

    SDHC cards have a fixed block size of 512. Trying to change the block length for a card that isn't initialized (idle bit is set) is your problem.

    Grab a copy of the latest version of the physical layer spec here:

    Hmmm. It looks I need to grab a new copy as well since they have moved on from V3.0 while I wasn't looking.

  • I had forgotten to set the HCS bit, just tried the code with it but it still fails.  I've attached my code in case there is anything obviously wrong.

    char SDidle (void)
    	{
    		char response=0x00;
    		char i;
    		char SDHC;
    		CS_LOW();
    
    		//Send Command 0 to put MMC in SPI mode
    		SDSendCmd(CMD0,0,CMD0crc);
    		//Now wait for READY RESPONSE
    		if(SDGetResponse()!=0x01)
    			{
    				mircoSDError = SD_INIT_ERROR;
    				return (FALSE);
    			}
    		if (mircoSDError != SD_v1)
    			{
    				while(response!=0x01)
    					{
    						CS_HIGH();
    						spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    						CS_LOW();
    						SDSendCmd(CMD8,CMD8data,CMD8crc);
    						response = SDGetResponse();
    						if(response == 0x05)							// SD card is not Version 2 or later
    							{
    								microSD = SD_v1;
    								return (FALSE);
    							}
    						else
    							{
    								mircoSDError = SD_INIT_ERROR;
    								if(spiSendByte(DUMMY_CHAR) != 0x00)return (FALSE);		// SD card is not Version 2 or later
    								if(spiSendByte(DUMMY_CHAR) != 0x00)return (FALSE);		// SD card is not Version 2 or later
    								if(spiSendByte(DUMMY_CHAR) != 0x01)return (FALSE);		// SD card is not Version 2 or later
    								if(spiSendByte(DUMMY_CHAR) != 0xAA)return (FALSE);		// SD card is not Version 2 or later
    								mircoSDError = 0x00;
    							}
    					}
    
    				//CMD55
    				response = 0x00;
    				while(response!=0x01 && response !=0x05)
    					{
    						CS_HIGH();
    						spiSendByte(DUMMY_CHAR);
    						CS_LOW();
    						SDSendCmd(CMD55,0,0xFF);
    						response = SDGetResponse();
    					}
    				if(response == 0x05)							// SD card is not Version 2 or later
    					{
    						microSD = SD_INIT_ERROR;  //?
    						return (FALSE);
    					}
    
    				//CMD41
    				response = 0x01;
    				while(response!=0x00 && response !=0x05)
    					{
    						CS_HIGH();
    						spiSendByte(DUMMY_CHAR);
    						CS_LOW();
    						SDSendCmd(CMD41,0x00000000,0xFF);
    						response = SDGetResponse();
    					}
    				if(response == 0x05)							// SD card is not Version 2 or later
    					{
    						microSD = SD_INIT_ERROR;
    						return (FALSE);
    					}
    
    				//CMD58
    				response = 0x00;
    				while(response!=0x01)
    					{
    						CS_HIGH();
    						spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    						CS_LOW();
    						SDSendCmd(CMD58,0,0xFF);
    						response = SDGetResponse();
    					}
    				SDHC = spiSendByte(DUMMY_CHAR);
    				spiSendByte(DUMMY_CHAR);
    				spiSendByte(DUMMY_CHAR);
    				spiSendByte(DUMMY_CHAR);
    
    				if (!(SDHC&0x40))
    					{
    						response = 0x00;
    						while(response!=0x01)
    							{
    								CS_HIGH();
    								spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    								CS_LOW();
    								SDSendCmd(CMD16,0x00000200,0xFF);
    								response = SDGetResponse();
    							}
    				}
    			}
    		else	//SD_v1 init code
    			{
    				//CMD8
    				response = 0x00;
    				while(response!=0x01 && response !=0x05)
    					{
    						CS_HIGH();
    						spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    						CS_LOW();
    						SDSendCmd(CMD8,CMD8data,CMD8crc);
    						response = SDGetResponse();
    					}
    				if(response == 0x01)							// SD card is not Version 2 or later
    					{
    						for(;;);					//bad logic to get to here
    					}
    
    				//CMD55
    				response = 0x00;
    				while(response!=0x01 && response !=0x05)
    					{
    						CS_HIGH();
    						spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    						CS_LOW();
    						SDSendCmd(CMD55,0,0xFF);
    						response = SDGetResponse();
    					}
    				if(response == 0x05)							// SD card is not Version 2 or later
    					{
    						microSD = SD_INIT_ERROR;  				//Card is definitely not an MMC card
    						return (FALSE);
    					}
    
    				//CMD41
    				response = 0x01;
    				while(response!=0x00 && response !=0x05)
    					{
    						CS_HIGH();
    						spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    						CS_LOW();
    						SDSendCmd(CMD41,0x00000000,0xFF);
    						response = SDGetResponse();
    					}
    				if(response == 0x05)							// SD card is not Version 2 or later
    					{
    						microSD = SD_INIT_ERROR;  				//Card is definitely not an MMC card!
    						return (FALSE);
    					}
    
    				//CMD16 - Set Block Length
    				response = 0x00;
    				while(response!=0x01  && response !=0x05)
    					{
    						CS_HIGH();
    						spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    						CS_LOW();
    						SDSendCmd(CMD16,0x00000200,0xFF);
    						response = SDGetResponse();
    					}
    				if (response==0x05)
    					{
    						microSD = SD_INIT_ERROR;  				//Card is definitely not an MMC card!
    						return (FALSE);
    					}
    			}
    
    		CS_HIGH();
    		spiSendByte(DUMMY_CHAR);
    		return (TRUE);
    	}

    CMD55 produces 0x01, CMD41 produces 0x05

    I'll have a more detailed look at the spec over the weekend, thanks for the link

  • Of course the response to CMD55 is going to show that the card is still in the idle state. It only changes out of that state in response to ACMD41. Which you haven't sent yet.

    I have taken two approaches to handling the ACMDs. I have set a bit in the command byte to flag it as needing a CMD55 first and then let the send command routine handle it. I have also, and this is what I prefer for now, have a separate routine to send them. Either is certainly preferable to sprinkling the code with special case sending of CMD55.
  • I've read through the SPI section of the SD physical layer PDF, I've taken your suggestion and placed ACMD41 into its own function, after CMD41 the response I get 0x01 followed by 0xFF, so the card is not liking CMD41 and going idle and then ignoring any further commands. 

    char SDacmd41(void)
    	{
    		unsigned int i,j;
    		char response;
    
    		for (i=0;i<100;i++)
    			{
    				//CMD55
    				CS_HIGH();
    				spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    				CS_LOW();
    				SDSendCmd(CMD55,0,0xFF);
    				spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    				spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    
    				//CMD41
    				CS_HIGH();
    				spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    				CS_LOW();
    				spiSendByte(DUMMY_CHAR);						// Send single Byte on SPI
    				SDSendCmd(CMD41,0x40000000,0xFF);
    j=0; while(j<=1000) { response = spiSendByte(DUMMY_CHAR); if(response==0x00)break; j++; } if(response == 0x00) { return TRUE; } } return FALSE; }

  • I've just got a SDHC and non-HC card to initialize and have successfully written to both.

    Thank you for your help!

**Attention** This is a public forum