基于uC/OS-II的嵌入式网络通信模块
2014-10-14郝玉胜逯玉兰
郝玉胜,逯玉兰
(1.兰州交通大学光电技术与智能控制教育部重点实验室,甘肃 兰州 730070;2.甘肃农业大学信息科学技术学院,甘肃 兰州 730070)
0 引言
uC/OS-II是一款开源的多任务实时操作系统,它是一个得到美国联邦航空管理局RTCA DO-178B认证的微内核,可以使用在安全性要求较高的场合中。在绝大多数应用中,uC/OS-II都被移植到微控制器中作为一个实时控制系统。伴随着Internet走进千家万户的浪潮,“能够上网”几乎已经成为任何一个嵌入式设备必须具备的条件,移植了uC/OS-II的系统也存在这样的需求。
ENC28J60是Microchip Technology公司推出的28引脚以太网控制器,它采用SPI接口,只需要4条线就可以和微控制器互联,内置PHY和MAC可以很方便地完成配置。因此,在小微网络通信模块中以ENC28J60作硬件是较为合理的选择。
由瑞典计算机科学学院Adam Dunkels博士开发的开源TCP/IP协议栈uIP在编译后的代码大小和运行时所需要的RAM需求量比其他一般的TCP/IP协议都要小,对于存储空间紧张的嵌入式系统,无疑是实用的。因此,在移植了uC/OS-II实时内核的系统中建立基于ENC28J60和uIP协议的嵌入式网络通信模块不但经济而且实用,能够满足普通嵌入式应用的要求。
1 uC/OS-II内核移植
uC/OS-II的代码源文件可以分为与上层应用程序相关的配置文件、与处理器无关的核心功能实现文件和与处理器有关的移植文件,其文件系统结构如图1所示。
图1 uC/OS-II文件结构
内核移植的重点主要有如下几个方面:
(1)在OS_CPU.H中定义与编译器相关的数据类型、宏定义以及堆栈数据类型。
(2)在OS_CPU.C中实现堆栈初始化函数及一系列在应用程序中将要用到的Hook函数。
(3)使用汇编语言在OS_CPU.S中实现处理器中断的开/关功能、任务切换函数以及时钟中断。
uC/OS-II内核移植完毕后,就需要以此为基础,构建TCP/IP协议栈,实现网络芯片驱动程序以及设计网络通信任务。?
2 ENC28J60及其驱动程序
ENC28J60以太网控制器采用业界标准串行外设接口(SPI),速率高达10 Mbps。同时,28脚的封装使其在硬件集成上也显得更为简单,只需要少许标准元器件就可以和MCU很好地集成在一起。ENC28J60以太网控制器符合IEEE 802.3协议,具有可编程过滤功能和8 kB双端口SRAM缓冲器。ENC28J60硬件原理如图2所示。
图2 ENC28J60硬件原理图
2.1 ENC28J60芯片初始化
任何芯片在使用之前都需要经过初始化操作,ENC28J60也不例外。在uC/OS-II的任务设计中,芯片的初始化任务要安排在处理器复位之后立即完成,而且不再需要频繁调度,只在需要的时候才调度。初始化主要完成接收缓冲器、发送缓冲器、接收过滤器、MAC初始化以及PHY的初始化,示例代码如下。
说明:DoWrite函数内部包含了SetBankMem<设置寄存器区>子程序,而WriteOperation直接根据SPI操作码<前3bit>+寄存器地址<后5bit>进行操作。
2.2 数据发送程序
在数据发送过程中,ENC28J6具有自动生成前导帧定界符的功能。如果用户需要,ENC28J60也能够自动生成CRC填充数据,前提是用户已经进行了相应的配置。在发送数据之前,处理器必须把即将要发送的所有数据帧写入到发送缓冲区以做好准备。另外,处理器也必须在发送数据包的前面增添一个数据包控制字节。数据包控制字节由这样几个位段构成:PHUGEEN(数据包超大帧使能位)、PPADEN(数据包填充使能位)、PCRCEN(数据包CRC使能位)以及POVERRIDE(数据包改写位)。数据发送程序的示例代码如下。?
2.3 数据接收程序
数据接收程序首先要检测中断标志以验证是否收到一个数据包。如果是,主控制器则使用RBM SPI命令从下一数据包指针的首地址开始读取,示例代码如下。
3 TCP/IP协议栈—uIP协议
3.1 uIP与其接口技术
uIP协议栈是使用于低至8位或16位嵌入式微处理器的极小的TCP/IP协议栈,其编译后的代码大小和运行时使用的RAM空间比其它一般的TCP/IP协议栈要小,编译完成后只占用几千字节的ROM,运行时只需要几百字节的RAM即可。uIP协议栈完全使用C语言编写,它可以自用分发和使用于商业和非商业目的。
uIP协议栈仅仅实现了TCP/IP协议族中的4个基本协议:ARP、IP、ICMP以及TCP协议。链路层协议,例如PPP可以实现作为uIP下面的设备驱动。应用层协议,例如HTTP、FTP或SMTP可以实现为uIP之上的应用程序。
图3 uIP接口示意图
uIP可以看作是一个为系统提供了确定函数的代码库。图3展示了uIP、系统底层和应用程序3者之间的关系。
从系统底层的角度看,uIP为底层系统提供了3个函数,分别是uip_init()、uip_input()和uip_periodic()。
uip_init()实现了初始化uIP协议栈的功能,在系统启动初期就必须调用此函数完成对uIP协议栈的初始化工作。
uIP协议栈和系统底层ENC28J60驱动程序交换数据的关键数据结构是全部变量uip_buf(在C语言中其实就是一个数组)。当ENC28J60驱动程序将一个收到的数据包写入uip_buf缓存中时,系统便调用uip_input()函数,该函数将会处理这个数据包并且在需要时调用应用程序提供的回调函数。当uip_input()函数执行完毕返回时,会将一个输出包写入到uip_buf中。包的大小由另外一个全局变量uip_len来标识。如果全局变量uip_len的值为0,表示没有数据包要发送。
uip_periodic()函数用于驱动所有uIP协议栈内部的时钟事件,例如数据包重发。对于已经建立的每一个TCP连接,都应该调用uip_periodic()。其中传递给uip_periodic()函数的参数是TCP连接的连接号。uip_periodic()类似于uip_input(),当执行完毕返回时,也会将输出包写到缓存uip_buf中。
从应用程序的角度出发,应用程序必须提供一个回调函数给uIP。当网络或定时事件发生时,uIP会调用回调函数。当然,uIP也提供了许多函数用于和堆栈交互。当接收数据、数据成功送达另一方、建立新的连接、数据需要重发等事件发生的时候,uIP只会调用应用程序提供的回调函数。对于不同的网络服务以及不同的端口连接统统由应用程序在回调函数中处理。uIP也提供了一系列的测试函数用于区别不同的事件,用户可以在应用程序中使用这些测试函数。
另外,应用程序还应该周期性地循环检测各个已经建立的连接,当新的数据来临的时候采用中断方式处理是最为常用的做法。
3.2 uIP协议UDP通信bug修复
uIP在UDP通信时显得十分不方便,也可以说是存在一个bug。从接收数据来说,在设备准备接收外部数据前,uIP首先要知道对方的IP信息并赋值给连接表,uip_input()完成接收数据的解析,而只有当接收到的数据包中的源IP以及源端口与当前UDP连接中的目的IP以及目的端口相一致的时候,数据包才会被接收。如果通信之前都约定好IP地址和端口号,那么对于uIP来说,进行UDP通信是没有问题的。但是,现实条件往往没有那么理想。若作为接收端,当一个新的UDP数据包到达时,如果因为连接表中的IP和端口信息与接收到的UDP数据包中的IP和端口信息不一致就不予理睬,显然是不合理的。假如这个UDP数据包是一个“新来的使者”,uIP应该是和该数据包的发送方建立联系而并非忽略之。
所以,对于uIP的这个问题,协议移植人员一定要解决。下面提供一种实现起来较为简单的思路,即在uip_process()函数的UDP数据包处理部分,增加如下代码。
经过上述处理后,对于一个新到的UDP数据包,uIP就能够处理了。
4 实验结果
实验主要测试从PC机能否ping通硬件开发板,在PC机的命令窗口中使用ping命令连接开发板时,成功收到了硬件开发板的反馈信息,如图4所示。
在PC机上运行网络助手,尝试和硬件开发板建立TCP连接并进行UDP通信时,也成功得到了预期的结果,如图5所示。
图4 从PC机成功ping通开发板
图5 网络调试助手和开发板建立TCP连接、进行UDP通信实验
5 结束语
本文设计并实现的嵌入式网络通信模块已经在一个小型系统中得以应用,成功地实现了PC机对嵌入式系统的远程控制。但其中的HTTP协议是作为uIP的上层应用程序实现的,这也是uIP使用比较不方便的地方,毕竟HTTP服务器的实现不是一件容易的事情。另外,如果硬件条件允许,用户也可以选择使用LwIP协议,它是Adam Dunkels博士对uIP进一步完善后的开源代码。
[1]Microchip Technology Inc.ENC28J60数据手册[R].Arizona:Microchip Technology Inc.,2006.
[2]Adam Dunkels.The uIP 1.0 Reference Manual[R].Stockholm:Swedish Institute of Computer Science,2006.
[3]InterNiche Technologies Inc.NicheStack Portable TCP/IP Stack[EB/OL].http://www.iniche.com/nichestack.php,2013-05-14.
[4]Adam Dunkels.uIP:A TCP/IP Stack for 8-and 16-bit Microcontrollers[EB/OL].http://www.dunkels.com/adam/,2013-07-29.
[5]Adam Dunkels.LwIP:A Lightweight TCP/IP Stack[EB/OL].http://www.sics.se/?adam/lwip/,2013-05-26.
[6]Nilesh Rajbharti.Microchip TCP/IP Stack Application Note[DB/OL].http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en011993,2008-08-21.
[7]LiveDevices Inc.Embedinet:Embedded Internet Software Products[EB/OL].http://www.livedevices.com/net_products/embedinet.shtml,2013-06-11.
[8]朱二喜.一种通用精简嵌入式TCP/IP协议栈的设计[J].南京工业职业技术学院学报,2009,9(4):23-25.
[9]伊文斌,周贤娟,鄢化彪,等.uIP TCP/IP协议分析及其在嵌入式系统中的应用[J].计算机技术与发展,2007,17(9):240-243.
[10]温阳东,何瑄,邓箐.基于RTL8019AS的以太网接口单元研究[J].仪器仪表用户,2006,13(3):83-85.
[11]陆玲,周航慈.嵌入式系统软件设计中的数据结构[M].北京:北京航空航天大学出版社,2008:57-59.
[12]张懿慧,陈泉林.源码公开的TCP/IP协议栈在远程监测中的应用[J].单片机与嵌入式系统应用,2004(11):61-64.
[13]刘琼,朱志伟,周志光.基于ENC28J60的嵌入式网络接口的设计[J].微计算机信息,2008,24(14):306-308.
[14]李章林,张立民.TCP/IP在51单片机上的实现特点和方法[C]//2003年全国单片机及嵌入式系统学术年会论文集(上册).2003:380-384.
[15]刘火良,杨森.STM32库开发实战指南[M].北京:机械工业出版社,2013:401-426.
[16]刘明波,孙永灿,耿文建.基于uC/OS-II的嵌入式网络监控系统的设计与实现[J].计算机工程与设计,2010,31(24):5211-5215.