几个月前我就开始研究W5100芯片而且非常喜欢它。我建立了一个测试平台,并使用ATmaga644p通过总线连接W5100(在WIZ811MJ开发板上)。
一开始,我从ermicroblog网站上下载并加载代码。这些代码都是由RWB开发的,他开创了一个很好的开端。我尤其是喜欢RWB一步一步演绎一个完整例子的过程,有一行行的代码作为理论基础,没有头文件会丢失,也不需要后台代码作支撑。
我重新编写了RWB的代码,并且创建一个W5100设备库。请注意这个库内的目标是相互独立的。这就意味着你可以链接任何ATmega项目的代码而不需要每次都对库进行重建,因为你的硬件和我的不兼容。这种独立性来自一个叫做W51_register()的成熟调用例程,它允许你通过函数指针设置到库内进行具体的目标测试,比如启用/禁用W5100芯片和通过设备进行数据交换。
W5100库代码
以下是W5100库模块的代码。不像RWB的代码,我的代码使用了一个头文件(源于RWB的代码和微知纳特数据手册的结合);你可以在页面最底部的的压缩包文件看到它。
/*
* w5100.c library of target-independent AVR support routines
* for the Wiznet W5100 Ethernet interface device
*
* This file is derived from the excellent work found here:
* www.ermicro.com/blog/?p=1773
* by RWB. I am leaving the header from the original file intact below,
* but you need to remember the rest of the source here is fairly
* heavily modified. Go to the above site for the original.
*/
/*****************************************************************************
// File Name : wiznetping.c
// Version : 1.0
// Description : Wiznet W5100
// Author : RWB
// Target : AVRJazz Mega168 Board
// Compiler : AVR-GCC 4.3.2; avr-libc 1.6.6 (WinAVR 20090313)
// IDE : Atmel AVR Studio 4.17
// Programmer : AVRJazz Mega168 STK500 v2.0 Bootloader
// : AVR Visual Studio 4.17, STK500 programmer
// Last Updated : 01 July 2010
*****************************************************************************/
/*
* The following code turns the above wiznetping.c source code into a
* generic library of W5100 support routines that are target-independent.
* That is, you build this library for a generic AVR ATmega device, then
* write your application to use the W51_xxx routines below for accessing
* the W5100 chip. Because these routines are target-independent, you
* never have to rebuild them just because you are moving your code from,
* say, a ‘mega128 to an ‘xmega128a1 device.
*
* For this to work properly, your application must provide three target-
* specific functions and must register the addresses of those functions
* with the W5100 library at run-time. These functions are:
*
* select target-specific function for enabling the W5100 chip
* xchg target-specific function for exchanging a byte with the W5100 chip
* deselect target-specific function for disabling the W5100 chip
* reset target-specific function for hardware reset of the W5100 chip
*
* Your application registers these three functions with the W5100 library
* by invoking the W51_register() function. Your application must make this
* call one time and must make this call before calling any other W5100
* functions.
*/
#include <util/delay.h>
#include “w5100.h”
#ifndef FALSE
#define FALSE 0
#define TRUE !FALSE
#endif
/*
* Define the function pointers used to access the SPI port assigned to the
* W5100 device. These pointers will be filled in at run-time when the host
* calls W51_register().
*/
static void (*select)(void) = (void *)0;
static unsigned char (*xchg)(unsigned char val) = (void *)0;
static void (*deselect)(void) = (void *)0;
static void (*reset)(void) = (void *)0;
static unsigned char inited = FALSE;
void W51_register(W5100_CALLBACKS *pcallbacks)
{
select = pcallbacks->_select;
xchg = pcallbacks->_xchg;
deselect = pcallbacks->_deselect;
reset = pcallbacks->_reset;
inited = FALSE;
if ((select) && (xchg) && (deselect)) inited = TRUE; // these functions must be valid
}
void W51_write(unsigned int addr, unsigned char data)
{
if (!inited) return; // not set up, ignore request
select(); // enable the W5100 chip
xchg(W5100_WRITE_OPCODE); // need to write a byte
xchg((addr & 0xff00) >> 8); // send MSB of addr
xchg(addr & 0xff); // send LSB
xchg(data); // send the data
deselect(); // done with the chip
}
unsigned char W51_read(unsigned int addr)
{
unsigned char val;
if (!inited) return 0; // not set up, ignore request
select(); // enable the W5100 chip
xchg(W5100_READ_OPCODE); // need to read a byte
xchg((addr & 0xff00) >> 8); // send MSB of addr
xchg(addr & 0xff); // send LSB
val = xchg(0×00); // need to send a dummy char to get response
deselect(); // done with the chip
return val; // tell her what she’s won
}
void W51_init(void)
{
if (reset) reset(); // if host provided a reset function, use it
else W51_write(W5100_MR, W5100_MR_SOFTRST); // otherwise, force the w5100 to soft-reset
_delay_ms(1);
}
unsigned char W51_config(W5100_CFG *pcfg)
{
if (pcfg == 0) return W5100_FAIL;
W51_write(W5100_GAR + 0, pcfg->gtw_addr[0]); // set up the gateway address
W51_write(W5100_GAR + 1, pcfg->gtw_addr[1]);
W51_write(W5100_GAR + 2, pcfg->gtw_addr[2]);
W51_write(W5100_GAR + 3, pcfg->gtw_addr[3]);
_delay_ms(1);
W51_write(W5100_SHAR + 0, pcfg->mac_addr[0]); // set up the MAC address
W51_write(W5100_SHAR + 1, pcfg->mac_addr[1]);
W51_write(W5100_SHAR + 2, pcfg->mac_addr[2]);
W51_write(W5100_SHAR + 3, pcfg->mac_addr[3]);
W51_write(W5100_SHAR + 4, pcfg->mac_addr[4]);
W51_write(W5100_SHAR + 5, pcfg->mac_addr[5]);
_delay_ms(1);
W51_write(W5100_SUBR + 0, pcfg->sub_mask[0]); // set up the subnet mask
W51_write(W5100_SUBR + 1, pcfg->sub_mask[1]);
W51_write(W5100_SUBR + 2, pcfg->sub_mask[2]);
W51_write(W5100_SUBR + 3, pcfg->sub_mask[3]);
_delay_ms(1);
W51_write(W5100_SIPR + 0, pcfg->ip_addr[0]); // set up the source IP address
W51_write(W5100_SIPR + 1, pcfg->ip_addr[1]);
W51_write(W5100_SIPR + 2, pcfg->ip_addr[2]);
W51_write(W5100_SIPR + 3, pcfg->ip_addr[3]);
_delay_ms(1);
W51_write(W5100_RMSR, 0×55); // use default buffer sizes (2K bytes RX and TX for each socket
W51_write(W5100_TMSR, 0×55);
return W5100_OK; // everything worked, show success
}
使用库
下面来教你使用这个库。创建一个新的AVRStudio4项目,并且在你的项目(记得修改你的项目配置中文件夹的大小,以便能放得下W5100.h文件)里添加W5100.h头文件。同时修改你的项目配置来添加W5100库,以便你在建立项目时连接器能找到它。
在你的资源库中,你需要写下三个(也可以是4个)具体目标例程。这三个必需的例程可以实现通过总线访问电路板中的W5100芯片。select()例程用来选择端口线与W5100设备进行连接。deselect()例程的作用是取消对W5100设备的选择。xchg()例程的作用是,每当通过总线向W5100发送一个字节的数据,会同时向调用例程返回一个字节的数据。有时候,你可能还会用到reset()例程,它的作用是使端口线重置W5100设备。我真心建议你将来在硬件设置中使用它,因为有时在W5100启动时会遇到一些故障,到时候你对它重置,就省事多了。
项目案例(网络服务器)
以下代码是使用库建立的网络服务器案例。
#include <avr/io.h>
#include <string.h>
#include <stdio.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include “w5100.h”
unsigned char OpenSocket(unsigned char sock, unsigned char eth_protocol, unsigned int tcp_port);
void CloseSocket(unsigned char sock);
void DisconnectSocket(unsigned char sock);
unsigned char Listen(unsigned char sock);
unsigned char Send(unsigned char sock, const unsigned char *buf, unsigned int buflen);
unsigned int Receive(unsigned char sock, unsigned char *buf, unsigned int buflen);
unsigned int ReceivedSize(unsigned char sock);
void my_select(void);
void my_deselect(void);
unsigned char my_xchg(unsigned char val);
void my_reset(void);
#define MAX_BUF 512 /* largest buffer we can read from chip */
#define HTTP_PORT 80 /* TCP port for HTTP */
/*
* Ethernet setup
*
* Define the MAC address, IP address, subnet mask, and gateway
* address for the target device.
*/
W5100_CFG my_cfg =
{
{0×00,0×16,0×36,0xDE,0×58,0xF6}, // mac_addr
{192,168,1,233}, // ip_addr
{255,255,255,0}, // sub_mask
{192,168,1,1} // gtw_addr
};
/*
* Callback function block
*
* Define callback functions for target-independent support of the
* W5100 chip. Here is where you store pointers to the various
* functions needed by the W5100 library code. These functions all
* handle tasks that are target-dependent, which means the library
* code can be target-INdependent.
*/
W5100_CALLBACKS my_callbacks;
unsigned char buf[MAX_BUF];
unsigned char OpenSocket(unsigned char sock, unsigned char eth_protocol, unsigned int tcp_port)
{
unsigned char retval;
unsigned int sockaddr;
retval = W5100_FAIL; // assume this doesn’t work
if (sock >= W5100_NUM_SOCKETS) return retval; // illegal socket value is bad!
sockaddr = W5100_SKT_BASE(sock); // calc base addr for this socket
if (W51_read(sockaddr+W5100_SR_OFFSET) == W5100_SKT_SR_CLOSED) // Make sure we close the socket first
{
CloseSocket(sock);
}
W51_write(sockaddr+W5100_MR_OFFSET ,eth_protocol); // set protocol for this socket
W51_write(sockaddr+W5100_PORT_OFFSET, ((tcp_port & 0xFF00) >> 8 )); // set port for this socket (MSB)
W51_write(sockaddr+W5100_PORT_OFFSET + 1, (tcp_port & 0x00FF)); // set port for this socket (LSB)
W51_write(sockaddr+W5100_CR_OFFSET, W5100_SKT_CR_OPEN); // open the socket
while (W51_read(sockaddr+W5100_CR_OFFSET)) ; // loop until device reports socket is open (blocks!!)
if (W51_read(sockaddr+W5100_SR_OFFSET) == W5100_SKT_SR_INIT) retval = sock; // if success, return socket number
else CloseSocket(sock); // if failed, close socket immediately
return retval;
}
void CloseSocket(unsigned char sock)
{
unsigned int sockaddr;
if (sock > W5100_NUM_SOCKETS) return; // if illegal socket number, ignore request
sockaddr = W5100_SKT_BASE(sock); // calc base addr for this socket
W51_write(sockaddr+W5100_CR_OFFSET, W5100_SKT_CR_CLOSE); // tell chip to close the socket
while (W51_read(sockaddr+W5100_CR_OFFSET)) ; // loop until socket is closed (blocks!!)
}
void DisconnectSocket(unsigned char sock)
{
unsigned int sockaddr;
if (sock > W5100_NUM_SOCKETS) return; // if illegal socket number, ignore request
sockaddr = W5100_SKT_BASE(sock); // calc base addr for this socket
W51_write(sockaddr+W5100_CR_OFFSET, W5100_SKT_CR_DISCON); // disconnect the socket
while (W51_read(sockaddr+W5100_CR_OFFSET)) ; // loop until socket is closed (blocks!!)
}
unsigned char Listen(unsigned char sock)
{
unsigned char retval;
unsigned int sockaddr;
retval = W5100_FAIL; // assume this fails
if (sock > W5100_NUM_SOCKETS) return retval; // if illegal socket number, ignore request
sockaddr = W5100_SKT_BASE(sock); // calc base addr for this socket
if (W51_read(sockaddr+W5100_SR_OFFSET) == W5100_SKT_SR_INIT) // if socket is in initialized state…
{
W51_write(sockaddr+W5100_CR_OFFSET, W5100_SKT_CR_LISTEN); // put socket in listen state
while (W51_read(sockaddr+W5100_CR_OFFSET)) ; // block until command is accepted
if (W51_read(sockaddr+W5100_SR_OFFSET) == W5100_SKT_SR_LISTEN) retval = W5100_OK; // if socket state changed, show success
else CloseSocket(sock); // not in listen mode, close and show an error occurred
}
return retval;
}
unsigned char Send(unsigned char sock, const unsigned char *buf, unsigned int buflen)
{
unsigned int ptr;
unsigned int offaddr;
unsigned int realaddr;
unsigned int txsize;
unsigned int timeout;
unsigned int sockaddr;
if (buflen == 0 || sock >= W5100_NUM_SOCKETS) return W5100_FAIL; // ignore illegal requests
sockaddr = W5100_SKT_BASE(sock); // calc base addr for this socket
// Make sure the TX Free Size Register is available
txsize = W51_read(sockaddr+W5100_TX_FSR_OFFSET); // make sure the TX free-size reg is available
txsize = (((txsize & 0x00FF) << 8 ) + W51_read(sockaddr+W5100_TX_FSR_OFFSET + 1));
timeout = 0;
while (txsize < buflen)
{
_delay_ms(1);
txsize = W51_read(sockaddr+W5100_TX_FSR_OFFSET); // make sure the TX free-size reg is available
txsize = (((txsize & 0x00FF) << 8 ) + W51_read(sockaddr+W5100_TX_FSR_OFFSET + 1));
if (timeout++ > 1000) // if max delay has passed…
{
DisconnectSocket(sock); // can’t connect, close it down
return W5100_FAIL; // show failure
}
}
// Read the Tx Write Pointer
ptr = W51_read(sockaddr+W5100_TX_WR_OFFSET);
offaddr = (((ptr & 0x00FF) << 8 ) + W51_read(sockaddr+W5100_TX_WR_OFFSET + 1));
while (buflen)
{
buflen–;
realaddr = W5100_TXBUFADDR + (offaddr & W5100_TX_BUF_MASK); // calc W5100 physical buffer addr for this socket
W51_write(realaddr, *buf); // send a byte of application data to TX buffer
offaddr++; // next TX buffer addr
buf++; // next input buffer addr
}
W51_write(sockaddr+W5100_TX_WR_OFFSET, (offaddr & 0xFF00) >> 8); // send MSB of new write-pointer addr
W51_write(sockaddr+W5100_TX_WR_OFFSET + 1, (offaddr & 0x00FF)); // send LSB
W51_write(sockaddr+W5100_CR_OFFSET, W5100_SKT_CR_SEND); // start the send on its way
while (W51_read(sockaddr+W5100_CR_OFFSET)) ; // loop until socket starts the send (blocks!!)
return W5100_OK;
}
/*
* Define the SPI port, used to exchange data with a W5100 chip.
*/
#define SPI_PORT PORTB /* target-specific port containing the SPI lines */
#define SPI_DDR DDRB /* target-specific DDR for the SPI port lines */
#define CS_DDR DDRD /* target-specific DDR for chip-select */
#define CS_PORT PORTD /* target-specific port used as chip-select */
#define CS_BIT 2 /* target-specific port line used as chip-select */
#define RESET_DDR DDRD /* target-specific DDR for reset */
#define RESET_PORT PORTD /* target-specific port used for reset */
#define RESET_BIT 3 /* target-specific port line used as reset */
/*
* Define macros for selecting and deselecting the W5100 device.
*/
#define W51_ENABLE CS_PORT&=~(1<<CS_BIT)
#define W51_DISABLE CS_PORT|=(1<<CS_BIT)
unsigned int Receive(unsigned char sock, unsigned char *buf, unsigned int buflen)
{
unsigned int ptr;
unsigned int offaddr;
unsigned int realaddr;
unsigned int sockaddr;
if (buflen == 0 || sock >= W5100_NUM_SOCKETS) return W5100_FAIL; // ignore illegal conditions
if (buflen > (MAX_BUF-2)) buflen = MAX_BUF – 2; // requests that exceed the max are truncated
sockaddr = W5100_SKT_BASE(sock); // calc base addr for this socket
ptr = W51_read(sockaddr+W5100_RX_RD_OFFSET); // get the RX read pointer (MSB)
offaddr = (((ptr & 0x00FF) << 8 ) + W51_read(sockaddr+W5100_RX_RD_OFFSET + 1)); // get LSB and calc offset addr
while (buflen)
{
buflen–;
realaddr = W5100_RXBUFADDR + (offaddr & W5100_RX_BUF_MASK);
*buf = W51_read(realaddr);
offaddr++;
buf++;
}
*buf=’