用于ATmega设备的Wiznet W5100库

几个月前我就开始研究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=’\0′; // buffer read is complete, terminate the string

// Increase the S0_RX_RD value, so it point to the next receive
W51_write(sockaddr+W5100_RX_RD_OFFSET, (offaddr & 0xFF00) >> 8); // update RX read offset (MSB)
W51_write(sockaddr+W5100_RX_RD_OFFSET + 1,(offaddr & 0x00FF)); // update LSB

// Now Send the RECV command
W51_write(sockaddr+W5100_CR_OFFSET, W5100_SKT_CR_RECV); // issue the receive command
_delay_us(5); // wait for receive to start

return W5100_OK;
}

unsigned int ReceivedSize(unsigned char sock)
{
unsigned int val;
unsigned int sockaddr;

if (sock >= W5100_NUM_SOCKETS) return 0;
sockaddr = W5100_SKT_BASE(sock); // calc base addr for this socket
val = W51_read(sockaddr+W5100_RX_RSR_OFFSET) & 0xff;
val = (val << 8) + W51_read(sockaddr+W5100_RX_RSR_OFFSET + 1);
return val;
}

/*
* Simple wrapper function for selecting the W5100 device. This function
* allows the library code to invoke a target-specific function for enabling
* the W5100 chip.
*/
void my_select(void)
{
W51_ENABLE;
}

/*
* Simple wrapper function for deselecting the W5100 device. This function
* allows the library code to invoke a target-specific function for disabling
* the W5100 chip.
*/
void my_deselect(void)
{
W51_DISABLE;
}

/*
* my_xchg callback function; exchanges a byte with W5100 chip
*/
unsigned char my_xchg(unsigned char val)
{
SPDR = val;
while (!(SPSR & (1<<SPIF))) ;
return SPDR;
}

/*
* my_reset callback function; force a hardware reset of the W5100 device
*/
void my_reset(void)
{
RESET_PORT |= (1<<RESET_BIT); // pull reset line high
RESET_DDR |= (1<<RESET_BIT); // now make it an output
RESET_PORT &= ~(1<<RESET_BIT); // pull the line low
_delay_ms(5); // let the device reset
RESET_PORT |= (1<<RESET_BIT); // done with reset, pull the line high
_delay_ms(10); // let the chip wake up
}

int main(void)
{
unsigned int sockaddr;
unsigned char mysocket;
unsigned int rsize;

mysocket = 0; // magic number! declare the socket number we will use (0-3)
sockaddr = W5100_SKT_BASE(mysocket); // calc address of W5100 register set for this socket

/*
* Initialize the ATmega644p SPI subsystem
*/
CS_PORT |= (1<<CS_BIT); // pull CS pin high
CS_DDR |= (1<<CS_BIT); // now make it an output

SPI_PORT = SPI_PORT | (1<<PORTB4); // make sure SS is high
SPI_DDR = (1<<PORTB4)|(1<<PORTB5)|(1<<PORTB7); // set MOSI, SCK and SS as output, others as input
SPCR = (1<<SPE)|(1<<MSTR); // enable SPI, master mode 0
SPSR |= (1<<SPI2X); // set the clock rate fck/2

/*
* Load up the callback block, then initialize the Wiznet W5100
*/
my_callbacks._select = &my_select; // callback for selecting the W5100
my_callbacks._xchg = &my_xchg; // callback for exchanging data
my_callbacks._deselect = &my_deselect; // callback for deselecting the W5100
my_callbacks._reset = &my_reset; // callback for hardware-reset of the W5100

W51_register(&my_callbacks); // register our target-specific W5100 routines with the W5100 library
W51_init(); // now initialize the W5100

/*
* Configure the W5100 device to handle PING requests.
* This requires configuring the chip, not a specific socket.
*/
W51_config(&my_cfg); // config the W5100 (MAC, TCP address, subnet, etc

/*
* The main loop. Control stays in this loop forever, processing any received packets
* and sending any requested data.
*/
while (1)
{
switch (W51_read(sockaddr+W5100_SR_OFFSET)) // based on current status of socket…
{
case W5100_SKT_SR_CLOSED: // if socket is closed…
if (OpenSocket(mysocket, W5100_SKT_MR_TCP, HTTP_PORT) == mysocket) // if successful opening a socket…
{
Listen(mysocket);
_delay_ms(1);
}
break;

case W5100_SKT_SR_ESTABLISHED: // if socket connection is established…
rsize = ReceivedSize(mysocket); // find out how many bytes
if (rsize > 0)
{
if (Receive(mysocket, buf, rsize) != W5100_OK) break; // if we had problems, all done
/*
* Add code here to process the payload from the packet.
*
* For now, we just ignore the payload and send a canned HTML page so the client at least
* knows we are alive.
*/
strcpy_P((char *)buf, PSTR(“HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n”));
strcat_P((char *)buf, PSTR(“<html>\r\n<body>\r\n”));
strcat_P((char *)buf, PSTR(“<title>Karl’s W5100 web server (ATmega644p)</title>\r\n”));
strcat_P((char *)buf, PSTR(“<h2>Karl’s ATmega644p web server using Wiznet W5100 chip</h2>\r\n”));
strcat_P((char *)buf, PSTR(“<br /><hr>\r\n”));
if (Send(mysocket, buf, strlen((char *)buf)) == W5100_FAIL) break; // just throw out the packet for now

strcpy_P((char *)buf, PSTR(“This is part 2 of the page.”));
strcat_P((char *)buf, PSTR(“</body>\r\n</html>\r\n”));
if (Send(mysocket, buf, strlen((char *)buf)) == W5100_FAIL) break; // just throw out the packet for now

DisconnectSocket(mysocket);
}
else // no data yet…
{
_delay_us(10);
}
break;

case W5100_SKT_SR_FIN_WAIT:
case W5100_SKT_SR_CLOSING:
case W5100_SKT_SR_TIME_WAIT:
case W5100_SKT_SR_CLOSE_WAIT:
case W5100_SKT_SR_LAST_ACK:
CloseSocket(mysocket);
break;
}
}

return 0;
}

关于网络服务器

上面的代码改编自RWB的源代码,因为源代码中有太多的限制因素。基本上这些代码都不解析服务器收到的数据包,它只负责固定和抛送文件。对于确认服务器的工作状态来说,这已经足够好了,但是对于完成一个项目来讲就差得远了。在网页中浏览RWB的代码,查找相似项目中的所有细节,他是怎么解析数据包并且创建网络服务器的。请注意我也删除了RWB源代码中所有USART的例程(但还是无效)。

我已经改编了4个RWB的W5100套接字源代码,你可以直接使用它们。这些代码都使用套接字0进行过测试,但还没经过其他的套接字测试,也没有进行多个套接字同时测试。

以上的代码,加上链接连到库里的,总共有2.4千字节数据量。是的!这就是不超过2.5千字节的ATmega644p网络服务器代码!

为了修改这些代码更好地被你所用,你需要重新编译回调函数(就是上面的main()主函数)来适应你的硬件。同时你也需要修改W5100_CFG的结构项来匹配你的网络;我在上面的my_cfg中有声明。最后一步就是重建并且链接W5100库模块,你应该会比我做得更好。

编译:WIZnet BJ – Ben

原文:Wiznet 5100 library for ATmega devices


了解更多关于W5100的介绍,请给我们留言评论或访问我们的官网。谢谢关注!

你可能感兴趣的:(用于ATmega设备的Wiznet W5100库)