热度 17
2012-11-15 09:45
1033 次阅读|
0 个评论
几个月前我就开始研究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 #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 ); // set up the gateway address W51_write(W5100_GAR + 1, pcfg-gtw_addr ); W51_write(W5100_GAR + 2, pcfg-gtw_addr ); W51_write(W5100_GAR + 3, pcfg-gtw_addr ); _delay_ms(1); W51_write(W5100_SHAR + 0, pcfg-mac_addr ); // set up the MAC address W51_write(W5100_SHAR + 1, pcfg-mac_addr ); W51_write(W5100_SHAR + 2, pcfg-mac_addr ); W51_write(W5100_SHAR + 3, pcfg-mac_addr ); W51_write(W5100_SHAR + 4, pcfg-mac_addr ); W51_write(W5100_SHAR + 5, pcfg-mac_addr ); _delay_ms(1); W51_write(W5100_SUBR + 0, pcfg-sub_mask ); // set up the subnet mask W51_write(W5100_SUBR + 1, pcfg-sub_mask ); W51_write(W5100_SUBR + 2, pcfg-sub_mask ); W51_write(W5100_SUBR + 3, pcfg-sub_mask ); _delay_ms(1); W51_write(W5100_SIPR + 0, pcfg-ip_addr ); // set up the source IP address W51_write(W5100_SIPR + 1, pcfg-ip_addr ); W51_write(W5100_SIPR + 2, pcfg-ip_addr ); W51_write(W5100_SIPR + 3, pcfg-ip_addr ); _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 #include #include #include #include #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 ; 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 #define W51_DISABLE CS_PORT|=(1)) 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 return SPDR; }))) /* * my_reset callback function; force a hardware reset of the W5100 device */ void my_reset(void) { RESET_PORT |= (1 RESET_DDR |= (1 RESET_PORT = ~(1 _delay_ms(5); // let the device reset RESET_PORT |= (1 _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_DDR |= (1);); SPI_PORT = SPI_PORT | (1 SPI_DDR = (1 SPCR = (1 SPSR |= (1);)|(1)|(1); /* * 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 .... 注:由于篇幅较长,完整代码请移步我们的官方博客(http://blog.iwiznet.cn/?p=2103) 关于网络服务器 上面的代码改编自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 WIZnet专注全硬件TCP/IP协议栈,面向嵌入式系统应用,为物联网发展助力,欢迎联系我们,官方博客:blog.iwiznet.cn 官方网站:www.iwiznet.cn