EmbeddedRelated.com
Forums
Memfault Beyond the Launch

Having trouble with SPI for SD Card

Started by mastarre May 26, 2010
Hello Everyone,

I have code for a MSP430FG4618 / RF2500 combo running SimpliciTI that is working very well and I would now like to add an SD card.

I am looking at the guide in SLAA281B (http://focus.ti.com/lit/an/slaa281b/slaa281b.pdf) and am using the associated example code.

I believe that USCI_B is used by the radio so I would like to communicate with the SD card using USCI_A. Apparently I am missing something as I have spent several a long time working on this without much success.

Here is where I am:
I have configured the hardware as follows:

1 (CS) ---------------> P7.0 (UCA0STE), H6.1
2 (DI) ----------------> P7.1 (UCA0SIMO), H6.2
3 (VSS) -------------> GND
4 (VCC) -------------> VCC
5 (SCK) -------------> P7.3 (UCA0CLK), H6.4
6 (CD) ---------------> P7.4, H6.5
7 (DO) ---------------> P7.2(UCA0SOMI), H6.3

And updated the software in hal_MMC_hardware_board.h to reflect those pin assignments and my MCU, the MSP430fg4618, as shown below.

Additionally, I modified SPI_SER_INTF as follows:

#define SPI_SER_INTF SER_INTF_USCIA0 // Interface to MMC

However, when debugging that I recieve many errors to the effect of :

Error[Pe020]: identifier "UTXIFG0" is undefined C:\\MCU\examples\sd_for_ti_SLAA281B\MMC_lib\hal_SPI.c 286

Etc. Looking in msp430xG46x.h I find that they are in fact not defined. As a test, I tried switching the definition above to SER_INTF_USCIB0 and it did compile in that case but of course it didn't actually work because my wiring does not reflect that change.

Again, I want to use USCI_A as I believe the radio is using USCI_B

What am I not seeing? I would really appreciate some help.

#include // Adjust this according to the
// MSP430 device being used.
// SPI port definitions // Adjust the values for the chosen
#define SPI_PxSEL P7SEL // interfaces, according to the pin
#define SPI_PxDIR P7DIR // assignments indicated in the
#define SPI_PxIN P7IN // chosen MSP430 device datasheet.
#define SPI_PxOUT P7OUT
#define SPI_SIMO 0x02
#define SPI_SOMI 0x04
#define SPI_UCLK 0x08
//----
// SPI/UART port selections. Select which port will be used for the interface
//----
#define SPI_SER_INTF SER_INTF_USCIB0 // Interface to MMC

// SPI port definitions // Adjust the values for the chosen
#define MMC_PxSEL SPI_PxSEL // interfaces, according to the pin
#define MMC_PxDIR SPI_PxDIR // assignments indicated in the
#define MMC_PxIN SPI_PxIN // chosen MSP430 device datasheet.
#define MMC_PxOUT SPI_PxOUT
#define MMC_SIMO SPI_SIMO
#define MMC_SOMI SPI_SOMI
#define MMC_UCLK SPI_UCLK

// Chip Select
#define MMC_CS_PxOUT P7OUT
#define MMC_CS_PxDIR P7DIR
#define MMC_CS 0x01

// Card Detect
#define MMC_CD_PxIN P7IN
#define MMC_CD_PxDIR P7DIR
#define MMC_CD 0x10

#define CS_LOW() MMC_CS_PxOUT &= ~MMC_CS // Card Select
#define CS_HIGH() while(!halSPITXDONE); MMC_CS_PxOUT |= MMC_CS // Card Deselect

#define DUMMY_CHAR 0xFF

Beginning Microcontrollers with the MSP430

Oh, and if I configure:
#define SPI_SER_INTF SER_INTF_BITBANG // Interface to MMC

It will not proceed past mmcReadCardSize();

--- In m..., "mastarre" wrote:
>
> Hello Everyone,
>
> I have code for a MSP430FG4618 / RF2500 combo running SimpliciTI that is working very well and I would now like to add an SD card.
>
> I am looking at the guide in SLAA281B (http://focus.ti.com/lit/an/slaa281b/slaa281b.pdf) and am using the associated example code.
>
> I believe that USCI_B is used by the radio so I would like to communicate with the SD card using USCI_A. Apparently I am missing something as I have spent several a long time working on this without much success.
>
> Here is where I am:
> I have configured the hardware as follows:
>
> 1 (CS) ---------------> P7.0 (UCA0STE), H6.1
> 2 (DI) ----------------> P7.1 (UCA0SIMO), H6.2
> 3 (VSS) -------------> GND
> 4 (VCC) -------------> VCC
> 5 (SCK) -------------> P7.3 (UCA0CLK), H6.4
> 6 (CD) ---------------> P7.4, H6.5
> 7 (DO) ---------------> P7.2(UCA0SOMI), H6.3
>
>
>
> And updated the software in hal_MMC_hardware_board.h to reflect those pin assignments and my MCU, the MSP430fg4618, as shown below.
>
> Additionally, I modified SPI_SER_INTF as follows:
>
> #define SPI_SER_INTF SER_INTF_USCIA0 // Interface to MMC
>
> However, when debugging that I recieve many errors to the effect of :
>
> Error[Pe020]: identifier "UTXIFG0" is undefined C:\\MCU\examples\sd_for_ti_SLAA281B\MMC_lib\hal_SPI.c 286
>
> Etc. Looking in msp430xG46x.h I find that they are in fact not defined. As a test, I tried switching the definition above to SER_INTF_USCIB0 and it did compile in that case but of course it didn't actually work because my wiring does not reflect that change.
>
> Again, I want to use USCI_A as I believe the radio is using USCI_B
>
> What am I not seeing? I would really appreciate some help.
>
>
>
> #include // Adjust this according to the
> // MSP430 device being used.
> // SPI port definitions // Adjust the values for the chosen
> #define SPI_PxSEL P7SEL // interfaces, according to the pin
> #define SPI_PxDIR P7DIR // assignments indicated in the
> #define SPI_PxIN P7IN // chosen MSP430 device datasheet.
> #define SPI_PxOUT P7OUT
> #define SPI_SIMO 0x02
> #define SPI_SOMI 0x04
> #define SPI_UCLK 0x08
> //----
> // SPI/UART port selections. Select which port will be used for the interface
> //----
> #define SPI_SER_INTF SER_INTF_USCIB0 // Interface to MMC
>
> // SPI port definitions // Adjust the values for the chosen
> #define MMC_PxSEL SPI_PxSEL // interfaces, according to the pin
> #define MMC_PxDIR SPI_PxDIR // assignments indicated in the
> #define MMC_PxIN SPI_PxIN // chosen MSP430 device datasheet.
> #define MMC_PxOUT SPI_PxOUT
> #define MMC_SIMO SPI_SIMO
> #define MMC_SOMI SPI_SOMI
> #define MMC_UCLK SPI_UCLK
>
> // Chip Select
> #define MMC_CS_PxOUT P7OUT
> #define MMC_CS_PxDIR P7DIR
> #define MMC_CS 0x01
>
> // Card Detect
> #define MMC_CD_PxIN P7IN
> #define MMC_CD_PxDIR P7DIR
> #define MMC_CD 0x10
>
> #define CS_LOW() MMC_CS_PxOUT &= ~MMC_CS // Card Select
> #define CS_HIGH() while(!halSPITXDONE); MMC_CS_PxOUT |= MMC_CS // Card Deselect
>
> #define DUMMY_CHAR 0xFF
>

Hi,

Have you checked that - in case it's bit banged like the code suggests -
the proper SPI mode is used ?
The card will init on its pulse train to switch to SPI, but with the
wrong CLK/DATA phase, it will stay mute and deaf.
Perhaps worth double check ?

HTH
Kris

On Wed, 2010-05-26 at 22:57 +0000, mastarre wrote:
> Oh, and if I configure:
> #define SPI_SER_INTF SER_INTF_BITBANG // Interface to MMC
>
> It will not proceed past mmcReadCardSize();
>
> --- In m..., "mastarre" wrote:
> >
> > Hello Everyone,
> >
> > I have code for a MSP430FG4618 / RF2500 combo running SimpliciTI that is working very well and I would now like to add an SD card.
> >
> > I am looking at the guide in SLAA281B (http://focus.ti.com/lit/an/slaa281b/slaa281b.pdf) and am using the associated example code.
> >
> > I believe that USCI_B is used by the radio so I would like to communicate with the SD card using USCI_A. Apparently I am missing something as I have spent several a long time working on this without much success.
> >
> > Here is where I am:
> > I have configured the hardware as follows:
> >
> > 1 (CS) ---------------> P7.0 (UCA0STE), H6.1
> > 2 (DI) ----------------> P7.1 (UCA0SIMO), H6.2
> > 3 (VSS) -------------> GND
> > 4 (VCC) -------------> VCC
> > 5 (SCK) -------------> P7.3 (UCA0CLK), H6.4
> > 6 (CD) ---------------> P7.4, H6.5
> > 7 (DO) ---------------> P7.2(UCA0SOMI), H6.3
> >
> >
> >
> > And updated the software in hal_MMC_hardware_board.h to reflect those pin assignments and my MCU, the MSP430fg4618, as shown below.
> >
> > Additionally, I modified SPI_SER_INTF as follows:
> >
> > #define SPI_SER_INTF SER_INTF_USCIA0 // Interface to MMC
> >
> > However, when debugging that I recieve many errors to the effect of :
> >
> > Error[Pe020]: identifier "UTXIFG0" is undefined C:\\MCU\examples\sd_for_ti_SLAA281B\MMC_lib\hal_SPI.c 286
> >
> > Etc. Looking in msp430xG46x.h I find that they are in fact not defined. As a test, I tried switching the definition above to SER_INTF_USCIB0 and it did compile in that case but of course it didn't actually work because my wiring does not reflect that change.
> >
> > Again, I want to use USCI_A as I believe the radio is using USCI_B
> >
> > What am I not seeing? I would really appreciate some help.
> >
> >
> >
> > #include // Adjust this according to the
> > // MSP430 device being used.
> > // SPI port definitions // Adjust the values for the chosen
> > #define SPI_PxSEL P7SEL // interfaces, according to the pin
> > #define SPI_PxDIR P7DIR // assignments indicated in the
> > #define SPI_PxIN P7IN // chosen MSP430 device datasheet.
> > #define SPI_PxOUT P7OUT
> > #define SPI_SIMO 0x02
> > #define SPI_SOMI 0x04
> > #define SPI_UCLK 0x08
> >
> >
> > //----
> > // SPI/UART port selections. Select which port will be used for the interface
> > //----
> > #define SPI_SER_INTF SER_INTF_USCIB0 // Interface to MMC
> >
> > // SPI port definitions // Adjust the values for the chosen
> > #define MMC_PxSEL SPI_PxSEL // interfaces, according to the pin
> > #define MMC_PxDIR SPI_PxDIR // assignments indicated in the
> > #define MMC_PxIN SPI_PxIN // chosen MSP430 device datasheet.
> > #define MMC_PxOUT SPI_PxOUT
> > #define MMC_SIMO SPI_SIMO
> > #define MMC_SOMI SPI_SOMI
> > #define MMC_UCLK SPI_UCLK
> >
> > // Chip Select
> > #define MMC_CS_PxOUT P7OUT
> > #define MMC_CS_PxDIR P7DIR
> > #define MMC_CS 0x01
> >
> > // Card Detect
> > #define MMC_CD_PxIN P7IN
> > #define MMC_CD_PxDIR P7DIR
> > #define MMC_CD 0x10
> >
> > #define CS_LOW() MMC_CS_PxOUT &= ~MMC_CS // Card Select
> > #define CS_HIGH() while(!halSPITXDONE); MMC_CS_PxOUT |= MMC_CS // Card Deselect
> >
> > #define DUMMY_CHAR 0xFF
> >
>
Here, maybe this will help. Get the PDF, Application notes link after
'background'.


His code worked right out of the box with DMA. I wrote a much simpler
version for the 149, non DMA from that. As I didn't post it there, I'll
include it below. This is tested code, it worked at the time I was using
it :)

Best, Dan.

sd.h~~~~~~~
#ifndef SD_H
#define SD_H

#define SD_BLOCKSIZE 512
#define SD_BLOCKSIZE_NBITS 9
#define SD_CLK_SLOW_DIV 400000
#define SD_TIMEOUT_READ 20
#define MSK_TOK_ERROR 0x01
#define MSK_TOK_DATAERROR 0xE0
#define SD_TOK_WRITE_STARTBLOCK 0xFE
#define SD_IDLE_WAIT_MAX 100
#define SD_CMD_TIMEOUT 100

//Standard voltage range expected
#define SD_ORC_RESPONSE 0x80ff0001

//the new commands, add as needed
//first is we don' t need any commands that return the type 'R1B'
//the comand is high byte and response count is low byte

//Must or mask all commands with 01000000
#define SD_CMDMASK 0x4000
#define SDRTN_R1 1
#define SDRTN_R3 5

#define SDCMD_0 ( SD_CMDMASK | ( 0 << 8 ) | SDRTN_R1 )
#define SDCMD_55 ( SD_CMDMASK | ( 55 << 8 ) | SDRTN_R1 )
#define SDCMD_A41 ( SD_CMDMASK | ( 41 << 8 ) | SDRTN_R1 )
#define SDCMD_58 ( SD_CMDMASK | ( 58 << 8 ) | SDRTN_R3 )
#define SDCMD_17 ( SD_CMDMASK | ( 17 << 8 ) | SDRTN_R1 )
#define SDCMD_24 ( SD_CMDMASK | ( 24 << 8 ) | SDRTN_R1 )

extern BYTE* secBuf;
extern BYTE response[5];
extern WORD block_count;

/* User functions */
bool sd_initialize( );

bool sd_read_block( DWORD blockaddr, const BYTE* pData );

bool sd_write_block( DWORD blockaddr, const BYTE* pData );

bool sd_send_command( WORD cmd, DWORD arg );

void sd_delay( BYTE number );

#endif

sd.c~~~~~~~~~
#include "Device_430.h"
#include "spi.h"
#include "sd.h"

/* SPI SD initialization sequence:
* CMD0
* CMD55
* ACMD41
* CMD58
*/

bool sd_initialize( )
{
// Delay for at least 74 clock cycles. This means to actually
// *clock* out at least 74 clock cycles with no data present on
// the clock. In SPI mode, send at least 10 idle bytes (0xFF).

//initialize the SPI port
spi_initialize( );

//Start out with a slow SPI clock, 400kHz, as required by the SD spec
spi_set_divisor( SD_CLK_SLOW );

//spi_cs_assert( );
SPI_CS_ASSERT;
sd_delay( 100 );

SPI_CS_DEASSERT;
sd_delay( 10 );

// Put the card in the idle state
if( ! sd_send_command( SDCMD_0, 0 ) )
return false;

//wait until idle clear
int j;
for( j= 0; j < SD_IDLE_WAIT_MAX; ++j )
{
if( sd_send_command( SDCMD_55, 0 ) )
{
/* Tell the card to send its OCR */
sd_send_command( SDCMD_A41, 0 );
}

if( ! response[0] )
break;
}
if( j == SD_IDLE_WAIT_MAX )
return false;

if( ! sd_send_command( SDCMD_58, 0 ) )
return false;

//Set the maximum SPI clock rate possible
spi_set_divisor( 2 );

return true;
}

bool sd_read_block( DWORD blockaddr, const BYTE* data )
{
unsigned long int i= 0;
unsigned char tmp;
block_count= 0;

// Adjust the block address to a linear address
blockaddr<<= SD_BLOCKSIZE_NBITS;

//Send block read command
if( ! sd_send_command( SDCMD_17, blockaddr ) )
return false;

// Check for an error, like a misaligned read
if( response[0] )
return false;

// Re-assert CS to continue the transfer
SPI_CS_ASSERT;

// Wait for the token
for( i= 0, tmp= 0xff; ( tmp == 0xFF ) && i < SD_TIMEOUT_READ; ++i )
tmp= spi_rcv_byte( );

if( ! ( tmp & MSK_TOK_DATAERROR ) )
{
// The card returned an error response. Bail and return false
// Clock out a byte before returning
spi_send_byte( 0xFF );

return 0;
}

//No dma, just do bulk transfer in code
for( block_count= 0; block_count < SD_BLOCKSIZE; ++block_count )
secBuf[ block_count ]= spi_rcv_byte( );

return 1;
}

bool sd_write_block( DWORD blockaddr, const BYTE *data )
{
// Adjust the block address to a linear address
blockaddr <<= SD_BLOCKSIZE_NBITS;

// Pack the address
//sd_packarg( argument, blockaddr );

if( ! sd_send_command( SDCMD_24, blockaddr ) )
return false;

// Check for an error, like a misaligned write
if( response[0] )
return false;

// Re-assert CS to continue the transfer
SPI_CS_ASSERT;

// The write command needs an additional 8 clock cycles before the
block write is started
spi_rcv_byte( );

// Kick off the transfer by sending the first byte, the "start
block" token
SD_UTXBUF = SD_TOK_WRITE_STARTBLOCK;

//Write the block
for( block_count= 0; block_count < SD_BLOCKSIZE; ++block_count )
spi_send_byte( secBuf[ block_count ] );

//need to pad some clocks or get Erase_Seq_Error
// on the next write, and a rcv_byte...
sd_delay( 4 );

//TODO ? might get hung here? Or should this be left for next op?
for( ; spi_rcv_byte( ) != 0xFF; )
;

return true;
}

bool sd_send_command( WORD cmd, DWORD arg )
{
volatile int i;
int rpos;
unsigned char tmp= 0;

SPI_CS_ASSERT;
spi_send_byte( cmd >> 8 );

for( i= 3; i >= 0 ; --i )
spi_send_byte( ( (BYTE*)&arg )[ i ] );

// This is the CRC. It only matters what we put here for the first
//command. Otherwise, the CRC is ignored for SPI mode unless we
//enable CRC checking.
spi_send_byte( 0x95 );

rpos= (BYTE)cmd - 1;

/* Wait for a response. A response can be recognized by the
start bit (a zero) */
for( i= 0; i < SD_CMD_TIMEOUT; ++i )
{
tmp= spi_rcv_byte( );
if( !( tmp & 0x80 ) )
break;
}
/* Just bail if we never got a response */
if( i >= SD_CMD_TIMEOUT )
{
SPI_CS_DEASSERT;
return false;
}
for(; rpos >= 0; --rpos )
{
response[ rpos ]= tmp;
tmp= spi_rcv_byte( );
}

SPI_CS_DEASSERT;
return true;
}

void sd_delay( BYTE number )
{
//clocks out idle bytes...
for( ; number--; )
spi_send_byte( 0xFF );
}

spi.h~~~~~~~~~~~~~~~~~~
#define SS_PORT_DIR P5DIR
#define SS_PORT_OUT P5OUT
#define SS_PIN BIT4

#define SPI_PORT_SEL P5SEL
#define SPI_PINS_SEL ( BIT1 | BIT2 | BIT3 )

//defines that set which port
#define SD_UCTL U1CTL
#define SD_UTCTL U1TCTL
#define SD_UBR0 U1BR0
#define SD_UBR1 U1BR1
#define SD_UMCTL U1MCTL
#define SD_MOD_REG ME2
#define SD_USPIE USPIE1
#define SPI_IFG IFG2
#define SD_UTXIFG UTXIFG1
#define SD_URXIFG URXIFG1
#define SD_UTXBUF U1TXBUF
#define SD_URXBUF U1RXBUF
#define SD_IE IE2
#define SD_URXIE URXIE1

// ...........................
void spi_initialize( );
void spi_set_divisor( WORD divisor );
void spi_send_byte( BYTE input );
BYTE spi_rcv_byte( );
void spi_enable( );
void spi_disable( );

#define SPI_CS_ASSERT SS_PORT_OUT&= ~SS_PIN
#define SPI_CS_DEASSERT SS_PORT_OUT|= SS_PIN

sdi.c~~~~~~~~~~~~~~~~~~~~~~~~
#include "Device_430.h"
#include "spi.h"

/* Initialize and enable the SPI module */
void spi_initialize( )
{
SPI_PORT_SEL = SPI_PINS_SEL; // Setup Px for SPI mode
SS_PORT_DIR|= SS_PIN; // Setup the SS as output
// low. So, initialize it high.
SS_PORT_OUT|= SS_PIN; // Set SS High

SD_UCTL= (CHAR | SYNC | MM | SWRST); // 8-bit, SPI, Master
SD_UTCTL= (SSEL1 | STC | CKPH); // Normal polarity, 3-wire
SD_UBR0= 0x002; // SPICLK = SMCLK/2 (2=Minimum divisor)
SD_UBR1= 0x000;
SD_UMCTL= 0x000;
SD_MOD_REG|= SD_USPIE; // Module enable
SD_UCTL&= ~SWRST; // SPI enable
}

/* Set the baud-rate divisor. The correct value is computed by dividing
the clock rate by the desired baud rate. The minimum divisor allowed
is 2. */
void spi_set_divisor( WORD divisor )
{
SD_UCTL|= SWRST; // Temporarily disable the SPI module
SD_UBR1= divisor >> 8;
SD_UBR0= divisor;
SD_UCTL&= ~SWRST; // Re-enable SPI
}

/* Send a single byte over the SPI port */
void spi_send_byte( BYTE input )
{
SPI_IFG&= ~SD_UTXIFG;
/* Send the byte */
SD_UTXBUF= input;
/* Wait for the byte to be sent */
while ( ! ( SPI_IFG & SD_UTXIFG ) )
;
}

/* Receive a byte. Output an 0xFF (the bus idles high) to receive the
byte */
BYTE spi_rcv_byte( )
{
unsigned char tmp;
SPI_IFG&= ~SD_URXIFG;

/* Send the byte */
SD_UTXBUF= 0xFF;

/* Wait for the byte to be received */
while( ! ( SPI_IFG & SD_URXIFG ) )
;
tmp= SD_URXBUF;
return tmp;
}

That Appnote was written with the MSP430F1xxx series in mind. Those didn't have the USCI modules that all MSPs have now, they had USART. Instead of having UCA0TXBUF, they had U0TXBUF.

I'd crack open the header file they used in that appnote, search through those files, and replace all the old USART commands with USCI commands.

-Steve

From: m... [mailto:m...] On Behalf Of mastarre
Sent: Wednesday, May 26, 2010 6:52 PM
To: m...
Subject: [msp430] Having trouble with SPI for SD Card

Hello Everyone,

I have code for a MSP430FG4618 / RF2500 combo running SimpliciTI that is working very well and I would now like to add an SD card.

I am looking at the guide in SLAA281B (http://focus.ti.com/lit/an/slaa281b/slaa281b.pdf) and am using the associated example code.

I believe that USCI_B is used by the radio so I would like to communicate with the SD card using USCI_A. Apparently I am missing something as I have spent several a long time working on this without much success.

Here is where I am:
I have configured the hardware as follows:

1 (CS) ---------------> P7.0 (UCA0STE), H6.1
2 (DI) ----------------> P7.1 (UCA0SIMO), H6.2
3 (VSS) -------------> GND
4 (VCC) -------------> VCC
5 (SCK) -------------> P7.3 (UCA0CLK), H6.4
6 (CD) ---------------> P7.4, H6.5
7 (DO) ---------------> P7.2(UCA0SOMI), H6.3

And updated the software in hal_MMC_hardware_board.h to reflect those pin assignments and my MCU, the MSP430fg4618, as shown below.

Additionally, I modified SPI_SER_INTF as follows:

#define SPI_SER_INTF SER_INTF_USCIA0 // Interface to MMC

However, when debugging that I recieve many errors to the effect of :

Error[Pe020]: identifier "UTXIFG0" is undefined C:\\MCU\examples\sd_for_ti_SLAA281B\MMC_lib\hal_SPI.c 286

Etc. Looking in msp430xG46x.h I find that they are in fact not defined. As a test, I tried switching the definition above to SER_INTF_USCIB0 and it did compile in that case but of course it didn't actually work because my wiring does not reflect that change.

Again, I want to use USCI_A as I believe the radio is using USCI_B

What am I not seeing? I would really appreciate some help.

#include // Adjust this according to the
// MSP430 device being used.
// SPI port definitions // Adjust the values for the chosen
#define SPI_PxSEL P7SEL // interfaces, according to the pin
#define SPI_PxDIR P7DIR // assignments indicated in the
#define SPI_PxIN P7IN // chosen MSP430 device datasheet.
#define SPI_PxOUT P7OUT
#define SPI_SIMO 0x02
#define SPI_SOMI 0x04
#define SPI_UCLK 0x08

//----------------------
// SPI/UART port selections. Select which port will be used for the interface
//----------------------
#define SPI_SER_INTF SER_INTF_USCIB0 // Interface to MMC

// SPI port definitions // Adjust the values for the chosen
#define MMC_PxSEL SPI_PxSEL // interfaces, according to the pin
#define MMC_PxDIR SPI_PxDIR // assignments indicated in the
#define MMC_PxIN SPI_PxIN // chosen MSP430 device datasheet.
#define MMC_PxOUT SPI_PxOUT
#define MMC_SIMO SPI_SIMO
#define MMC_SOMI SPI_SOMI
#define MMC_UCLK SPI_UCLK

// Chip Select
#define MMC_CS_PxOUT P7OUT
#define MMC_CS_PxDIR P7DIR
#define MMC_CS 0x01

// Card Detect
#define MMC_CD_PxIN P7IN
#define MMC_CD_PxDIR P7DIR
#define MMC_CD 0x10

#define CS_LOW() MMC_CS_PxOUT &= ~MMC_CS // Card Select
#define CS_HIGH() while(!halSPITXDONE); MMC_CS_PxOUT |= MMC_CS // Card Deselect

#define DUMMY_CHAR 0xFF



Thank you for the replies! I actually have the Foust note but forgot about the code on the bottom. I am going to look through both of them thoroughly.

One thing that is really bugging me, though, is why the msp430xG46x.h

Does not include definitions for things like U0RXBUF (mentioned in the foust paper). I can see it clear as day on 18-13 in the manual but yet it is not defined in the header file. I am trying to figure out why. Any ideas?

--- In m..., Dan Bloomquist wrote:
> Here, maybe this will help. Get the PDF, Application notes link after
> 'background'.
> His code worked right out of the box with DMA. I wrote a much simpler
> version for the 149, non DMA from that. As I didn't post it there, I'll
> include it below. This is tested code, it worked at the time I was using
> it :)
>
> Best, Dan.
>
> sd.h~~~~~~~
> #ifndef SD_H
> #define SD_H
>
> #define SD_BLOCKSIZE 512
> #define SD_BLOCKSIZE_NBITS 9
> #define SD_CLK_SLOW_DIV 400000
> #define SD_TIMEOUT_READ 20
> #define MSK_TOK_ERROR 0x01
> #define MSK_TOK_DATAERROR 0xE0
> #define SD_TOK_WRITE_STARTBLOCK 0xFE
> #define SD_IDLE_WAIT_MAX 100
> #define SD_CMD_TIMEOUT 100
>
> //Standard voltage range expected
> #define SD_ORC_RESPONSE 0x80ff0001
>
> //the new commands, add as needed
> //first is we don' t need any commands that return the type 'R1B'
> //the comand is high byte and response count is low byte
>
> //Must or mask all commands with 01000000
> #define SD_CMDMASK 0x4000
> #define SDRTN_R1 1
> #define SDRTN_R3 5
>
> #define SDCMD_0 ( SD_CMDMASK | ( 0 << 8 ) | SDRTN_R1 )
> #define SDCMD_55 ( SD_CMDMASK | ( 55 << 8 ) | SDRTN_R1 )
> #define SDCMD_A41 ( SD_CMDMASK | ( 41 << 8 ) | SDRTN_R1 )
> #define SDCMD_58 ( SD_CMDMASK | ( 58 << 8 ) | SDRTN_R3 )
> #define SDCMD_17 ( SD_CMDMASK | ( 17 << 8 ) | SDRTN_R1 )
> #define SDCMD_24 ( SD_CMDMASK | ( 24 << 8 ) | SDRTN_R1 )
>
> extern BYTE* secBuf;
> extern BYTE response[5];
> extern WORD block_count;
>
> /* User functions */
> bool sd_initialize( );
>
> bool sd_read_block( DWORD blockaddr, const BYTE* pData );
>
> bool sd_write_block( DWORD blockaddr, const BYTE* pData );
>
> bool sd_send_command( WORD cmd, DWORD arg );
>
> void sd_delay( BYTE number );
>
> #endif
>
> sd.c~~~~~~~~~
> #include "Device_430.h"
> #include "spi.h"
> #include "sd.h"
>
> /* SPI SD initialization sequence:
> * CMD0
> * CMD55
> * ACMD41
> * CMD58
> */
>
> bool sd_initialize( )
> {
> // Delay for at least 74 clock cycles. This means to actually
> // *clock* out at least 74 clock cycles with no data present on
> // the clock. In SPI mode, send at least 10 idle bytes (0xFF).
>
> //initialize the SPI port
> spi_initialize( );
>
> //Start out with a slow SPI clock, 400kHz, as required by the SD spec
> spi_set_divisor( SD_CLK_SLOW );
>
> //spi_cs_assert( );
> SPI_CS_ASSERT;
> sd_delay( 100 );
>
> SPI_CS_DEASSERT;
> sd_delay( 10 );
>
> // Put the card in the idle state
> if( ! sd_send_command( SDCMD_0, 0 ) )
> return false;
>
> //wait until idle clear
> int j;
> for( j= 0; j < SD_IDLE_WAIT_MAX; ++j )
> {
> if( sd_send_command( SDCMD_55, 0 ) )
> {
> /* Tell the card to send its OCR */
> sd_send_command( SDCMD_A41, 0 );
> }
>
> if( ! response[0] )
> break;
> }
> if( j == SD_IDLE_WAIT_MAX )
> return false;
>
> if( ! sd_send_command( SDCMD_58, 0 ) )
> return false;
>
> //Set the maximum SPI clock rate possible
> spi_set_divisor( 2 );
>
> return true;
> }
>
> bool sd_read_block( DWORD blockaddr, const BYTE* data )
> {
> unsigned long int i= 0;
> unsigned char tmp;
> block_count= 0;
>
> // Adjust the block address to a linear address
> blockaddr<<= SD_BLOCKSIZE_NBITS;
>
> //Send block read command
> if( ! sd_send_command( SDCMD_17, blockaddr ) )
> return false;
>
> // Check for an error, like a misaligned read
> if( response[0] )
> return false;
>
> // Re-assert CS to continue the transfer
> SPI_CS_ASSERT;
>
> // Wait for the token
> for( i= 0, tmp= 0xff; ( tmp == 0xFF ) && i < SD_TIMEOUT_READ; ++i )
> tmp= spi_rcv_byte( );
>
> if( ! ( tmp & MSK_TOK_DATAERROR ) )
> {
> // The card returned an error response. Bail and return false
> // Clock out a byte before returning
> spi_send_byte( 0xFF );
>
> return 0;
> }
>
> //No dma, just do bulk transfer in code
> for( block_count= 0; block_count < SD_BLOCKSIZE; ++block_count )
> secBuf[ block_count ]= spi_rcv_byte( );
>
> return 1;
> }
>
> bool sd_write_block( DWORD blockaddr, const BYTE *data )
> {
> // Adjust the block address to a linear address
> blockaddr <<= SD_BLOCKSIZE_NBITS;
>
> // Pack the address
> //sd_packarg( argument, blockaddr );
>
> if( ! sd_send_command( SDCMD_24, blockaddr ) )
> return false;
>
> // Check for an error, like a misaligned write
> if( response[0] )
> return false;
>
> // Re-assert CS to continue the transfer
> SPI_CS_ASSERT;
>
> // The write command needs an additional 8 clock cycles before the
> block write is started
> spi_rcv_byte( );
>
> // Kick off the transfer by sending the first byte, the "start
> block" token
> SD_UTXBUF = SD_TOK_WRITE_STARTBLOCK;
>
> //Write the block
> for( block_count= 0; block_count < SD_BLOCKSIZE; ++block_count )
> spi_send_byte( secBuf[ block_count ] );
>
> //need to pad some clocks or get Erase_Seq_Error
> // on the next write, and a rcv_byte...
> sd_delay( 4 );
>
> //TODO ? might get hung here? Or should this be left for next op?
> for( ; spi_rcv_byte( ) != 0xFF; )
> ;
>
> return true;
> }
>
> bool sd_send_command( WORD cmd, DWORD arg )
> {
> volatile int i;
> int rpos;
> unsigned char tmp= 0;
>
> SPI_CS_ASSERT;
> spi_send_byte( cmd >> 8 );
>
> for( i= 3; i >= 0 ; --i )
> spi_send_byte( ( (BYTE*)&arg )[ i ] );
>
> // This is the CRC. It only matters what we put here for the first
> //command. Otherwise, the CRC is ignored for SPI mode unless we
> //enable CRC checking.
> spi_send_byte( 0x95 );
>
> rpos= (BYTE)cmd - 1;
>
> /* Wait for a response. A response can be recognized by the
> start bit (a zero) */
> for( i= 0; i < SD_CMD_TIMEOUT; ++i )
> {
> tmp= spi_rcv_byte( );
> if( !( tmp & 0x80 ) )
> break;
> }
> /* Just bail if we never got a response */
> if( i >= SD_CMD_TIMEOUT )
> {
> SPI_CS_DEASSERT;
> return false;
> }
> for(; rpos >= 0; --rpos )
> {
> response[ rpos ]= tmp;
> tmp= spi_rcv_byte( );
> }
>
> SPI_CS_DEASSERT;
> return true;
> }
>
> void sd_delay( BYTE number )
> {
> //clocks out idle bytes...
> for( ; number--; )
> spi_send_byte( 0xFF );
> }
>
> spi.h~~~~~~~~~~~~~~~~~~
> #define SS_PORT_DIR P5DIR
> #define SS_PORT_OUT P5OUT
> #define SS_PIN BIT4
>
> #define SPI_PORT_SEL P5SEL
> #define SPI_PINS_SEL ( BIT1 | BIT2 | BIT3 )
>
> //defines that set which port
> #define SD_UCTL U1CTL
> #define SD_UTCTL U1TCTL
> #define SD_UBR0 U1BR0
> #define SD_UBR1 U1BR1
> #define SD_UMCTL U1MCTL
> #define SD_MOD_REG ME2
> #define SD_USPIE USPIE1
> #define SPI_IFG IFG2
> #define SD_UTXIFG UTXIFG1
> #define SD_URXIFG URXIFG1
> #define SD_UTXBUF U1TXBUF
> #define SD_URXBUF U1RXBUF
> #define SD_IE IE2
> #define SD_URXIE URXIE1
>
> // ...........................
> void spi_initialize( );
> void spi_set_divisor( WORD divisor );
> void spi_send_byte( BYTE input );
> BYTE spi_rcv_byte( );
> void spi_enable( );
> void spi_disable( );
>
> #define SPI_CS_ASSERT SS_PORT_OUT&= ~SS_PIN
> #define SPI_CS_DEASSERT SS_PORT_OUT|= SS_PIN
>
> sdi.c~~~~~~~~~~~~~~~~~~~~~~~~
> #include "Device_430.h"
> #include "spi.h"
>
> /* Initialize and enable the SPI module */
> void spi_initialize( )
> {
> SPI_PORT_SEL = SPI_PINS_SEL; // Setup Px for SPI mode
> SS_PORT_DIR|= SS_PIN; // Setup the SS as output
> // low. So, initialize it high.
> SS_PORT_OUT|= SS_PIN; // Set SS High
>
> SD_UCTL= (CHAR | SYNC | MM | SWRST); // 8-bit, SPI, Master
> SD_UTCTL= (SSEL1 | STC | CKPH); // Normal polarity, 3-wire
> SD_UBR0= 0x002; // SPICLK = SMCLK/2 (2=Minimum divisor)
> SD_UBR1= 0x000;
> SD_UMCTL= 0x000;
> SD_MOD_REG|= SD_USPIE; // Module enable
> SD_UCTL&= ~SWRST; // SPI enable
> }
>
> /* Set the baud-rate divisor. The correct value is computed by dividing
> the clock rate by the desired baud rate. The minimum divisor allowed
> is 2. */
> void spi_set_divisor( WORD divisor )
> {
> SD_UCTL|= SWRST; // Temporarily disable the SPI module
> SD_UBR1= divisor >> 8;
> SD_UBR0= divisor;
> SD_UCTL&= ~SWRST; // Re-enable SPI
> }
>
> /* Send a single byte over the SPI port */
> void spi_send_byte( BYTE input )
> {
> SPI_IFG&= ~SD_UTXIFG;
> /* Send the byte */
> SD_UTXBUF= input;
> /* Wait for the byte to be sent */
> while ( ! ( SPI_IFG & SD_UTXIFG ) )
> ;
> }
>
> /* Receive a byte. Output an 0xFF (the bus idles high) to receive the
> byte */
> BYTE spi_rcv_byte( )
> {
> unsigned char tmp;
> SPI_IFG&= ~SD_URXIFG;
>
> /* Send the byte */
> SD_UTXBUF= 0xFF;
>
> /* Wait for the byte to be received */
> while( ! ( SPI_IFG & SD_URXIFG ) )
> ;
> tmp= SD_URXBUF;
> return tmp;
> }
>

Ah okay, that makes sense. I was wondering about that but didn't have the history to know!

I have since made the change and it now compiles fine and runs. However, when running it gets to the initialization:
mmcInit()

Which calls:
CS_HIGH()

Which is:
#define CS_HIGH() while(!halSPITXDONE); MMC_CS_PxOUT |= MMC_CS

Which is:
#define halSPITXDONE (UCA0STAT&UCBUSY) /* Wait for TX to finish */
However, as I watch the registers in IAR in debug mode,
UCA0STAT___SPI = 0x00

Thus:
UCBUSY = 0

However, it will not proceed past CS_HIGH, it just continues to loop there.

At this point I feel like I have done so much searching and reading that I am spinning a little bit. This might be simple but I can't get the clarity I need to figure it out.

Does anyone have a suggestion?

And may I also say that the help here is just tremendous. I really appreciate it.

You're suffering from too many macros IMHO, and it hides the obvious problem.

You have CS_HIGH defined (effectively) as:
While (!(UCA0STAT&UCBUSY));

So you're looping while USCI A0 is not busy.

-Steve

From: m... [mailto:m...] On Behalf Of mastarre
Sent: Thursday, May 27, 2010 12:12 PM
To: m...
Subject: [msp430] Re: Having trouble with SPI for SD Card

Ah okay, that makes sense. I was wondering about that but didn't have the history to know!

I have since made the change and it now compiles fine and runs. However, when running it gets to the initialization:
mmcInit()

Which calls:
CS_HIGH()

Which is:
#define CS_HIGH() while(!halSPITXDONE); MMC_CS_PxOUT |= MMC_CS

Which is:
#define halSPITXDONE (UCA0STAT&UCBUSY) /* Wait for TX to finish */

However, as I watch the registers in IAR in debug mode,
UCA0STAT___SPI = 0x00

Thus:
UCBUSY = 0

However, it will not proceed past CS_HIGH, it just continues to loop there.

At this point I feel like I have done so much searching and reading that I am spinning a little bit. This might be simple but I can't get the clarity I need to figure it out.

Does anyone have a suggestion?

And may I also say that the help here is just tremendous. I really appreciate it.



Steve,

I agree. The example is, in my opinion, WAY overly abstracted. I was planning to reduce it when I got something functional going but didn't want to before hand because it is a TI example and *should* work as written.

I see what you are saying but what I can't figure out is why it was written that way in the first place. That is why I feel like I might have read it somewhere in the past but am not able to think of it now, etc. Remember, this is working directly with a TI example so they wrote it to have that behavior. I am not saying that I am taking that for what it is and not trying to understand it - I am trying very hard to figure out why they did that - but I am having some trouble understanding the behavior that they expect to justify that and, thus, why my device is not behaving that way.

--- In m..., "Hayashi, Steve" wrote:
>
> You're suffering from too many macros IMHO, and it hides the obvious problem.
>
> You have CS_HIGH defined (effectively) as:
> While (!(UCA0STAT&UCBUSY));
>
> So you're looping while USCI A0 is not busy.
>
> -Steve
>
> From: m... [mailto:m...] On Behalf Of mastarre
> Sent: Thursday, May 27, 2010 12:12 PM
> To: m...
> Subject: [msp430] Re: Having trouble with SPI for SD Card
>
> Ah okay, that makes sense. I was wondering about that but didn't have the history to know!
>
> I have since made the change and it now compiles fine and runs. However, when running it gets to the initialization:
> mmcInit()
>
> Which calls:
> CS_HIGH()
>
> Which is:
> #define CS_HIGH() while(!halSPITXDONE); MMC_CS_PxOUT |= MMC_CS
>
> Which is:
> #define halSPITXDONE (UCA0STAT&UCBUSY) /* Wait for TX to finish */
>
> However, as I watch the registers in IAR in debug mode,
> UCA0STAT___SPI = 0x00
>
> Thus:
> UCBUSY = 0
>
> However, it will not proceed past CS_HIGH, it just continues to loop there.
>
> At this point I feel like I have done so much searching and reading that I am spinning a little bit. This might be simple but I can't get the clarity I need to figure it out.
>
> Does anyone have a suggestion?
>
> And may I also say that the help here is just tremendous. I really appreciate it.
>
>
>

The original flag was U0TCTL & TXEPT.
Translated into English, it will return 0 if the TX buffer is NOT empty.

USCI changed that functionality to busy, so that the closest equivalent function returns 0 if the USCI IS empty.

-Steve

From: m... [mailto:m...] On Behalf Of mastarre
Sent: Thursday, May 27, 2010 1:22 PM
To: m...
Subject: [msp430] Re: Having trouble with SPI for SD Card

Steve,

I agree. The example is, in my opinion, WAY overly abstracted. I was planning to reduce it when I got something functional going but didn't want to before hand because it is a TI example and *should* work as written.

I see what you are saying but what I can't figure out is why it was written that way in the first place. That is why I feel like I might have read it somewhere in the past but am not able to think of it now, etc. Remember, this is working directly with a TI example so they wrote it to have that behavior. I am not saying that I am taking that for what it is and not trying to understand it - I am trying very hard to figure out why they did that - but I am having some trouble understanding the behavior that they expect to justify that and, thus, why my device is not behaving that way.

--- In m..., "Hayashi, Steve" wrote:
>
> You're suffering from too many macros IMHO, and it hides the obvious problem.
>
> You have CS_HIGH defined (effectively) as:
> While (!(UCA0STAT&UCBUSY));
>
> So you're looping while USCI A0 is not busy.
>
> -Steve
>
> From: m... [mailto:m...] On Behalf Of mastarre
> Sent: Thursday, May 27, 2010 12:12 PM
> To: m...
> Subject: [msp430] Re: Having trouble with SPI for SD Card
>
> Ah okay, that makes sense. I was wondering about that but didn't have the history to know!
>
> I have since made the change and it now compiles fine and runs. However, when running it gets to the initialization:
> mmcInit()
>
> Which calls:
> CS_HIGH()
>
> Which is:
> #define CS_HIGH() while(!halSPITXDONE); MMC_CS_PxOUT |= MMC_CS
>
> Which is:
> #define halSPITXDONE (UCA0STAT&UCBUSY) /* Wait for TX to finish */
>
> However, as I watch the registers in IAR in debug mode,
> UCA0STAT___SPI = 0x00
>
> Thus:
> UCBUSY = 0
>
> However, it will not proceed past CS_HIGH, it just continues to loop there.
>
> At this point I feel like I have done so much searching and reading that I am spinning a little bit. This might be simple but I can't get the clarity I need to figure it out.
>
> Does anyone have a suggestion?
>
> And may I also say that the help here is just tremendous. I really appreciate it.
>
>
>




Memfault Beyond the Launch