CURRENT ISSUE

Contests

bottom corner

Feature Article



Issue #208 November 2007
Analog Techniques
iEthernet Bootcamp
Get Started with the W5100
by Fred Eady

Start | WIZnet W5100 | Build A Development Board | WIZnet W5100 Garage Code| Congratulations! | Sources & PDF

WIZnet W5100 GARAGE CODE

From a WIZnet W5100 programmer’s point of view, the W5100 consists of Common registers, Socket registers, TX memory, and RX memory. The W5100’s Common registers consist mostly of W5100 local IP and MAC addressing fields. Also included within the confines of the Common registers are RX and TX memory sizes and PPP/PPPoE parameters. It looks like we will be populating most of the Common registers. So, let’s kill two birds with one stone and use the Common registers to test the PIC18LF8722 driver hardware by writing some basic PIC18LF8722 routines to read and write the W5100’s registers. I’ll use the HI-TECH PICC-18 C compiler in conjunction with MPLAB and a Microchip Technology REAL ICE as my W5100 firmware brewing tools.

About 18 hours later, I returned to write this sentence. I could not get my W5100 to communicate correctly with the PIC18LF8722 to save my life. A cursory look at the W5100 project board didn’t indicate any problems. So, I turned to my C code to see if I could find the bug. As it turns out, my C was fine, but my eyes deceived me. A great number of the PIC18LF8722 W5100 address and data I/O pins simply did not get soldered to the W5100 PCB. I use an industrial hot air reflow machine to mount fine-pitched ICs like the W5100 on a regular basis. I’ve done so many of them that I take the process for granted. Well, this time the reflow machine bit me.

In the meantime, I managed to put some W5100 I/O code together. The official factory W5100 driver code I have is written for AVR devices. So, rather than build my own PIC W5100 include file, I de-Atmeled the factory-supplied W5100 include file. Right now, all I really want from the W5100 include file is the definition code that lays out all of the W5100’s internal register addresses. The W5100 factory include file also contains definitions of all of the W5100 register contents, which I’m sure will come in handy later. I lost a bunch of time chasing my soldering snafu, but I gained some of that time back by Microchip-izing the original AVR include file.

My first official W5100 firmware act was to punch the W5100 into a hardware reset (see Listing 1). Since I went to all of the trouble to fix those W5100 address and data solder joints, I’m going to run in W5100 Direct Bus Interface mode. Running in Direct Bus Interface mode means that I don’t have to touch the W5100 Mode register, which happens to be the very first W5100 Common register. So, we can run our initial W5100/PIC18LF8722 I/O test on the set of Gateway Address registers at address range 0x0001:0x0004. The Gateway Address register addresses GAR0:GAR3 are defined in the include file I converted, which I renamed w5100_pic.h. As you can see in Listing 1, I put together some basic PIC18LF8722 I/O routines to read and write the W5100 registers. Then, I wrote the contents of the gwayipaddrc array to the W5100’s Gateway Address Common register set. To make sure I performed the Common register write, I turned around and read the contents of GAR0:GAR3 into an array called svrmacaddrc. You can imagine how pleased I was to see the gateway IP address represented in hexadecimal format in the MPLAB WATCH window shot you see in Photo 3. I tested a bit further by using my W5100 register read routines to read the RTR0 Common register pair, which defaults to 0x07D0 and the RCR Common register that follows and defaults to 0x08. All went well. So, I initialized the W5100’s gateway, MAC address, subnet mask, and IP address Common registers.

Photo 3—With the success of reading back what I stored in the WIZnet gateway IP address register, we’ve established a base of operations for reading and writing the W5100’s internal registers.

The next step on our way to putting the W5100 project board online involves setting up and defining the socket memory information. We’ll use the default of 2-KB-per-socket sizing, which means we don’t touch the RMSR (RX memory size) and TMSR (TX memory size) default register values (0x55). As you can see in Listing 2, all we are really doing is establishing the receive and transmit memory boundaries for each of the four sockets that the W5100 supports. With the socket memory allocation task behind us, we can concentrate on what it takes to manipulate a W5100 socket. 

The topmost portion of Listing 3 is the code we will execute to open a W5100 UDP socket. The first order of business is to tell the W5100 what type of socket we want to work with. We are working with UDP at the moment. So, I loaded the socket 0 Mode register with a UDP socket value. I already have an application (EDTP Internet test panel) that will send ASCII characters to well-known port 7 and, as you can see in Listing 3, I’ve loaded the socket 0 Source Port register with 0x0007. We’ve already loaded our IP and MAC information. Thus, the addition of the UDP source port value enables us to open a UDP socket. From the proliferation of zeros in the Listing 3 socket initialization code, it should be obvious that we will open the W5100’s socket 0 in UDP mode.

Once the socket comes online, we have the power to send and receive UDP datagrams. There are a couple of ways to sense an incoming UDP datagram. We can poll the socket’s Received Size register or look for the RECV bit in the socket’s Interrupt register. As you can see in the UDP datagram receive code that occupies the center section of Listing 3, I have chosen to use the latter. 

An incoming UDP datagram sets the RECV bit of the socket’s Interrupt register. Our first reaction to this is to clear the RECV bit by writing a “1” to correspond to the RECV bit’s position within the Interrupt register. The W5100 takes care of checksums internally and we, as programmers, never see them in our UDP datagram information. The size of the incoming UDP datagram is automatically posted in the socket’s Receive Size register. Here, we retrieve the contents of the Receive Size register and place the value into the get_size variable. I used the W5100’s datasheet variable names where possible to make it a bit easier for you to compare my W5100 driver code with the UDP pseudocode flow example in the W5100 datasheet. The receive buffer’s read pointer value is kept in the socket’s Read Pointer register. We will use the read pointer value to form the basis for the variable get_offset, whose value we will combine with the socket’s receive buffer base address to calculate the beginning address of the UDP datagram’s header. The UDP datagram header offered by the W5100 is made up of 4 bytes of destination IP address, 2 bytes of destination port address, and 2 bytes of data size information. Thus, the header_size variable value is eight. Once all of the addressing calculations have been made, we can use our W5100 read register routine to store the data away in the PIC18LF8722’s SRAM for later.

Logically, what is not header information must be data information because we are protected from checksums by the W5100 architecture. With that, we can deduce that the udp_data_size variable will contain the number of data bytes we need to retrieve and store. Again, using our home-brewed W5100 I/O code, we read the data from the W5100 receive buffer memory and store it in the appropriate PIC18LF8722 SRAM locations. Our absorption of the UDP datagram and its header is complete. We end our receive session by issuing the RECV command in the socket’s Command register, which updates the receive buffer pointers. For those of you who are following along with the pseudocode flow in the W5100 datasheet, note that the udp_data_size variable is not a W5100 datasheet variable. It’s a Fred variable.

Sending a UDP datagram is very similar to receiving one. We’ll reuse the information we received earlier and bounce a UDP message back at the sender. Recall that our received UDP datagram header contained a destination IP address and a destination UDP port value. We thought ahead and stored both of the header values. Now all we have to do is retrieve them from the PIC18LF8722’s SRAM and load them into the proper W5100 Socket registers. I begin my UDP datagram transmission in that manner within the bottom portion of the code in Listing 3. Using the socket’s transmit buffer write pointer, I calculate where in the W5100 transmit buffer to begin stuffing the data I wish to transmit. I then transfer the previously stored UDP datagram data from the PIC18LF8722’s SRAM into the W5100’s transmit buffer. The W5100 will transmit the data located between the socket’s transmit read pointer and transmit write pointer. So, I must update the transmit write pointer by increasing it by the number of bytes I need to transmit. Once that’s done, I issue the SEND command and wait for the send success signal, which is a cleared socket Command register. I can now issue a CLOSE command in the socket’s Command register to close the socket or send or receive another UDP datagram.

Previous | Next

 


bottom corner