本帖最后由 xukai871105 于 2013-6-12 15:26 编辑 1.前言 最近半年的时间一直在学习应用嵌入式以太网。虽然学习的动机仅仅是玩玩,但是以太网真的深深吸引了我。这里我和各位分享一下uIP的使用经验,希望对大家有所帮助。uIP是一个简单好用的嵌入式网络协议栈,易于移植且消耗的内存空间较少,非常适合学习和使用。可以肯定的说uIP是嵌入式以太网学习的好起点,但不一定是终点。毫无疑问的说,uIP的功能远不如LwIP强大,但两者并没有孰优孰劣之分,uIP和LwIP的作者同为Adam Dunkels,LwIP在前uIP在后,uIP经过这几年的发展从IPV4迁移到IPV6,最终可以适用于无线传感网络。总的来说,uIP是一个很好的起点,学好uIP可以迁移到LwIP,也可以迁移到uIPV6。 下面稍微啰嗦一下,先推荐两本图书。本人喜欢读书,书中的论述比较完整,调理液清晰一些。 1.1推荐图书资料 《基于IP的物联网架构、技术与应用》。图书的作者之一adam dunkels为uIP和LwIP的作者,虽然uIP在书中只占非常小的一部分,但是这本书的信息量非常大,技术非常新颖。书中提到的PACHUBE即是在论坛打广告乐为物联的原型。 《嵌入式Internet TCP IP基础、实现及应用》。本书的TCP IP部分介绍的非常详细,书中有实现嵌入式以太网的代码分析。本书的作者也设计了一套功能完善的TCP IP协议栈。结合书中前半部分的基础和中部的实现,会有非常大的收获。 1.2其他网络资料 第一次有学习嵌入式以太网的冲动便从淘宝上购入ENC28J60模块,卖家提供的源码为国外AVRNET项目的源码。如果耐心一点认真分析AVRNET项目的源代码,并不断修改实践,收获颇丰。顺着ARP、IP、ICMP、UDP、TCP写了几个帖子,算是自己对嵌入式以太网的第一个总结。在这里再次贴一下链接。 http://www.amobbs.com/thread-5519452-1-1.html 【ARP部分】 http://www.amobbs.com/thread-5519494-1-1.html 【IP和ICMP部分】 http://www.amobbs.com/thread-5519530-1-1.html 【UDP部分】 http://www.amobbs.com/thread-5519592-1-1.html 【TCP部分】 在这一系类帖子中,还欠了一个HTTP的帖子。通过大家的关注度我发现,ARP部分关注的人最少,因为这个离HTTP最远。包括我在内,得到网络模块ENC28J60的第一个反应就是如果实现网页(HTTP)控制LED灯,读取温度湿度数据。现在回过头来看看基础还是非常重要的。呵呵。 2.搭建实验环境 先讲一下如何搭建实验环境。我个人倾向于把开发板接到路由器上,而调试使用的PC机通过有线或者无线也接到PC机上,先保证开发板和PC机接入同一个路由器。由于uIP不支持DHCP(只能说不直接支持),所以需要保证开发板和PC位于相同的子网,开发板的IP地址、路由器地址和子网掩码都需要手动设定。设定之前最好看看调试PC机的IP地址和路由器(网关)地址。例如调试PC机的IP地址如下图所示。 之后便可确定路由器的IP地址为192.168.1.1。那么开发板的IP地址可以设定为 192.168.1.2到192.168.1.255。为保证你的调试万无一失,还是建议您访问路由器,确认现在有哪些设备连入了路由器。该步骤的主要功能是避免IP地址重复。 3.硬件和软件说明 3.1硬件环境: 奋斗开发板。奋斗开发板上有一片ENC28J60,ENC28J60通过SPI接口控制内部寄存器,并有中断输出接口。STM32通过SPI1和ENC28J60相连。具体接口如下: [email=SPI1_MISO@PA6]SPI1_MISO@PA6[/email] [email=SPI1_MOSI@PA7]SPI1_MOSI@PA7[/email] [email=SPI1_SCK@PA5]SPI1_SCK@PA5[/email] [email=ENC28J60_CS@PA4]ENC28J60_CS@PA4[/email] [email=ENC28J60_INT@PA1]ENC28J60_INT@PA1[/email](不过本例并没有使用外部中断,不过LwIP中推荐使用外部中断) 由于SPI上同时挂在其他SPI从设备,所有初始化的过程中需要通过操作CS端口禁止其他SPI从设备。(别小看这步,调试的时候在这步花费了非常多的时间)其他SPI从设备包括SST25VF016,CS端位于PC5;VS1003,CS端口位于PB12。 3.2其他说明 串口调试位于UART1。有三个LED灯分别位于PB.5,PD.6和PD.3。如果您的开发板和有存在差别,请按照顺序修改相关IO口,并打开相应RCC时钟。 3.3软件说明 工具链为EWARM 5.5(如果是EWARM 6.X请修改相关CMSIS选项) 4.网卡驱动 网卡驱动采用ENC28J60。具体可参考论坛中的另一个帖子http://www.amobbs.com/thread-5519381-1-1.html 需要说明的是ENC28J60的驱动也是参考网上资料的。但是帖子中有若干说明,详细分析了如何使用ENC28J60,需要深入理解两点,第一点如何通过SPI发送命令和数据;第二点理解ENC28J60的缓冲区,在发送以太网和接受以太网数据包的过程中,ENC28J60会帮助用户做些额外的工作,例如发送时自动填充SFD,在读取接收缓冲区数据时会包含若干状态信息,包括数据包长度和CRC校验结果等。如果你比较“速食”可以跳过该部分内容,如果你比较“耐心”可以花点时间看看。其他的以太网驱动芯片包括RF芯片也遵循相同的规律,可以做到触类旁通。 5.一个简单有效的定时器 TCP通信为了保证可靠性需要采用定时重传等安全机制,所以需要一个简单有效的定时器。设计定时器的方法很多,在这里推荐uIP原作者的timer模块。简单说timer模块的原理类似于MCU硬件中的比较匹配原理,timer模块中有一个全部变量counter,每次MCU发生某个定时器中断时累加1,如果某个任务需要使用定时器服务,在该任务中声明一个timer(在该任务中为全局变量),并记录此时的counter值,在本任务中声明的timer何时发生软件溢出呢?查询现在的counter和被记录的counter的差值,如果差值超过间隔值那么软件定时器timer溢出(类似于发生比较匹配中断),那么任务该做点什么了。软件定时器的主要目的有两个。第一,更新TCP或UDP连接,第二,更新ARP缓冲区(ARP表)。虽然uIP在功能上比LwIP简单的多,但是LwIP也有类似的部分(或者说完全一样)。 详细代码如下:
复制代码 6.uIP基本结构与配置 6.1 uIP基本结构 uIP的代码编写需要遵守一定的结构,而且这种结构最好保持稳定(保持不变)。这个结构主要做以下几个部分任务。 1.获得以太网数据包 2.处理ARP报文 3.处理IP报文 4.定期处理TCP和UDP连接 5.定期更新ARP缓冲区
复制代码 做几点简单说明 1.#define BUF ((struct uip_eth_hdr *)&uip_buf[0]) 指向uIP缓冲区,强制类型转化为uip_eth_hdr结构体,uip_eth_hdr即为以太网首部结构,6字节目标MAC地址 6字节源MAC地址 2字节类型。 2. tapdev_init();tapdev_read();tapdev_send(); 三个函数为以太网操作函数,只有tapdev_read有返回值,其他函数即无输入参数也无返回参数。这三个函数便是ENC28J60操作的三个封装,ENC28J60发送或接收直接操作uIP的两个全局变量uip_buf和uip_len。 具体代码如下:
复制代码 6.2 uIP配置部分 IP地址配置: IP地址设置包括,本地IP地址,网关地址和子网掩码。具体代码如下: MAC地址配置: MAC的地址较为特殊,由于ENC28J60本身没有唯一的EUI-48(俗称MAC地址)地址,所以EUI-48地址需要手动配置。该地址不但应用于ENC28J60也应用于uIP。相关代码在上一小节已说明。 6.3 uip-conf.h部分 uip-conf部分说明三点 1.如果不熟悉请保留默认参数,例如UIP_CONF_MAX_CONNECTIONS等 2.如果设置UIP_CONF_LOGGING为1,请添加void uip_log(char *m){} 3.必须包含用户任务头文件,且放在该头文件的最后。例如添加#include "example1.h"。这样做的主要目的便是定义uip_tcp_appstate_t和UIP_APPCALL两个关键参数。 具体代码如下
复制代码 7.案例1——最简单的TCP echo程序 先来一个最简单的TCP程序。uIP作为server,IP地址为192.168.1.15。PC机做client,IP地址为192.168.1.10X。 1.在网络调试助手中,选择以太网通信种类为client(表示PC机为Client),IP地址输入192.168.1.15,端口号输入1234。最后点击连接。 2.在发送区域输入任意内容,点击发送数据。 3.观察返回结果,是否和发送数据相同。 为了实现该功能新建example1.c和example1.两个文件。代码如下
复制代码
复制代码 实验结果如下图所示。 代码做如下分析 1.uip_listen(HTONS(1234));侦听1234端口, 2.uip_newdata()即查询uip_buf中是否有新数据,如果返回1的话,任务需要做些什么了。 3.uip_send(uip_appdata,uip_len);uip_send为发送数据包函数 4.uip_appdata指向用户数据,所谓用户数据即TCP负载数据,例如网络调试助手发送xukai871105,那么uip_appdata指向xukai871105. 5.uip_len为用户数据长度,若串口调试助手发送xukai871105,那么uip_len为11。 8.wireshark网络包分析 程序虽然简单,但是TCP通过过程还是可以好好分析一下的。通过wireshark软件抓取整个通信过程。 其中192.168.1.102为调试PC机(下文简称PC机),192.168.1.15为uIP嵌入式开发板(下文简称uIP)。 =================================================== 1.建立连接阶段 【36】PC机向uIP发送SYN,表示请求连接(点击网络调试助手的连接按钮) 【37】uIP向PC机返回ACK,同时发送SYN(注意若接收到SYN标志,必须返回ACK) 【38】PC机向uIP发送应答ACK,表示该次TCP连接成功。 =================================================== 2.数据交换阶段(负载数据包假定为1234) 【51】PC机向uIP发送1234,标志位PSH+ACK,表示该数据包需要立即处理,并需要应答 【52】uIP向PC机返回1234,标志位PSH+ACK,表示该数据包需要立即处理,并需要应答 【53】PC机返回应答,表示PC机接收到echo数据包。 此时数据交换完成,若在网络调试助手再次点击发送,便重复51到53部分。 =================================================== 3.关闭连接部分 【65】PC机要求停止连接,发送FIN标志。(点击网络调试助手的关闭按钮) 【66】uIP返回FIN+ACK,表示同意结束本次TCP连接。 【67】PC机发送ACK,表示收到了uIP的FIN。(至此,TCP连接完全结束) =================================================== 9.案例2——uip_poll中发送数据 uip中发送数据包要稍微复杂一些。若使用uIP如果不做特殊处理的话,只能在uIP用户任务中发送以太网数据包,而不能在其他的任务中发送。虽然有人说这是uIP的bug,不过我并不认同。在多个uIP示例代码中,均定义了用户任务本身状态,例如任务处于发送状态还是接受状态,任务有独立的发送缓冲区或接收缓冲区并记缓冲区的大小。 设计一个简单的任务说明uip_poll的应用。 1.uIP作为server,IP地址为192.168.1.15,侦听端口端口为1234,PC机通过网络调试助手连接uIP。 2.建立连接之后,uIP主动发送10个数据包,数据包之前间隔1S。 3.发送完数据包只收,uIP主动关闭连接。 详细代码请查看工程附件。 实验结果,uIP向PC机连续发送10次数据,并最后主动终止连接 10.总结 1.掌握嵌入式以太网需要较多的背景知识,只能在实践的过程中一点一滴积累。回过头来想想自己的学习嵌入式以太网的经历,多数时间多是在急躁和失望中度过。唯有耐心与细致并不断学习基础知识才可以把问题解决,最终把想法变成现实 2.uIP功能简单,但是易于使用。如果觉得uIP在实际中难以发挥作用的话,还有LwIP作为补充。虽然两者存在功能上的差异,但是TCP连接还是那几个——SYN、ACK、PSH、FIN标志位。LwIP提供套接字通信,这使得嵌入式以太网应用和PC机上的以太网应用变得极为相似。 3.由于TCP协议属于运输层协议,TCP传输的内容本身并没有含义,这些被传输的数据需要被赋予含义才可以使用。从工业控制来说,MODBUS协议可以应用与TCP协议(本帖子是对modbus tcp的铺垫),并可以实现完善的检测与控制功能。从其他应用来说,嵌入式系统可以提供HTTP通信、提供web service应用,通过解析JSON格式等手段实现更广泛的应用。 |
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) | Powered by Discuz! 7.0.0 |