Arduino Playground is read-only starting December 31st, 2018. For more info please look at this Forum Post

Using the SD-Slot on the EthernetShield with Hardware SPI

The Problem

The SD-Slot on the EthernetShield is not officially supported. So there's no driver. Most available SD-SPI Drivers rely on the Hardware SPI Pins, which are Pins 10 to 13.

Temporary Solution

On the Ethernetshield the SD-Port is wired as follows.

Arduino PinSD SPI-ModeSD 4Wire Mode
3MISODAT0
4 DAT1
5 DAT2
6CSDAT3
8MOSICMD
9CLKCLK

The reason why they chose this way is unknown.

One solution to this is to rewire the SD-Slot to use the Hardware-SPI pins. The Picture shows how you can reroute the Pins.

Since SPI is a Bus we have to select which device is active. So for Chipselect Pin 6 is still used because the CS on Pin 10 is needed for the ethernet Chip. MISO, MOSI and CLK are now on PIN 11-13.

So wiring is as follows:

Arduino PinRerouted to PinSD SPI-Mode
312MISO
66CS
811MOSI
913CLK

For the Wire on Pin 8 see my Addition at the End.

Software

Now you should be able to use most of the available SD Libraries. But you have to change the CS Pin in the Code to Pin 6, since the default CS Pin is used for the Ethernet Device.

Permanent Solution

The upper Solution works fine but has the Problem that it still uses all Pins 3 to 6, 8 and 9 which is kind of a waste. Since I needed the Shield for a project where I need as many free ports as possible I decided to build a permanent solution. The Picture below shows the rewiring I did. So the only additionally used Port is 6 for the CS. Ports 3,4,5 and 9 are available (note the cut paths on those pins). For the Wire on Pin 8 read the text about the Buggy Wiznet at the End of this Article.

Buggy Wiznet SPI

In an Application Note Wiznet states that the SPI Part of their chip is broken, which pollutes the MISO Line when the Chip is not active. This should disable other Devices from using the SPI Bus. Their proposed solution is to disable the Chips SPI Part when not in use.

There is a Pin called SPI_EN on the Wiznet chip which is connected to the lower Pad of the Pads labeled "PROG" on the EthernetShield. So I connected this Pin to Pin 7 in the temporary solution and Pin 8 in the permanent solution.

I did a little change to the Ethernet Library, so the SPI Part is only active when Ethernet is used.

In libraries/Ethernet/utilty/spi.h find the following after

#define SPI0_SendByte(Data)				SPI0_TxData(Data);SPI0_WaitForSend()
#define SPI0_RecvBute()					SPI0_RxData()

and replace all that follows with this code:

Example

  1. /* Port7
  2.  #define SPI_EN_BIT                  BIT7
  3.  #define SPI_EN_DDR                  DDRD
  4.  #define SPI_EN_PORT                 PORTD
  5. */
  6.  
  7. //Port 8
  8. #define SPI_EN_BIT                  BIT0
  9. #define SPI_EN_DDR                  DDRB
  10. #define SPI_EN_PORT                 PORTB
  11.  
  12. // PB4(MISO), PB3(MOSI), PB5(SCK), PB2(/SS)         // CS=1, waiting for SPI start // SPI mode 0, 4MHz
  13. #define SPI0_Init()                                             DDRB  |= SPI0_SS_BIT|SPI0_SCLK_BIT|SPI0_MOSI_BIT;\
  14.                                                                                 PORTB |= SPI0_SS_BIT; PORTB &= ~(SPI0_SCLK_BIT|SPI0_MOSI_BIT);\
  15.                                                                                 SPCR  = 0x50; SPI_EN_DDR |= SPI_EN_BIT
  16. //-----------------------------------------------------------------------------
  17.  
  18. //-----------------------------------------------------------------------------
  19. //IInChip SPI HAL
  20. #define IINCHIP_SpiInit                                 SPI0_Init
  21. #define IINCHIP_SpiSendData                             SPI0_SendByte  
  22. #define IINCHIP_SpiRecvData                             SPI0_RxData
  23.  
  24.  
  25. #define IINCHIP_CS_BIT                                  BIT2
  26. #define IINCHIP_CS_DDR                                  DDRB
  27. #define IINCHIP_CS_PORT                                 PORTB
  28.  
  29. #define IINCHIP_CSInit()      (IINCHIP_CS_DDR |= IINCHIP_CS_BIT); (SPI_EN_DDR |= SPI_EN_BIT)
  30. #define IINCHIP_CSon()        (IINCHIP_CS_PORT |= IINCHIP_CS_BIT); (SPI_EN_PORT &= ~SPI_EN_BIT)
  31. #define IINCHIP_CSoff()       (SPI_EN_PORT |= SPI_EN_BIT);(IINCHIP_CS_PORT &= ~IINCHIP_CS_BIT)