原帖地址http://blog.youkuaiyun.com/JassFuchang/article/details/6413271
经过十几天的努力,终于在板子上把uC-TCPIP跑起来了。趁着还记得,赶紧把过程记录下来,分享心得体会。当中也走了不少弯路,以后吸取经验教训。
这十几天的工作大概可以分为三个部分:搜集资料、代码移植、测试调试。
由于项目需要,我们要在现有的ARM7平台上加网口跑TCP应用,然而我们现有的工程并没有实现TCP/IP协议栈。在初步调研之后,开始在网上搜集了关于一些uC-TCPIP的资料,主要有“官方Micrium”uC-TCPIP的源码,再加两三个不同硬件平台上的使用示例(感谢csdn、google svn和分享者等)。此外还搜索到几篇uC-TCPIP使用经验的帖子或者博文。
官方源代码里面分了好几个目录,其中uC-TCPIP目录下的Source是最核心的源码。简单地说,Source是直接拿过来用的,其他部分例如NIC、OS、CPU等目录是根据具体开发环境具体分析修改的。此外uC-Lib是个通用的函数库,处理内存分配和字符串转换等,也可以直接拿来用。
我们的项目一直是使用uCOS-II的,然而仔细看了uC-TCPIP的配置说明,uCOS-II的最低版本要求是v2.8x,而我们当前用的是uCOS-II v2.52,所以第一步必须先升级uCOS-II。
经过一两天的努力,我把原来DSP和ARM平台上面项目的uCOS-II都升级成v2.86,中间因为配置错了idle task堆栈大小导致溢出,花了点时间去调试。
移植uC-TCPIP就比较花功夫了。首先我是在ARM7的平台上做尝试。先把uC-TCPIP/source加进工程,设置好搜索目录路径,这时编译通过但链接失败,原因是还有很多函数引用没有实现。解决方法是搜索uC-TCPIP/IF、uC-TCPIP/OS、uC-Lib等模块,选择适合的实现文件并加入到工程。因为目前这个平台上面没有物理网络芯片,需要自行添加一些与网络芯片操作相关的空接口。另外还改写了CPU相关的CPU_CRITICAL_ENTER和CPU_CRITICAL_EXIT宏,禁用了一些调试和汇编优化相关的配置例如NET_DBG_CFG_*(@net_cfg.h)、uC_CFG_OPTIMIZE_ASM_EN(@app_cfg.h)等。最终生成了执行文件,下载到目标平台上“运行”起来,当然,这时候只是任务开始跑起来,实质没干什么。
有了之前的经验,把uC-TCPIP移植到DSP平台就顺利多了。正好我有一块DSP板有网络芯片Cs8900a,并且在网上找到用于uC-TCPIP的驱动net_nic for Cs8900a,正好可以把NIC模块给替换掉。不过这还不是最“底层”的驱动,因为NIC模块只知道怎样操作Cs8900a的寄存器,但具体那些寄存器的物理地址在哪里还不知道,这取决于具体的平台。在uC-TCPIP的组成架构里由BSP模块去管这事,这说明net_bsp的实现需要我们自己动手了。事实上需要我们自己动手添加的东西不多,主要是读、写寄存器的操作接口,还有中断服务程序。顺便一提的是uC-TCPIP的层次划分,虽然目前我对于IF、NIC、BSP等的概念还是比较模糊,但大概可以知道BSP相当于最底层,与硬件密切相关,NIC是网络接口的一个抽象,这两个对应物理层,至于IF我还不了解,可能是和数据链路层有关。反正换板子可能要改BSP,换网络芯片可能就要改NIC。这样的设计在移植的时候很方便很灵活。之前遇到过某驱动,里面涉及到从物理层到应用层,用得哭啊...代价是引入了很多层,空间和时间效率都会有影响。不过我认为相比起开发和维护的难度,这个绝对是物有所值。
Build好然后就是下到板上调试了。设了个简单的定时发UDP包的任务。可是一直没有包可以侦听到。断点单步跟踪,检查网线,用旧的程序驱动对比、替换,折腾了一个星期,终于发现是BSP写错了,是我对寄存器的操作有误解...这时候我不禁感叹,如果有个拍档之类稍做一点code review的话,这问题应该早就能解决了。当我抓到第一个UDP包,激动了不到一分钟,发现内容有字节反了过来,看来大小端的问题还是没注意好。在各个头文件里和ENDIAN相关的无数个宏定义里转了好长时间,断定是“拿来”的NIC里定义有误?,改之,终于收发数据包都正常了。最后我开了一个socket,先后简单地测试了UDP和TCP的收发。到这里,我的项目可以说是只迈出了第一步……
小结移植过程中的经验教训:
1.刚接触uC-TCPIP时觉得目录结构有点复杂,不知道从何入手。快速上手的办法还是读说明手册,看示例工程,找别人写的使用经验……
2.os_cfg.h、cpu.h、net_cfg.h和app_cfg.h等头文件要认真根据实际情况配置,可以参考其他工程,但不能盲目照搬。
3.嵌入式程序跑飞主要原因是数组溢出、堆栈溢出和野指针等,这回升级uCOS-II之后跑不起来原因是堆栈设置小了。
4.硬件或者协议相关的代码要注意字节排列大小端的问题,这个工程里有CPU的ENDIAN,Cs8900a的ENDIAN,还有TCPIP协议的ENDIAN,我们的工程中CPU是LITTLE_ENDIAN的,和Cs8900a、TCPIP相反。配置错了的话,如果是数据反了那还好查原因,如果是没数据出来那就可能得查半天了。
5.通常加入一个新的库编译链接时都会有许多稀奇古怪的warning和error,让人眼花缭乱,不胜其烦,甚至会吓退意志薄弱者放弃使用新库。稍总结了几种常见原因:
一是头文件没找到,通常需要加搜索路径(-i"PATH");
二是找不到定义或者重复定义,有些头文件没有防重复包含,include顺序不当就会出现类似的问题;
三是找不到符号,链接缺了外部函数或者全局变量,那就得按图索骥添加模块,有时可能得自己实现,另外uCOS喜欢define OS_MASTER_FILE,uC-TCPIP则喜欢define NET_xxx_MODULE,找不到全局变量时不妨关照下这类宏;
四是命名冲突,这也表现为重复定义,但相关的宏、变量或函数在不同模块不是一回事,这木有namespace只好牺牲一方,改名了,这回就让我遇上BSD Socket API的close和DSP运行库rts6700.lib的close冲突了...;
其实比起调试来说编译出错并不可怕,毕竟没有类、模板这些东西,还是不难对付的。
附上工程源码:http://download.youkuaiyun.com/source/3270822
uC-TCPIP和Cs8900a驱动源代码(网上搜罗的):http://download.youkuaiyun.com/source/3274016