Communicating with W5100 via SPI

The purpose of this article is to explain the process of communication between a microcontroller and Wiznet’s W5100 networking chip. Of the three methods of interfacing with W5100, Serial Peripheral Interface (SPI) is the method of choice at least for all non-commercial work. Basic knowledge of SPI is assumed. The code is written for AVR ATmega16.

The first thing to do is to initialize the SPI.

#define DDR_SS_PORT     DDRD
#define SS_PORT         PORTD
#define SS_PIN          PD2
#define DDR_SPI_PORT    DDRB
#define SPI_PORT        PORTB
#define MISO_PIN        PB6
#define MOSI_PIN        PB5
#define SCK_PIN         PB7

void SpiInit(void)
    DDR_SS_PORT |= (1<<SS_PIN);                   // SS: Slave Select
    DDR_SPI_PORT |= (1<<MOSI_PIN) | (1<<SCK_PIN); // MOSI and SCK

    SPI_PORT |= (1<<MISO_PIN);       // Enabling pull-up register
    SS_PORT |= (1<<SS_PIN);          // Disabling slave

    // Enable, Master. Xtal/4. MSB first. Mode 0.
    SPCR |= (1<<SPE) | (1<<MSTR); } 

Now we come to the real part. Communication with W5100 occurs in 32-bit streams. Each stream takes 4 SPI cycles, one for each byte. The first byte is the opcode. There are only two operations: READ and WRITE. The second and third bytes are the address to the memory position in W5100 which is to be accessed. In case of a WRITE operation, the last byte indicates the value to be written. In case of a READ operation, the last byte has no purpose except to push the desired data out of W5100. The following function simply reads the value at the 16-bit address given in the parameter.

 uint8_t readW5100(uint16_t addr) { 	// Divide address into two bytes 	uint8_t addr0 = (uint8_t)(addr&0x0F); 	uint8_t addr1 = (uint8_t)((addr&0xF0)>>8);

	SS_PORT &= ~(1<<SS_PIN);      // Select slave

	SPDR = 0x0F;                  // 'Read' operation
	while(!(SPSR & (1<<SPIF)));

	SPDR = addr1;                 // Send address
	while(!(SPSR & (1<<SPIF)));
	SPDR = addr0;
	while(!(SPSR & (1<<SPIF)));

	SPDR = 0xFF;                  // Read data
	while(!(SPSR & (1<<SPIF)));

	SS_PORT |= (1<<SS_PIN);       // De-select slave 	return SPDR; } 

A similar function is used for writing the W5100 memory:

 void writeW5100(uint16_t addr, char data) { 	uint8_t addr0 = (uint8_t)(addr&0x0F); 	uint8_t addr1 = (uint8_t)((addr&0xF0)>>8);

	PORTD &= ~(1<<SS);          // Select slave

	SPDR = 0xF0;                // 'Write' operation
	while(!(SPSR & (1<<SPIF)));

	SPDR = addr1;               // Send address
	while(!(SPSR & (1<<SPIF)));
	SPDR = addr0;
	while(!(SPSR & (1<<SPIF)));

	SPDR = data;                // Send data
	while(!(SPSR & (1<<SPIF)));

	PORTD |= (1<<SS);

The motivation for this article comes from the experience that many of the libraries available for W5100 socket programming, including the one provided by Wiznet, do not contain definitions of the elementary functions that are discussed here. The reason given for this is the platform-dependent nature of the functions.

The code given in this post can be helpful for running W5100 from scratch without the use of any library. It can also be helpful for adapting a library to your system in order to make it functional.


2 thoughts on “Communicating with W5100 via SPI

    • Hey Erich,
      Thanks for the comment. I checked out your blog and I have to say that I really admire your work. It has already presented me with a few ideas about dealing with some issues in my future projects.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s