logo资料库

uCOS平台下的LwIP移植(非常详细).doc

第1页 / 共62页
第2页 / 共62页
第3页 / 共62页
第4页 / 共62页
第5页 / 共62页
第6页 / 共62页
第7页 / 共62页
第8页 / 共62页
资料共62页,剩余部分请下载后查看
uC/OS-II 平台下的LwIP 移植笔记――作者:焦海波 2006-9-1 1 下载LwIP................................................................................................................................................................. 2 5 2 建立一个最基本的工程 .........................................................................................................................................2 3 把LwIP加入工程 .....................................................................................................................................................2 4 编写操作系统模拟层相关代码 .............................................................................................................................3 4.1 操作系统模拟层移植说明――中文翻译.................................................................................................3 4.2 编写操作系统模拟层 .................................................................................................................................6 4.2.1 准备工作――建立文件、定义数据类型及其它.........................................................................6 4.2.2 信号量操作函数 .............................................................................................................................8 4.2.3 邮箱操作函数 ...............................................................................................................................13 4.2.4 实现sys_thread_new()函数.......................................................................................................20 4.2.5 实现sys_arch_timeouts()函数.................................................................................................22 4.2.6 实现临界保护函数 .......................................................................................................................25 4.2.7 扫尾――结束操作系统模拟层的编写.......................................................................................26 LwIP接口――初始设置及网络驱动 ...................................................................................................................28 5.1 准备工作――建立LwIP入口函数文件...................................................................................................28 ilvInitLwIP().....................................................................................................................................29 5.2 ilvSetLwIP().......................................................................................................................................30 5.3 ethernetif_init()――初始化底层界面.............................................................................................35 5.4 ethernetif_init()函数分析.....................................................................................................35 5.4.1 low_level_output()――链路层发送函数...............................................................................36 5.4.2 low_level_init()――网卡初始化函数...................................................................................38 5.4.3 EMACInit()――网卡初始化工作的实际完成者.......................................................................40 5.4.4 ethernetif_input()――实现接收线程...................................................................................47 5.4.5 low_level_input()――得到一整帧数据.................................................................................49 5.4.6 GetInputPacketLen()――获得帧长.........................................................................................50 5.4.7 EMACReadPacket()――复制,从接收缓冲区到pbuf ...............................................................53 5.4.8 EMACSendPacket()――发送一帧资料.......................................................................................55 5.4.9 5.4.10 编译――ethernetif.c及lib_emac.c ......................................................................................56 ping――结束LwIP的移植 ...................................................................................................................................57 6.1 编译、链接整个工程 ...............................................................................................................................57 ping测试...................................................................................................................................................59 6.2 后记...............................................................................................................................................................................62 6 - 1 -
uC/OS-II 平台下的LwIP 移植笔记――作者:焦海波 2006-9-1 本文将指导读者一步步完成LwIP在ADS1.2开发环境下的移植工作,包括底层驱动的编写。本文使用的 硬件平台是AT91SAM7X256 + RTL8201BL(PHY),至于软件平台,读者从本文标题即可看出。我们使用 uC/OS-II 作为底层操作系统,而LwIP的移植亦将主要围绕uC/OS-II展开。好了,不再多说,开始吧…… 1 下载LwIP 很简单,到LwIP的官方网站即可:http://savannah.nongnu.org/projects/lwip/。如果你不想看看 其 它 内 容( 可 能 对你 会 很 重要), 就 只 是 想 得 到源 码 , 好的 , 直 接到 这 个 地 址 下 载: http://download.savannah.nongnu.org/releases/lwip/。 目 前 官 方 发 布 的 最 新 版 本 是 1.1.1, 找 到 lwip-1.1.1.zip,然后下载、解压缩,第一项工作完成。 2 建立一个最基本的工程 要想完成移植工作,我们必须要有一个包含uC/OS-II的工程才行,这一步我们就是要建立这个工程。 工程 建立完毕后,编译链接没有问题,那么,第二项工作也完成了。关于如何建立一个包含uC/OS-II的 ADS 工程 的问 题 , 不 在本 文 描 述 范围 之 内 , 这里 不 做 讲 述。 随 本 笔 记一 同 发 布 的源 码 文 档 中 LwIPPortingTest_2 档夹下包含了这个最基本工程的源码,读者可以直接使用。我的基本工程建立的 路径是D:\work\LwIPPortingTest,下文将以相对路径进行讲述,不再提供绝对路径。 3 把LwIP加入工程 首先,在\src\活页夹下,建立LwIP活页夹,即:\src\LwIP;然后将下载的LwIP源码文件中api、 core、include、netif文件复制到\src\LwIP\活页夹下,如下图所示: 图3.1 然后,用ADS打开工程文件,按照LwIP源码文件的实际存放路径建立LwIP的工程结构,如下图所示: - 2 -
uC/OS-II 平台下的LwIP 移植笔记――作者:焦海波 2006-9-1 图3.2 这里需要特别说明的是,源码中的IP V6、SLIP及PPP部分我们没有添加进来,主要是考虑我及大多数 读 者的网络还是V4,而SLIP、PPP暂时不在我的考虑范围之内。另外,在移植层面V6也和V4相差不多, 这里 就不再讲解这部分内容了。现在基础工程结构建立完毕,可以把LwIP源码添加进来了。这一步很容 易, 按照文件存放路径,将源码文件添加到相应的工程结构下即可。源码添加完成后的工程参见所附源 码档 的LwIPPortingTest_3活页夹。 4 编写操作系统模拟层相关代码 LwIP的作者为操作系统模拟层提供了较为详细的说明,文件名为sys_arch.txt,在LwIP的doc文 件夹 下。我们的编写工作根据这个说明进行。 4.1 操作系统模拟层移植说明――中文翻译 事先声明,之所以笔者要翻译该文档,主要是笔者在撰写这篇笔记时亦没有通读该文档。笔者先 前 使用的模拟层源码是杨晔大侠的。为了真正弄懂 LwIP,笔者决定自己重新实现 LwIP 的移植,本 笔 记是跟随移植同步进行的,因此,翻译的文档也放在了这篇笔记中,使读者能够真正了解笔者 的移 植历程。另外再说一句,这个文档是为 LwIP 0.6++版编写,笔者搜遍了整个 LwIP 官方网站, 没有 发现比这更新的,笔者只好认为操作系统模拟层在 0.6++之后没有任何改动,如果有谁发现 了更新 的,一定通知笔者,先谢谢了。好的,言归正传,下面就是译文: - 3 -
uC/OS-II 平台下的LwIP 移植笔记――作者:焦海波 2006-9-1 LwIP 0.6++ sys_arch 界面 作者:Adam Dunkels 操作系统模拟层(sys_arch)存在的目的主要是为了方便 LwIP 的移植,它在底层操作系统和 LwIP 之间提供了一个接口。这样,我们在移植 LwIP 到一个新的目标系统时,只需修改这个接口即 可。不过,不依赖底层操作系统的支持也可以实现这个接口。 sys_arch需要为LwIP提供信号量(semaphores)和邮箱(mailboxes)两种进程间通讯方式(IPC)。 如果想获得 LwIP 的完整功能,sys_arch 还必须支持多线程。当然,对于仅需要基本功能的用户来 说,可以不去实现多线程。LwIP 以前的版本还要求 sys_arch 实现定时器调度,不过,从 LwIP0.5 开始,这一需求在更高一层实现。除了上文所述的 sys_arch 源文件需要实现的功能外,LwIP 还要 求用户提供几个头文件,这几个头档包含LwIP使用的宏定义。下文将详细讲述sys_arch及头文 件的实现。 信号量即可以是计数信号量,也可以是二值信号量――LwIP都可以正常工作。邮箱用于消息传 递,用户即可以将其实现为一个队列,允许多条消息投递到这个邮箱,也可以每次只允许投递一个 消息。这两种方式LwIP都可以正常运作。不过,前者更加有效。需要用户特别注意的是――投递到 邮箱中的消息只能是一个指标。 在sys_arch.h檔中,我们指定数据类型“sys_sem_t”表示信号量,“sys_mbox_t”表示邮箱。 至于sys_sem_t和sys_mbox_t如何表示这两种不同类型,LwIP没有任何限制。 以下函数必须在sys_arch中实现: - void sys_init(void) 初始化sys_arch层。 - sys_sem_t sys_sem_new(u8_t count) 建立并返回一个新的信号量。参数count指定信号量的初始状态。 - void sys_sem_free(sys_sem_t sem) 释放信号量。 - void sys_sem_signal(sys_sem_t sem) 发送一个信号。 - u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout) 等待指定的信号并阻塞线程。timeout参数为0,线程会一直被阻塞至收到指定的信号;非0, 则线程仅被阻塞至指定的timeout时间(单位为毫秒)。在timeout参数值非0的情况下,返回值为 等 待 指 定的 信 号 所消 耗 的 毫秒 数 。 如果 在 指 定的 时 间 内并 没 有 收到 信 号 ,返 回 值为 SYS_ARCH_TIMEOUT。如果线程不必再等待这个信号(也就是说,已经收到信号),返回值也可以为0。 注意,LwIP实现了一个名称与之相似的函数来调用这个函数,sys_sem_wait(),注意区别。 - sys_mbox_t sys_mbox_new(void) 建立一个空的邮箱。 - void sys_mbox_free(sys_mbox_t mbox) 释放一个邮箱。如果释放时邮箱中还有消息,它表明LwIP中存在一个编程错误,应该通知开发 者(原文如此,这句话很费解。个人理解的意思是:当执行 sys_mbox_free()这个函数时,按道理邮箱中不应该再 存在任何消息,如果用户使用 LwIP 时发现邮箱中还存在消息,说明 LwIP 的开发者存在一个编程错误,不能把邮箱 中的消息全部取出并处理掉。遇到这种情况,用户应该告诉LwIP的作者,纠正这个bug,译注)。 - 4 -
uC/OS-II 平台下的LwIP 移植笔记――作者:焦海波 2006-9-1 - void sys_mbox_post(sys_mbox_t mbox, void *msg) 投递消息“msg”到指定的邮箱“mbox”。 - u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout) 阻 塞线 程 直 至邮 箱 收 到至 少 一 条消 息 。 最长 阻 塞 时间 由 timeout 参数 指定 ( 与 sys_arch_sem_wait()函数类似)。msg是一个结果参数,用来保存邮箱中的消息指针(即*msg = ptr), 它的值由这个函数设置。“msg”参数有可能为空,这表明当前这条消息应该被丢弃。 返回值与 sys_arch_sem_wait()函数相同:等待的毫秒数或者 SYS_ARCH_TIMEOUT――如果时间溢出 的话。LwIP实现的函数中,有一个名称与之相似的――sys_mbox_fetch(),注意区分。 - struct sys_timeouts *sys_arch_timeouts(void) 返 回 一 个 指 向 当 前 线 程 使 用 的 sys_timeouts 结 构 的 指 针 。 LwIP 中 , 每 一 个 线 程 都 有 一 个 timeouts链表,这个链表由sys_timeout结构组成,sys_timeouts结构则保存了指向这个链表的指 针。这个函数由LwIP的超时调度程序调用,并且不能返回一个空(NULL)值。 单线程sys_arch实现中,这个函数只需简单返回一个指标即可。这个指标指向保存在sys_arch模 块中的sys_timeouts全局变量。 如果底层操作系统支持多线程并且LwIP中需要这样的功能,那么,下面的函数必须实现: - sys_thread_t sys_thread_new(void(*thread)(void *arg), void *arg, int prio) 启动一个由函数指针 thread 指定的新线程,arg 将作为参数传递给 thread()函数,prio 指定 这个新线程的优先级。返回值为这个新线程的ID,ID和优先级由底层操作系统决定。 - sys_prot_t sys_arch_protect(void) 这是一个可选函数,它负责完成临界区域保护并返回先前的保护状态。该函数只有在小的临界 区域需要保护时才会被调用。基于 ISR 驱动的嵌入式系统可以通过禁止中断来实现这个函数。基于 任务的系统可以通过互斥量或禁止任务来实现这个函数。该函数应该支持来自于同一个任务或中断 的递归调用。换句话说,当该区域已经被保护,sys_arch_protect()函数依然能被调用。这时,函 数的返回值会通知调用者该区域已经被保护。 如果你的移植正在支持一个操作系统,sys_arch_protect()函数仅仅是一个需要。 - void sys_arch_unprotect(sys_prot_t pval) 该函数同样是一个可选函数。它的功能就是恢复受保护区域的先前保护状态,先前是受到保护 还 是 没 有 受 到 保 护 由 参 数 pval 指 定 。 它 与 sys_arch_protect()函 数 配 套 使 用 , 详 细 信 息 参 看 sys_arch_protect()函数。 该函数的说明是按照译者个人理解的意思翻译,原文讲述不是很清楚,如有错误,欢迎批评指正,译注。 -------------------------------------------------------------------------------------- OS支持的模拟层需要添加的头檔说明 ------------------------------------------------------------------------------------- - cc.h 与硬件平台及编译器相关的环境变量及数据类型声明文件(一些或许应该移到sys_arch.h 文件)。 LwIP使用的数据类型定义――u8_t, s8_t, u16_t,s16_t,u32_t,s32_t,mem_ptr_t。 - 5 -
uC/OS-II 平台下的LwIP 移植笔记――作者:焦海波 与编译器相关的LwIP结构体封装宏: 2006-9-1 PACK_STRUCT_FIELD(x) PACK_STRUCT_STRUCT PACK_STRUCT_BEGIN PACK_STRUCT_END 与平台相关的调试输出: LWIP_PLATFORM_DIAG(X) LWIP_PLATFORM_ASSERT(x) - 故障,输出一条故障信息并放弃执行。 - 非故障,输出一条提示信息。 “轻便的(lightweight)”同步机制: SYS_ARCH_DECL_PROTECT(x) SYS_ARCH_PROTECT(x) SYS_ARCH_UNPROTECT(x) - 声明一个保护状态变量。 - 进入保护模式。 - 脱离保护模式。 如果编译器不提供 memset()函数,这个文件必须包含它的定义,或者包含(include)一个定 义它的文件。 这个文件要么包含一个本地系统(system-local)提供的头档――这个档定义了 标 准 的 *nix 错 误 编 码 , 要 么 增 加 一 条 宏 定 义 语 句 : #define LWIP_PROVIDE_ERRNO, 这 将 使 得 lwip/arch.h头文件来定义这些编码。这些编码被用于LwIP的各个部分。 -------------------------------------------------------------------------------------- - perf.h 定义了性能测量使用的宏,由LwIP调用,可以将其定义为一个空的宏。 PERF_START PERF_STOP(x) - 开始测量。 - 结束测量并记录结果。 -------------------------------------------------------------------------------------- - sys_arch.h sys_arch.c的头文件。 定义Arch(即整个移植所依赖的操作系统平台,译注)需要的数据类型:sys_sem_t,sys_mbox_t, sys_thread_t,以及可选类型:sys_prot_t。 sys_mbox_t和sys_sem_t变量的NULL值定义: SYS_MBOX_NULL SYS_SEM_NULL NULL NULL 4.2 编写操作系统模拟层 4.1节已经明白的讲述了如何实现sys_arch接口,我们按照这个说明完成即可。 4.2.1 准备工作――建立文件、定义数据类型及其它 在ADS工程LwIP组中添加一个新组arch并在这个组下面建立源文件sys_arch.c,实际存 放路 径亦如此组织,如下图所示: - 6 -
uC/OS-II 平台下的LwIP 移植笔记――作者:焦海波 2006-9-1 图4.2.1 然后在LwIP/include组同样建立一个新组arch,在arch组建立新档sys_arch.h及cc.h, 如下图 示: 图4.2.2 檔建立完成,我们先实现数据类型定义,这个实现完全按照移植说明一文进行。首先在 cc.h檔中定义常用的数据类型。这些常用数据类型不仅模拟层接口函数使用,底层协议栈实 现 亦 要 使 用 。 在 cc.h文 件 中 添 加 如 下语 句 ( 参 见 LwIPPortingTest_4活页 夹 下 的 cc.h文 件 ): typedef unsigned char u8_t; typedef char s8_t; typedef unsigned short u16_t; s16_t; typedef short typedef unsigned int u32_t; s32_t; typedef int typedef u32_t mem_ptr_t; 在上面的数据类型定义中,除了mem_ptr_t之外其它类型均很直观,不需解释。至于mem_ptr_t - 7 -
uC/OS-II 平台下的LwIP 移植笔记――作者:焦海波 2006-9-1 为什么指定为 u32_t,而不是像它的名称所表现的一样将其指定为指针呢?其实原因很简单, 笔者在定义它时首先找到了使用它的相关语句,从这些语句中才确定这样声明。读者可找 到 mem.h 文件看看里面有关 mem_ptr_t 的使用语句就能明白怎么回事。好了,不再多说,让我 们 的准备工作接着进行。在sys_arch.h文件中添加如下语句: typedef HANDLER sys_sem_t; 其中HANDLER是笔者本人自定义的一个宏,它是为了方便uC/OS-II的使用定义的。读者可以在 所附 源码档\src\uCOS_II\API\os_api.h中找到相关定义: typedef OS_EVENT* 它实际上就是指向uCOS中OS_EVENT结构的指针。 声明相关数据类型的语句添加完成后,我们再把 HANDLER; 这两个档 sys_arch.h 和 cc.h 添加到 sys_arch.c档中,以使该文件里的相关函数能够使用这些新定义的数据类型: #include #include 这里一定要注意顺序,先包含cc.h檔,再包含sys_arch.h檔,因为sys_arch.h檔中有 些语句需要用 到cc.h檔中的类型声明。好了,准备工作已经完成,现在开始编写接口函数。 "/LwIP/include/arch/cc.h" "/LwIP/include/arch/sys_arch.h" 4.2.2 信号量操作函数 相关函数实现读者也可直接参看sys_arch.c文件。 - sys_new_sem() //*-------------------------------------------------------------------------- //* 函数名称 : sys_sem_new //* 功能描述 : 建立并返回一个新的信号量 //* 入口参数 : [in] 指定信号量的初始状态 //* 出口参数 : 返回新的信号量 //*-------------------------------------------------------------------------- sys_sem_t sys_sem_new(u8_t count) { return OSAPISemNew(count); } 这个函数的实现其实很简单,因为uC/OS-II提供了信号量,我们只需直接调用建立信号量 的相关函数就行了。上面的源码中OSAPISemNew是笔者本人为了统一对OS底层函数的调用重新 定义的一个接口函数,这个接口函数负责调用OS底层函数完成相应功能。在后面的模拟层接口 函数实现中,笔者使用了很多这样的 API。这些接口函数都以 OSAPI 作为函数名前缀,其实现 细节请参看\src\uCOS_II\API\os_api.c檔,本文不再赘述。 - sys_sem_signal() //*-------------------------------------------------------------------------- //* 函数名称 : sys_sem_signal //* 功能描述 : 发送信号 //* 入口参数 : [in] sem指定要发送的信号 //* 出口参数 : 无 //*-------------------------------------------------------------------------- - 8 -
分享到:
收藏