UIP成功移植到CC2530上
来源:互联网 发布:spring sql注入拦截器 编辑:程序博客网 时间:2024/05/21 03:26
最近老板有个项目,其中要做一个Zigbee的无线接入点,即将ZigBee无线传感网络中的数据通过TCP/IP协议传输到以太网上。传统的这种无线接入点即网关都是上位机加下位机模式做成的,即主控芯片(如ARM)加无线模块(如cc2530),ARM与cc2530通过UARST通信,cc2530建立WSN网络,ARM与PC机通过TCP/IP通信,考虑到此系统的成本以及其中的数据传输量不大,就不用ARM,直接将cc2530做成网关,这就需要将TCP/IP协议栈移植到cc2530上,同时与ZigBee协议栈能很好的协同运行。
因为cc2530的FLASH有256K,Z-stack占用了大部分,所以要用TCP/IP,只能移植一个轻量型网络协议栈,现在比较流行的就是Adam Dunkels写的lwIP和uIP,还有Micrium的uc/IP,lwip和uc/ip所占空间较大,移植较为麻烦,所以就用uip。uip是一种免费可的极小的TCP/IP协议栈,主要实现了ARP,ICMP,TCP,UDP协议,在8位或16位单片机上用的较多,对rom和ram要求很少。
在网上看了一些uip移植到51或STM32的文章,同时也花了两天时间看了uip的实现源码,如果不熟悉TCP/IP协议的话读起来还是很吃力,所以先看看TCP/IP,建议看TCP/IP协议详解——卷一。看完之后大概知道移植过程了。
移植之前先要写网络芯片驱动程序,我用的是enc28j60,独立控制的SPI接口,因为cc2530的spi接口用来下载调试了,另一个spi被串口复用了,所以只用用GPIO模拟SPI。
写驱动程序之前认真读了enc28j60的datasheet,在网上也找到了相关的驱动程序,可以稍加修改拿来用。下面贴出spi程序和enc28j60的程序。
#include "spi.h"void WriteByte(u8_t dat){ u8_t i; for(i=0;i<8;i++) { SCKN = 0; asm("nop"); if(dat&0x80) { SIN = 1; } else SIN = 0; dat <<= 1; asm("nop"); SCKN = 1; asm("nop"); } SCKN=0; //空闲状态为低电平}u8_t ReadByte(void){ u8_t i,dat; SCKN=0; dat1=0; for(i=0;i<8;i++) { SCKN=1; dat1 <<=1; dat1 |= SON; SCKN=0; } return dat;}
spi.h文件定义了与enc28j60spi接口的GPIO,
#ifndef SPI_H#define SPI_H#include <ioCC2530.h>#define SON P0_5 // MISO#define SIN P0_6 // MOSI#define SCKN P0_7 // SCK#define CSN P1_3 // 28J60-- CS#define RESET P1_2 // Reset void WriteByte(u8_t dat);u8_t ReadByte(void);#endif
enc28j60.c文件:
#include "enc28j60.h"#include "spi.h"#define MIN(a,b) (a) < (b) ? (a) : (b)XDATA u8_t Enc28j60Bank;XDATA u16_t NextPacketPtr;void delay_100ns(){ asm("nop"); asm("nop"); asm("nop");}void delay_ms(int t1){ int i; while(t1--) {for(i=10;i;--i) { delay_100ns(); } } }//*******************************************************************************************//// Function : enc28j60ReadOp// Description : ////*******************************************************************************************u8_t enc28j60ReadOp(u8_t op, u8_t address){ u8_t dat1; // activate CS CSN =0; // issue read command delay_100ns(); WriteByte(op | (address & ADDR_MASK)); dat1 = ReadByte(); // do dummy read if needed (for mac and mii, see datasheet page 29) if(address & 0x80) dat1 = ReadByte(); // release CS CSN=1; return(dat1);}//*******************************************************************************************//// Function : enc28j60WriteOp// Description : ////*******************************************************************************************void enc28j60WriteOp(u8_t op, u8_t address, u8_t mydat){ CSN=0; // issue write command delay_100ns(); WriteByte( op | (address & ADDR_MASK)); // write data WriteByte(mydat); CSN=1; delay_100ns();}//*******************************************************************************************//// Function : icmp_send_request// Description : Send ARP request packet to destination.////*******************************************************************************************void enc28j60SetBank(u8_t address){ // set the bank (if needed) if((address & BANK_MASK) != Enc28j60Bank) { // set the bank enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0)); enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5); Enc28j60Bank = (address & BANK_MASK); }}//*******************************************************************************************//// Function : icmp_send_request// Description : Send ARP request packet to destination.////*******************************************************************************************u8_t enc28j60Read(u8_t address){ // select bank to read enc28j60SetBank(address); // do the read return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address);}//*******************************************************************************************//// Function : icmp_send_request// Description : Send ARP request packet to destination.////*******************************************************************************************void enc28j60Write(u8_t address, u8_t mydat){ // select bank to write enc28j60SetBank(address); // do the write enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, mydat);}//*******************************************************************************************//// Function : icmp_send_request// Description : Send ARP request packet to destination.////*******************************************************************************************u16_t enc28j60_read_phyreg(u8_t address){ u16_t mydat; // set the PHY register address enc28j60Write(MIREGADR, address); enc28j60Write(MICMD, MICMD_MIIRD); // Loop to wait until the PHY register has been read through the MII // This requires 10.24us while( (enc28j60Read(MISTAT) & MISTAT_BUSY) ); // Stop reading 这里应该清零 //enc28j60Write(MICMD, MICMD_MIIRD); enc28j60Write(MICMD, 0x0); // Obtain results and return mydat = enc28j60Read ( MIRDL ); //PrintHex(mydat); //mydat |= enc28j60Read ( MIRDH ); //此地方源代码有误 改成 mydat |= (enc28j60Read ( MIRDH )<<8); // PrintHex(mydat); return mydat;} //*******************************************************************************************//// Function : icmp_send_request// Description : Send ARP request packet to destination.////*******************************************************************************************void enc28j60PhyWrite(u8_t address, u16_t mydat){// set the PHY register address enc28j60Write(MIREGADR, address); // write the PHY data enc28j60Write(MIWRL, mydat & 0x00ff); enc28j60Write(MIWRH, mydat >> 8); // wait until the PHY write completes while(enc28j60Read(MISTAT) & MISTAT_BUSY) { delay_100ns(); }}void enc28j60ReadBuffer(u16_t len, u8_t* dat){ // assert CS CSN = 0; // issue read command delay_100ns(); WriteByte(ENC28J60_READ_BUF_MEM); while(len--) { *dat++ = ReadByte(); } // release CS CSN = 1;}void enc28j60WriteBuffer(u16_t len, u8_t* dat){ // assert CS CSN = 0; // issue write command WriteByte(ENC28J60_WRITE_BUF_MEM); // while(!(SPSR & (1<<SPIF))); while(len--) { WriteByte(*dat++); } // release CS CSN = 1;}#define ETHERNET_MIN_PACKET_LENGTH0x3C#define ETHERNET_HEADER_LENGTH0x0E#define IP_TCP_HEADER_LENGTH 40#define TOTAL_HEADER_LENGTH (IP_TCP_HEADER_LENGTH+ETHERNET_HEADER_LENGTH)void enc28j60PacketSend(u16_t len, u8_t* packet){ // Set the write pointer to start of transmit buffer area enc28j60Write(EWRPTL, TXSTART_INIT); enc28j60Write(EWRPTH, TXSTART_INIT>>8); // Set the TXND pointer to correspond to the packet size given enc28j60Write(ETXNDL, (TXSTART_INIT+len)); enc28j60Write(ETXNDH, (TXSTART_INIT+len)>>8); // write per-packet control byte enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); // TODO, fix this up if( uip_len <= TOTAL_HEADER_LENGTH ) { // copy the packet into the transmit buffer enc28j60WriteBuffer(len, packet); } else { len -= TOTAL_HEADER_LENGTH; enc28j60WriteBuffer(TOTAL_HEADER_LENGTH, packet); enc28j60WriteBuffer(len, (unsigned char *)uip_appdata); } // send the contents of the transmit buffer onto the network enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);}u16_t enc28j60PacketReceive(u16_t maxlen, u8_t* packet){ u16_t rxstat,len; if (enc28j60Read(EPKTCNT) == 0) { return 0; }// Set the read pointer to the start of the received packet enc28j60Write(ERDPTL, (NextPacketPtr)); enc28j60Write(ERDPTH, (NextPacketPtr)>>8);// read the next packet pointer NextPacketPtr = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); NextPacketPtr |= (enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8); // read the packet length len = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); len |= (enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8); len -= 4; //以太帧最小46字节 减去4字节的FCS校验和 加上帧头14字节 共64字节 //PrintHex(len);// read the receive status rxstat = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;// limit retrieve length // (we reduce the MAC-reported length by 4 to remove the CRC) len = MIN(len, maxlen);// copy the packet from the receive buffer enc28j60ReadBuffer(len, packet);// Errata workaround #13. Make sure ERXRDPT is odd u16_t rs,re; rs = enc28j60Read(ERXSTH); rs <<= 8; rs |= enc28j60Read(ERXSTL); re = enc28j60Read(ERXNDH); re <<= 8; re |= enc28j60Read(ERXNDL); if (NextPacketPtr - 1 < rs || NextPacketPtr - 1 > re) { enc28j60Write(ERXRDPTL, (re)); enc28j60Write(ERXRDPTH, (re)>>8); } else { enc28j60Write(ERXRDPTL, (NextPacketPtr-1)); enc28j60Write(ERXRDPTH, (NextPacketPtr-1)>>8); }// decrement the packet counter indicate we are done with this packet enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); return len;}void dev_init(void){ enc28j60_init();}void dev_send(void){ enc28j60PacketSend(uip_len, uip_buf);}u16_t dev_poll(void){ return enc28j60PacketReceive(UIP_BUFSIZE, uip_buf);}void enc28j60_init(void){//SPI INIT SpiInit(); // perform system reset enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);// check CLKRDY bit to see if reset is complete //while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY)); // Errata workaround #2, CLKRDY check is unreliable, delay 1 mS instead delay_ms(5);// lamp test// enc28j60PhyWrite(PHLCON, 0x0AA2); // do bank 0 stuff // initialize receive buffer // 16-bit transfers, must write low byte first// set receive buffer start address NextPacketPtr = RXSTART_INIT; enc28j60Write(ERXSTL, RXSTART_INIT&0xFF); enc28j60Write(ERXSTH, RXSTART_INIT>>8);// set receive pointer address enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF); enc28j60Write(ERXRDPTH, RXSTART_INIT>>8);// set receive buffer end // ERXND defaults to 0x1FFF (end of ram) enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF); enc28j60Write(ERXNDH, RXSTOP_INIT>>8);// set transmit buffer start // ETXST defaults to 0x0000 (beginnging of ram) enc28j60Write(ETXSTL, TXSTART_INIT&0xFF); enc28j60Write(ETXSTH, TXSTART_INIT>>8); // do bank 2 stuff // enable MAC receive enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);// bring MAC out of reset enc28j60Write(MACON2, 0x00);// enable automatic padding and CRC operations enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);// enc28j60Write(MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); // set inter-frame gap (non-back-to-back) enc28j60Write(MAIPGL, 0x12); enc28j60Write(MAIPGH, 0x0C);// set inter-frame gap (back-to-back) enc28j60Write(MABBIPG, 0x12);// Set the maximum packet size which the controller will accept enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF); enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8);// do bank 3 stuff // write MAC address // NOTE: MAC address in ENC28J60 is byte-backward enc28j60Write(MAADR5, UIP_ETHADDR0); enc28j60Write(MAADR4, UIP_ETHADDR1); enc28j60Write(MAADR3, UIP_ETHADDR2); enc28j60Write(MAADR2, UIP_ETHADDR3); enc28j60Write(MAADR1, UIP_ETHADDR4); enc28j60Write(MAADR0, UIP_ETHADDR5); // no loopback of transmitted frames enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS);// switch to bank 0 enc28j60SetBank(ECON1);// enable interrutps enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);// enable packet reception enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); }
在上面文件中有一个delay_100ns函数,为什么要写这个函数呢,看了enc28j60的输入输出的时序之后发现在cs置低之后只要要保持100ns,所以每次在写入之前都要延迟100ns左右。cc2530的时钟是32MHz,所以差不多3个机器周期就是100ns了。这个是要注意的地方,我在这之前,调试了半天,一直都没发现有网络数据包出来,然后再仔细读了datasheet才发现的。
这个驱动根据datasheet写,或者参考网上的,哈哈,只要能实现就行了。
有了以上驱动程序就很好移植了! 废话不多说,直接给移植步骤。
在网上下载源码,我用的是uip0.9版本。编译环境是IAR,因为cc2530用的是IAR开发环境。
接下来建立工程,如下图所示:
将spi.c enc28j60.c即相关头文件放在driver目录下,将uip.c uip_arp.c uip_arch.c还有相关头文件放在uip目录下,
linker目录下放的是IAR8051的连接文件(可以不用),将应用程序main.c app.c app.h文件放在user目录下。
将uip源代码中unix文件夹中的main函数改一下,其中tapdev_read函数替换成我们的dev_poll,将tapdev_send替换成dev_send,然后将http相关的东西删去,我们先实现简单的功能。
根据源码文件夹中的doc文档,写个简单的历程。
app.c的代码如下:
#include "app.h"#include "uip.h"void example1_init(void) { uip_listen(HTONS(1234));}void example1_appcall(void){ struct example1_state *s; s = (struct example1_state *)uip_conn->appstate; if(uip_connected()) { s->state = WELCOME_SENT; uip_send("Welcome!\n", 9); return; } if(uip_acked() && s->state == WELCOME_SENT) { s->state = WELCOME_ACKED; } if(uip_newdata()) { uip_send("ok\n", 3); } if(uip_rexmit()) { switch(s->state) { case WELCOME_SENT: uip_send("Welcome!\n", 9); break; case WELCOME_ACKED: uip_send("ok\n", 3); break; } }}
对了,还有个重要的问题,就是大小端的问题,这个也是我调试好久未果的一个经验。cc2530是8051内核的,是小端,所以在uipopt.h文件中做如下改动
#ifndef BYTE_ORDER
#define BYTE_ORDER LITTLE_ENDIAN
#endif /* BYTE_ORDER */
还有个编译设置的问题,具体按照如下来:
好了,然后编译连接,过程中可能有一些警告和错误,一个一个耐心排除,很容易。
用网线连上电脑,然后打开命令终端,输入ping 219.223.173.242 我主机ip是219.223.173.243
如下结果:
然后打开网络调试助手
如程序需要的结果相同,哈哈!过几天实现一个web服务器!
接下来的工作就是将uip移植到z-stack上,在cc2530上实现网关的功能!
有需要源码的可以联系我!
- UIP成功移植到CC2530上
- UIP移植到CC2530上
- UIP移植到CC2530上
- 移植ucgui到linux上成功
- uip在stm32上移植问题总结
- uip协议栈移植到window
- uip移植
- UIP移植
- UIP移植
- TinyOS在CC2530上移植之USART
- Contiki在CC2530上的内核移植
- freakz移植到IAR的CC2530工程
- qtopia-opensource-src-4.2.3成功移植到mini2440上
- 成功移植u-boot-2009.03到qt2410上
- 成功移植u-boot-2009.03到adm5120平台上
- 成功的把ftk移植到arm-linux上
- android2.3系统成功下载编译移植到BeagleBoard上
- 成功将qt程序移植到arm板上
- shift_循环获取位置参数
- 网页适量绘图,VML+SVG双剑合璧 打造GIS客户端应用
- 最值得创业者聆听的10大TED演讲
- TCP/IP总结
- Android应用开发之(你必须知道的8个Android开发工具)
- UIP成功移植到CC2530上
- 简单的控制台五子棋游戏
- 百练 1004 Financial Management
- 百练 2714 求平均年龄
- 百练 2883 Checking order
- C++Primer再读读书笔记:第四章
- vc 浮点数转换为内存十六进制字符串
- 一个android工程的运行过程
- iOS应用开发入门指南 - 编写 Objective-C 代码