TinyFAT Library

For the SPI to work, pin 10 (Uno, etc), or pin 53 (Mega), must be set to output, whatever pin you eventually assign to SS. If this is done, you should be able to use pins 8 or 9, as long as they are also set as outputs. As for using other pins as SS, or debugging, keep reading.

TinyFAT uses an auxiliary file called mmc.cpp. This is where the SS initialization and SPI handshaking occur. For my Mega application, I found that it was reassigning the SS pin, but not correctly setting the DDR register associated with the new pin's port. This is not very complicated. Each physical I/O pin on the Arduino belongs to a port (PORTA,PORTB,PORTC,...) and each port has a data direction register associated with it (DDRA, DDRB, ...).

mmc.cpp does its SPI protocol stuff by port manipulation. It needs to know 3 things at initialization: The SS pin, the PORT associated with the SS pin, the DDR for that port. For the most Arduinos except the Mega, this defaults to pin 10, PORTB, DDRB. Pins 8 and 9 should also be PORTB and DDRB. For the Mega, the default SS is pin 53, PORTB, DDRB. Other pins may have other ports and DDR's. But you can use any other pin available if you just make sure mmc.cpp is using the correct 3 parameters for its calls. I was able to do this for the Mega by cleaning up mmc.cpp.

Here are the relevant parts of mmc.cpp that I changed. I have set it to use pin 42 as the default SS for the Mega, but it can be set to any pin.

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
	byte SS= 7;  //using PL7 (D42) as SS
	byte SSlegacy = 0;	// bit position of chip select on the legacy SPI port
					// for Mega: PB0 (pin d53), for Uno: PB2 (pin d10) 

	volatile uint8_t *SSPORT = &PORTL;
	volatile uint8_t *SSDDR =  &DDRL;
	#define MOSI	2
	#define MISO	3
	#define SCK	1
#else
    byte SS = 2;
	byte SSlegacy = 2;
	volatile uint8_t *SSPORT = &PORTB;
	volatile uint8_t *SSDDR =  &DDRB;
	#define MOSI	3
	#define MISO	4
	#define SCK        5
#endif

byte mmc::initialize(byte speed) {
  byte  i;
  uint16_t counter;
  uint32_t answer;

  disk_state = DISK_ERROR;

  // setup SPI I/O pins

  PORTB |=  _BV(SCK)  | _BV(SSlegacy) | _BV(MISO); // set SCK, pullup on MISO
  DDRB  |=  _BV(SCK)   | _BV(SSlegacy) | _BV(MOSI); // set SCK/MOSI as output
  DDRB  &= ~_BV(MISO); 
// set MISO as input

  *SSPORT |=  _BV(SS); // set SS
  *SSDDR  |=  _BV(SS) ; // set SS as output

  // setup SPI interface:
  //   interrupts disabled, SPI enabled, MSB first, master mode,
  //   leading edge rising, sample on leading edge, clock = f/4,
  SPCR = B01010000 | speed;

  // Enable SPI double speed mode -> clock = f/8
  //  SPSR = _BV(SPI2X);

  // clear status
  i = SPSR;

  // clear recieve buffer
  i = SPDR;

  SPI_SS_HIGH();

  // Send 80 clks
  for (i=0; i<10; i++) {
    spiTransferByte(0xFF);
  }

  // Reset card
  i = sendCommand(GO_IDLE_STATE, 0, 1);
  if (i != 1) {

    return STA_NOINIT | STA_NODISK;
  }

  counter = 0xffff;
  // According to the spec READ_OCR should work at this point
  // without retries. One of my Sandisk-cards thinks otherwise.
  do {
    // Send CMD58: READ_OCR
    i = sendCommand(READ_OCR, 0, 0);
    if (i > 1) {
      // kills my Sandisk 1G which requires the retries in the first place
      // deselectCard();
    }
  } 
  while (i > 1 && counter-- > 0);

  if (counter > 0) {
    answer = spiTransferLong(0);

    // See if the card likes our supply voltage
    if (!(answer & SD_SUPPLY_VOLTAGE)) {
      // The code isn't set up to completely ignore the card,
      // but at least report it as nonworking
      deselectCard();
      return STA_NOINIT | STA_NODISK;
    }
  }

  // Keep sending CMD1 (SEND_OP_COND) command until zero response
  counter = 0xffff;
  do {
    i = sendCommand(SEND_OP_COND, 1L<<30, 1);
    counter--;
  } 
  while (i != 0 && counter > 0);

  if (counter==0) {
    return STA_NOINIT | STA_NODISK;
  }

  // Send MMC CMD16(SET_BLOCKLEN) to 512 bytes
  i = sendCommand(SET_BLOCKLEN, 512, 1);
  if (i != 0) {
    return STA_NOINIT | STA_NODISK;
  }

  // Thats it!
  disk_state = DISK_OK;
  return RES_OK;
}
void SPI_SS_HIGH(){	*SSPORT |= _BV(SS); }

void SPI_SS_LOW(){	*SSPORT &= ~_BV(SS);}

static void deselectCard(void) {
  // Send 8 clock cycles
  SPI_SS_HIGH();
  spiTransferByte(0xff);
}

byte mmc::initialize(byte speed) {
  byte  i;
  uint16_t counter;
  uint32_t answer;

  disk_state = DISK_ERROR;

  // setup SPI I/O pins

  PORTB |=  _BV(SCK)  | _BV(SSlegacy) | _BV(MISO); // set SCK, pullup on MISO
  DDRB  |=  _BV(SCK)   | _BV(SSlegacy) | _BV(MOSI); // set SCK/MOSI as output
  DDRB  &= ~_BV(MISO); 
// set MISO as input

  *SSPORT |=  _BV(SS); // set SS
  *SSDDR  |=  _BV(SS) ; // set SS as output

  // setup SPI interface:
  //   interrupts disabled, SPI enabled, MSB first, master mode,
  //   leading edge rising, sample on leading edge, clock = f/4,
  SPCR = B01010000 | speed;

  // Enable SPI double speed mode -> clock = f/8
  //  SPSR = _BV(SPI2X);

  // clear status
  i = SPSR;

  // clear recieve buffer
  i = SPDR;

  SPI_SS_HIGH();

  // Send 80 clks
  for (i=0; i<10; i++) {
    spiTransferByte(0xFF);
  }


void mmc::setSSpin(const uint8_t _pin) {
	#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
		if (_pin==53) { 
			SSPORT = &PORTB;
			SSDDR = &DDRB;
			SS=0;
		}
		else if ((_pin>=42) and (_pin<=49)) {
			SSPORT = &PORTL;
			SSDDR = &DDRL;
			SS= 49 - _pin;
		}
	#else	
		if ((_pin>=8) and (_pin<=10))	{ 
				SS=_pin-8;
				SSPORT = &PORTB; 
				SSDDR = &DDRB;
		}
	#endif
	}