Xmega FatFS SD Initialization Problems

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello, for those of you familiar with interfacing with SD cards using SPI, I am at a loss and need some help.

I am working with an ATxmega128A1 (http://www.atmel.com/dyn/products/tools_card_mcu.asp?tool_id=4506) and am using Chan's FatFS library (http://elm-chan.org/fsw/ff/00index_e.html) in an attempt to interface with an SD card.

For those of you familiar with FatFS, you know that Chan provides sample AVR code for using FatFS on an AVR, and that there are only a few specific functions in this code that are hardware dependent.

I am using FatFS 0.07e (most recent) and have essentially copied my code from Gabriel's FatFS v.0.07a code that he uses for his XMEGA XMultiKit (http://www.gabotronics.com/development-boards/xmega-xmultikit.htm) which uses the same Xmega as me. The link for his code is at the top of that page.

He uses SPI Port E while I am using SPI Port D. I have set up some pin defines as follows:

#define PORTx               PORTD
#define PORTx_INS_bm        PIN2_bm
#define PORTx_WP_bm         PIN3_bm

#define SPIx                SPID
#define SPIx_SS_bm          PIN4_bm
#define SPIx_MOSI_bm        PIN5_bm
#define SPIx_MISO_bm        PIN6_bm
#define SPIx_SCK_bm         PIN7_bm

I will now post my code side by side with Gabriel's code to show you what I have changed. Please note some differences may be due to changes in the updated FatFS.

Gabriel's Port Controls

/* Port Controls  (Platform dependent) */
#define SELECT()	PORTE.OUTCLR = 0x10     // MMC CS = L 
#define	DESELECT()	PORTE.OUTSET = 0x10		// MMC CS = H

#define SD_WP		0x04			    // Write protect switch (PF2)
#define SD_CD		0x02			    // Card detect switch (PE1)

#define	FCLK_SLOW()	SPIE.CTRL = 0x53    // Set slow clock (100k-400k)
#define	FCLK_FAST()	SPIE.CTRL = 0xD0;	// Set fast clock (depends on the CSD)

My Port Conrols

/* Port Controls  (Platform dependent) */
#define CS_LOW()	PORTx.OUTCLR = SPIx_SS_bm	/* MMC CS = L */
#define	CS_HIGH()	PORTx.OUTSET = SPIx_SS_bm	/* MMC CS = H */

#define SOCKPORT	PORTx.IN					/* Socket contact port */
#define SOCKWP		PORTx_WP_bm					/* Write protect switch (PB5) */
#define SOCKINS		PORTx_INS_bm				/* Card detect switch (PB4) */

// SPI clk divides system clock by 128, SPI enable, SPI master
#define	FCLK_SLOW()	SPIx.CTRL = 0x53  			// Set slow clock (100k-400k)
#define	FCLK_FAST()	SPIx.CTRL = 0xD0;			// Set fast clock (depends on the CSD)

Gabriel's Transmit/Receive via SPI

/*-----------------------------------------------------------------------*/
/* Transmit a byte to MMC via SPI  (Platform dependent)                  */
/*-----------------------------------------------------------------------*/
#define xmit_spi(dat) 	SPIE.DATA=(dat); loop_until_bit_is_set(SPIE.STATUS, 7)

/*-----------------------------------------------------------------------*/
/* Receive a byte from MMC via SPI  (Platform dependent)                 */
/*-----------------------------------------------------------------------*/
static BYTE rcvr_spi (void) {
    SPIE.DATA = 0xFF;               // Set sequential mode
	loop_until_bit_is_set(SPIE.STATUS, 7);
	return SPIE.DATA;
}

/* Alternative macro to receive data fast */
#define rcvr_spi_m(dst)	SPIE.DATA = 0xFF; loop_until_bit_is_set(SPIE.STATUS, 7); *(dst)=SPIE.DATA

My Transmit/Receive via SPI

/*-----------------------------------------------------------------------*/
/* Transmit a byte to MMC via SPI  (Platform dependent)                  */
/*-----------------------------------------------------------------------*/

#define xmit_spi(dat) 	SPIx.DATA=(dat); loop_until_bit_is_set(SPIx.STATUS, SPI_IF_bp)



/*-----------------------------------------------------------------------*/
/* Receive a byte from MMC via SPI  (Platform dependent)                 */
/*-----------------------------------------------------------------------*/

static
BYTE rcvr_spi (void)
{
	SPIx.DATA = 0xFF;
	loop_until_bit_is_set(SPIx.STATUS, SPI_IF_bp);
	return SPIx.DATA;
}

/* Alternative macro to receive data fast */
#define rcvr_spi_m(dst)	SPIx.DATA=0xFF; loop_until_bit_is_set(SPIx.STATUS, SPI_IF_bp); *(dst)=SPIx.DATA

Gabriel's Power Control

/*-----------------------------------------------------------------------*/
/* Power Control  (Platform dependent)                                   */
/*-----------------------------------------------------------------------*/
/* When the target system does not support socket power control, there   */
/* is nothing to do in these functions and chk_power always returns 1.   */
static void power_on (void) {
	for (Timer1 = 2; Timer1; );	/* Wait for approx. 50mS to 100mS */
    SPIE.CTRL = 0x53;           // Enable Master Mode, clkper/128
}

static void power_off (void) {
	SELECT();				/* Wait for card ready */
	wait_ready();
	release_spi();
	Stat |= STA_NOINIT;		/* Set STA_NOINIT */
}

static int chk_power(void) {	/* Socket power state: 0=off, 1=on */
	return 1;
}

The only other setup he does for Port E is in his main():

PORTE.DIR = 0xB9;       // SPI, TX, RX, SD_CD, TEST
PORTE.PIN3CTRL = 0x40;  // Invert TX output
PORTE.OUT = 0x00;

My Power Control

/*-----------------------------------------------------------------------*/
/* Power Control  (Platform dependent)                                   */
/*-----------------------------------------------------------------------*/
/* When the target system does not support socket power control, there   */
/* is nothing to do in these functions and chk_power always returns 1.   */

static
void power_on (void)
{
	PORTx.DIR = 0xB0; // Set SS, SCK, and MOSI as outputs
	PORTx.PIN4CTRL = PORT_OPC_WIREDANDPULL_gc; // Pull up SS
	for (Timer1 = 100; Timer1; );	/* Wait for 30ms */
}

static
void power_off (void)
{
	// dont need it
}

static
int chk_power(void)		/* Socket power state: 0=off, 1=on */
{
	return 1;
}

The problem is that it appears I am getting no response from my SanDisk 2GB SD card when I try to initialize. My code is effectively the same as Gabriel's for this part:

/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive                                                 */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
	BYTE drv		/* Physical drive nmuber (0) */
)
{
	BYTE n, cmd, ty, ocr[4];


	if (drv) return STA_NOINIT;			/* Supports only single drive */
	if (Stat & STA_NODISK) return Stat;	/* No card in the socket */

	power_on();							/* Force socket power on */
	CCPWrite(&CLK.PSCTRL, (CLK_PSADIV_2_gc | CLK_PSBCDIV_1_1_gc)); // 16MHz
	FCLK_SLOW();
	for (n = 10; n; n--) rcvr_spi();	/* 80 dummy clocks */

	ty = 0;
	if (send_cmd(CMD0, 0) == 1) {			/* Enter Idle state */
...and so on...

The problem is that send_cmd(CMD0, 0) is not returning 1 as it should. I have checked my wiring many times and I'm pretty sure it's correct. I feel as if I may be doing something wrong in configuring the SPI pins and/or configuring the system/SPI clock speeds.

I have my Xmega running at 32MHz using this code:

void Clock_Init(void)
{
	OSC.CTRL |= _BV(OSC_RC32MEN_bp);			// turn on 32MHz internal RC oscillator
	while(!(OSC.STATUS & OSC_RC32MRDY_bm));	// wait for it to be ready

	CCP=0xD8;									// allow modification of protected register
	CLK.CTRL |= CLK_SCLKSEL_RC32M_gc;			// change from 2MHz to 32MHz
}

When calling initialize, CCPWrite() (which I took from Gabe's code) presumably divides the system clock in 2, and FCLK_SLOW() should set the SPI clock to the system clock divided by 128.

I have tried various combinations of system clock speed, SPI clock scaling, and SPI pin configuration, but to no avail. I always fail to have send_cmd(CMD0, 0) return 1.

I apologize for making such a long post but I'm just trying to do my best to provide as much relevant information as possible. Hopefully I haven't missed any important parts of my code.

Please let me know what other information is needed to show what is going on. Any and all help will be much appreciated. Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have this sort of setup working. Looking at my code I have it set for Mode 3 but the code I was converting from (ATmega664) was mode 0. I don't remember changing it and have not checked what it should be now. I do remember that I got the scope out before I got it working.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ifor,

Do you mind posting your SPI initialization code along with any relevant disk initialization code so that I might see if I'm missing something?

Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Problem solved.

By chance I found a solution when testing that signals were going to the correct places in my circuit.

When initializing the SPI pins, for some reason I need to set the MISO input as a wired AND with pull-up:

	// init SPI
	PORTD.DIR = 0xB0; // SPID_SS_bm | SPID_MOSI_bm | SPID_SCK_bm
	PORTD.PIN6CTRL = PORT_OPC_WIREDANDPULL_gc;
	PORTD.OUT = 0x00;

Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi!. I'm glad that my code is being useful (and thanks to Chan for his FatFS library). Good Luck.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

I assume when no pullup resistor is placed on the PCB, the MISO pin has to be pulled up internally with the extra code line as provided by CrazyDesi

PORTD.PIN6CTRL = PORT_OPC_WIREDANDPULL_gc;

I have a problem to port the code for the xmega64a3; I'm

    not a software specialist to make a workaround. So could someone provide me some testcode so i can check the FATFS library is working well on the AVRXPLAIN board(ATxmega128a1) or how i can convert the library for the xmega64a3 on my st600board. I've tried to debug with JTAG but Avrstudio always hangs.

    Many thanks in advance

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hey
i am kaushal panchal and working on the interface xmega to sd card i want the code that you have written if you dont have a problem with that. i am having troubles interfacing it. i can use your code for problem solving and having a refrence.
thanking you
kaushal panchal

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello all, 

Same problem, send_cmd(CMD0, 0 is not returning a 1. I have enabled Pullup for MISO and it shows 3.3V this line. What should I do? Please suggest. Thank you.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Does FatFs work? If so then just copy what disk_initialize() is doing?

 

(though it does kind of raise the question as to why don't you just use the whole FatFs anyway!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is the disk_initialize() function

 

DSTATUS disk_initialize (void) {
	BYTE n, cmd, ty, ocr[4];

	if (Stat & STA_NODISK) return Stat;	/* No card in the socket */

	power_on();							/* Force socket power on */
    FCLK_SLOW();                        /* Set slow SPI during initialization */

	for (n = 10; n; n--) rcvr_spi();	/* 80 dummy clocks */

	ty = 0;
	if (send_cmd(CMD0, 0) == 1) {			/* Enter Idle state */
		Timer1 = 32;						/* Initialization timeout of 1000 msec */
		if (send_cmd(CMD8, 0x1AA) == 1) {	/* SDHC */
			for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();		/* Get trailing return value of R7 resp */
			if (ocr[2] == 0x01 && ocr[3] == 0xAA) {				/* The card can work at vdd range of 2.7-3.6V */
				while (Timer1 && send_cmd(ACMD41, 1UL << 30));	/* Wait for leaving idle state (ACMD41 with HCS bit) */
				if (Timer1 && send_cmd(CMD58, 0) == 0) {		/* Check CCS bit in the OCR */
					for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
					ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;
				}
			}
		} else {							/* SDSC or MMC */
			if (send_cmd(ACMD41, 0) <= 1) 	{
				ty = CT_SD1; cmd = ACMD41;	/* SDSC */
			} else {
				ty = CT_MMC; cmd = CMD1;	/* MMC */
			}
			while (Timer1 && send_cmd(cmd, 0));			/* Wait for leaving idle state */
			if (!Timer1 || send_cmd(CMD16, 512) != 0)	/* Set R/W block length to 512 */
				ty = 0;
		}
	}
	CardType = ty;
	release_spi();

	if (ty) {			        // Initialization succeded 
		Stat &= ~STA_NOINIT;	// Clear STA_NOINIT
	} else {			        // Initialization failed
		power_off();
	}
    FCLK_FAST();
	return Stat;

Didn't try FatFs separately. in this function send_cmd(CMD0,0) never returns 1. any suggestions please?

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you own a scope? What is actually happening on the SCK/MOSI lines?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Right now not available but yesterday I checked a chunk of clock signals on SCK line.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I see clock signal of 250KHz on SCK line. But still no response from SD.