实时操作系统uC/0S II下TCP/IP协议栈的实现

  • 1 引言

随着嵌入式系统与网络的日益结合,在嵌 入式实时操作系统中引入TCP/IP协议栈,以支持嵌入式设备接入网络,成为嵌入式领域重要的研究方向。uC/0S II是近年来发展迅速的一个开放源码实时操作系统,但它只是一个实时的任务调度及通信内核,缺少对外围设备和接口的支持,如没有文件系统、网络协议、图形 界面。笔者在多个嵌入式项目的开发过程中,以开源TCP/IP协议栈LwIP为基础,给uC/0S II加上了网络支持。下面就以uC/0S II +LwIP分别在8位MCU ez80和32位MCU ARM7TDMI上的实现为例进行说明。
需要说明的是,笔者使用的ez80系统是 Zilog公司的ez80190开发板,自带网络芯片。而ARM7系统是使用笔者参与开发的Skyeye,一个基于GDB的ARM7TDMI指令级软件仿 真器。Skyeye小组最近为Skyeye加上了软件模拟的Ne2k兼容网络芯片,可以运行带网络支持的μcLinux和uC/0S II。以下的全部相关程序和代码都可以在Skyeye网站(hpclab.cs.tsinghua.edu.cn/~skyeye/)下载。

  • 2 基于uC/0S II的网络平台概述

嵌 入式操作系统uC/0S II是一个公开源代码的占先式多任务的微内核RTOS,其性能和安全性可以与商业产品竞争。uC/0S II的特点可以概括为以下几个方面:公开源代码,代码结构清晰、明了,注释详尽,组织有条理,可移植性好。可裁剪,可固化。内核属于抢占式,最多可以管理 60个任务。uC/0S II自1992年的第一版(uC/0S)以来已经有好几百个应用,是一个经实践证明好用且稳定可靠的内核。目前国内对uC/0S II的研究和应用都很多。
TCP/IP是Internet的基本协议,以其实用性、高效性已经成为事实上的工业标准。嵌入式设备要与Internet网络直接交换信息,就必须支持TCP/IP协议。目前嵌入式设备上TCP/IP方案有很多种,但面向低端应用的开源嵌入式网络平台还很少见。
uC/0S II是一个富有开放色彩的RTOS,只要买一本书就可获得源代码,对学校和教育的使用完全免费,商业应用的费用相对也很低。但是它目前的一些第三方 TCP/IP支持都是完全商业化的,用户需要付费才能获得,很少给出源代码,这影响了uC/0S II的研究和推广。通过把开放源代码的TCP/IP协议栈LwIP移植到uC/0S II上来,就获得了一套可免费研究、学习的嵌入式网络软件平台。系统示意图如图1:

实时操作系统uC/0S II下TCP/IP协议栈的实现 - cl2006ky@126 - 一生有你
图1 uC/0S II+LwIP系统示意图

  • 3 开源TCP/IP协议栈LwIP简介

LwIP 是瑞士计算机科学院(Swedish Institute of Computer Science)的Adam Dunkels等开发的一套用于嵌入式系统的开放源代码TCP/IP协议栈。LwIP的含义是Light Weight(轻型)IP协议。LwIP可以移植到操作系统上,也可以在无操作系统的情况下独立运行。LwIP TCP/IP实现的重点是在保持TCP协议主要功能的基础上减少对RAM的占用,一般它只需要几十K的RAM和40K左右的ROM就可以运行,这使 LwIP协议栈适合在低端嵌入式系统中使用。
LwIP的特性如下:
(1) 支持多网络接口下的IP转发
(2) 支持ICMP协议
(3) 包括实验性扩展的的UDP(用户数据报协议)
(4) 包括阻塞控制,RTT估算和快速恢复和快速转发的TCP(传输控制协议)
(5) 提供专门的内部回调接口(Raw API)用于提高应用程序性能
(6) 可选择的Berkeley接口API(多线程情况下)
我们目前使用的是LwIP的最新稳定版V0.5.3。有关LwIP的详细内容,可以参考其代码和网站上的文档。

  • 4 LwIP在uC/0S II下的实现

4.1 概述
LwIP 协议栈在设计时就考虑到了将来的移植问题,因此把所有与硬件、OS、编译器相关的部份独立出来,放在/src/arch目录下。因此LwIP在uC/0S II上的实现就是修改这个目录下的文件,其它的文件一般不应该修改。下面分几部份分别说明相应文件的实现原理和过程。具体的代码限于篇幅没有给 出,Skyeye网站上有完整的代码和说明。
4.2 与CPU或编译器相关的include文件
/src/arch/include/arch目录下cc.h、cpu.h、perf.h中有一些与CPU或编译器相关的定义,如数据长度,字的高低位顺序等。这应该与用户实现uC/0S II时定义的数据长度等参数是一致的。
#define BYTE_ORDER LITTLE_ENDIAN //ARM7默认为小端存储系统
//数据类型长度的定义
typedef unsigned char u8_t;
typedef signed char s8_t;
typedef unsigned short u16_t;
typedef signed short s16_t;
typedef unsigned int u32_t;
typedef signed int s32_t;
此 外还有一点:一般情况下C语言的结构体struct是4字节对齐的,但是在处理数据包的时候,LwIP使用的是通过结构体中不同数据的长度来读取相应的数 据的,所以,一定要在定义struct的时候使用_packed关键字,让编译器放弃struct的字节对齐。LwIP也考虑到了这个问题,所以,在它的 结构体定义中有几个PACKED_FIELD_xxx宏,默认的时候这几个宏都是空的,可以在移植的时候添加不同的编译器所对应的_packed关键字。 比如在Skyeye(ARM7)上对应gcc编译器的定义:
#define PACK_STRUCT_FIELD(x) x __attribute__((packed))
#define PACK_STRUCT_STRUCT __attribute__((packed))
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_END
4.3 sys_arch操作系统相关部份
sys_arch.[ch]中的内容是与OS相关的一些结构和函数,主要可以分为四个部份:
(1) sys_sem_t 信号量
LwIP中需要使用信号量通信,所以在sys_arch中应实现信号量结构体和处理函数:
struct sys_sem_t
sys_sem_new() //创建一个信号量结构
sys_ sem _free() //释放一个信号量结构
sys_ sem _signal() //发送信号量
sys_ arch_sem _wait() //请求信号量
由于uC/0SII已经实现了信号量OS_EVENT的各种操作,并且功能和LwIP上面几个函数的目的功能是完全一样的,所以只要把uC/0SII的函数重新包装成上面的函数,就可以直接使用了。
(2) sys_mbox_t 消息
LwIP使用消息队列来缓冲、传递数据报文,因此要在sys_arch中实现消息队列结构sys_mbox_t,以及相应的操作函数:
sys_mbox_new() //创建一个消息队列
sys_mbox_free() //释放一个消息队列
sys_mbox_post() //向消息队列发送消息
sys_arch_mbox_fetch() //从消息队列中获取消息
uC/0SII同样实现了消息队列结构OSQ及其操作,但是uC/0SII没有对消息队列中的消息进行管理,因此不能直接使用,必须在uC/0SII的基础上重新实现。为了实现对消息的管理,我们定义了以下结构:
typedef struct {
OS_EVENT* pQ;
void* pvQEntries[MAX_QUEUE_ENTRIES];
} sys_mbox_t;
在 以上结构中,包括OS_EVENT类型的队列指针(pQ)和队列内的消息(pvQEntries)两部分,对队列本身的管理利用uC/0SII自己的 OSQ操作完成,然后使用uC/0SII中的内存管理模块实现对消息的创建、使用、删除回收,两部分综合起来形成了LwIP的消息队列功能。
(3) sys_arch_timeout 函数
LwIP 中每个与外界网络连接的线程都有自己的timeout属性,即等待超时时间。这个属性表现为每个线程都对应一个sys_timeout结构体队列,包括这 个线程的timeout时间长度,以及超时后应调用的timeout函数,该函数会做一些释放连接,回收资源的工作。如果一个线程对应的 sys_timeout为空(NULL),说明该线程对连接做永久的等待。
timeout结构体已经由LwIP自己在sys.h中定义好了,而且对结构体队列的数据操作也由LwIP负责,我们所要实现的是如下函数:
struct sys_timeouts * sys_arch_timeouts(void)
这个函数的功能是返回目前正处于运行态的线程所对应的timeout队列指针。timeout队列属于线程的属性,因此是OS相关的函数,只能由用户实现。
(4) sys_thread_new 创建新线程
LwIP可以是单线程运行,即只有一个tcpip线程(tcpip_thread),负责处理所有的tcp/ucp连接,各种网络程序都通过tcpip线程与网络交互。但LwIP也可以多线程运行,以提高效率,降低编程复杂度。这时就需要用户实现创建新线程的函数:
void sys_thread_new(void (* thread)(void *arg), void *arg);
在uC/0S II中,没有线程(thread)的概念,只有任务(Task)。它已经提供了创建新任务的系统API调用OSTaskCreate,因此只要把 OSTaskCreate封装一下,就可以实现sys_thread_new。需要注意的是LwIP中的thread并没有uC/0S II中优先级的概念,实现时要由用户事先为LwIP中创建的线程分配好优先级。
4.4 lib_arch中库函数的实现
LwIP协议栈中用到了8个外部函数,这些函数通常与用户使用的系统或编译器有关,因此留给用户自己实现。如下:
u16_t htons(u16_t n); //16位数据高低字节交换
u16_t ntohs(u16_t n);
u32_t htonl(u32_t n); //32位数据大小头对调
u32_t ntohl(u32_t n);
int strlen(const char *str); //返回字符串长度
int strncmp(const char *str1, const char *str2, int len); //字符串比较
void bcopy(const void *src, void *dest, int len); //内存数据块之间的互相拷贝
void bzero(void *data, int n); //内存中指定长度的数据块清零
前四个函数通常由用户自己实现。Skyeye(ARM7)中,由于使用了gcc编译器,gcc的lib库里已经有了后四个函数。而ez80的编译器函数库中缺少bcopy和bzero两个,需要自己编写。用户在其它CPU上实现时应根据自己的编译器来决定。

4.5 网络设备驱动程序
ez80 开发板自带的网络芯片为RealTek的8019as芯片,这是ISA 10BASE-T的以太网芯片,与Ne2k兼容。而我们在AT91模拟器Skyeye中所仿真的网络芯片也是Ne2k,所以目前实现的网络设备驱动是针对 Ne2k的,其它类型的网络芯片驱动可以在LwIP的网站上找到。LwIP的网络驱动有一定的模型,/src/netif/ethernetif.c 文件即为驱动的模板,用户为自己的网络设备实现驱动时应参照此模板。
在LwIP中可以有多个网络接口,每个网络接口都对应了一个struct netif,这个netif包含了相应网络接口的属性、收发函数。LwIP调用netif的方法netif->input()及 netif->output()进行以太网packet的收、发等操作。在驱动中主要做的,就是实现网络接口的收、发、初始化以及中断处理函数。驱 动程序工作在IP协议模型的网络接口层,它提供给上层(IP层)的接口函数如下:
//网卡初始化函数
void ethernetif_init(struct netif *netif)
//网卡接收函数,从网络接口接收以太网数据包并把其中的IP报文向IP层发送
//在中断方式下由网卡ISR调用
void ethernetif_input(struct netif *netif)
//网卡发送函数,给IP层传过来的IP报文加上以太网包头并通过网络接口发送
err_t ethernetif_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr)
//网卡中断处理函数ISR
void ethernetif_isr(void);
以上的函数都可以分为协议栈本身的处理和对网络接口硬件的操作两部份,但硬件操作是对上层屏蔽的,具体参见RTL8019as、DM9008等Ne2k网络芯片的数据手册。驱动程序可以到Skyeye或LwIP的网站下载。

  • 5 应用实例的建立和测试

做 完上面的移植修改工作以后,就可以在uC/0SII中初始化LwIP,并创建TCP或UDP任务进行测试了。这部份完全是C语言的实现,因此这部份在 ez80和ARM7上基本都是一样的。值得注意的是LwIP的初始化必须在uC/0SII完全启动之后也就是在任务中进行,因为它的初始化用到了信号量等 OS相关的操作。关键部份的代码和说明如下:
main(){
OSInit();
OSTaskCreate(lwip_init_task, &LineNo11, &lwip_init_stk[TASK_STK_SIZE-1], 0);
OSTaskCreate(usr_task,&LineNo12,&usr_stk[TASK_STK_SIZE-1],1);
OSStart();
}
主 程序中创建了lwip_init_task初始化LwIP任务(优先级0)和usr_task用户任务(优先级1)。lwip_init_task任务中 除了初始化硬件时钟和LwIP之外,还创建了tcpip_thread(优先级5)和tcpecho_thread(优先级6)。实际上 tcpip_thread才是LwIP的主线程,多线程的Berkley API也是基于这个线程实现的,即上面的tcpecho_thread线程也要依靠tcpip_thread线程来与外界通信,这样做的好处是编程简单, 结构清晰。
实用Berkley API实现的tcpecho_thread是一个TCP echo服务器,监听7号端口,程序框架如下:
void tcpecho_thread(void *arg){
conn = netconn_new(NETCONN_TCP); //创建新的连接标识
netconn_bind(conn, NULL, 7); //绑定到7号端口
netconn_listen(conn); //开始监听端口
while(1){
newconn = netconn_accept(conn); //接收外部到来的连接
buf = netconn_recv(newconn) //获取数据
……. //处理数据
netconn_write(newconn, data, len, NETCONN_COPY); //发送数据
netconn_delete(newconn); //释放本次连接
}
}
编译运行后,用ping ip地址命令可以得到ICMP reply响应。用telnet ip地址 7(登录7号端口)命令可以看到echo server的回显效果。说明ARP、ICMP、IP、TCP协议都已正确运行。

文档共60页。主要向初学者提供了Zynq开发的技术方向,针对不同应用给出了基本的参考文档;同时对Zynq双核AMP加载方式做了详细描述,对Zynq的fsbl启动流程做了简单介绍。章节如下: Zynq User Guide 1 介绍 4 2 快速上手指南 4 3 多核开发教程 4 3.1 AMP开发说明 6 3.1.1 快速生成amp工程 6 3.1.2 Generating Boot File 8 3.1.3 烧写程序 9 3.1.4 启动 10 3.1.5 调试 10 3.1.6 总结 11 3.2 SMP开发说明 11 4 ZC706启动代码分析 11 4.1 启动代码 12 4.2 FSBL流程(FOR AMP) 13 4.3 CPU0启动CPU1流程 14 5 程序在线烧写方案及流程 14 5.1 程序烧写需求 14 5.2 提出该需求的原因 14 5.3 程序烧写方案 14 5.3.1 BOOT.BIN组成 14 5.3.2 BOOT.BIN生成方法 15 5.4 FSBL.BIN和APP.BIN等的生成 15 5.5 制作*BIN及烧写的具体步骤 15 5.5.1 制作*bin流程 15 5.5.2 BOOT.bin制作过程 15 5.5.3 FSBL.bin和APP.bin等的生成过程 22 5.6 烧写BOOT.BIN步骤 26 5.6.1 通过SDK工具烧写步骤 26 5.6.2 通过上位机烧写软件的烧写步骤 29 5.6.3 通过串口调试助手烧写步骤 29 6 Zynq Qspi控制器 30 6.1 基本特性 30 6.2 I/O接口 31 6.3 QSPI控制器模式 33 6.3.1 I/O模式 33 6.3.2 线性地址(linear address)模式 33 6.3.3 传统(legacy)SPI模式 34 6.4 QSPI 例程 34 6.5 QSPI控制器支持访问32MB方法 35 6.5.1 Bank地址寄存器(Bank address register) 35 6.5.2 扩展地址模式(Extended address mode) 35 6.5.3 使用新写命令(New commands) 35 6.6 QSPI FLASH选择 35 6.7 作为BOOT器件考虑 35 7 µC/OS系统启动指南 36 7.1 INTRODUCTION 36 7.1.1 Software Requirements 36 7.1.2 Hardware Requirements 36 7.2 HARDWARE DESIGN 37 7.2.1 Step 1. Invoke the Vivado IDE and Create a project 37 7.2.2 Step 2. Create an IP Integrator Design 39 7.2.3 Step 3. Add and setup the Zynq processor system IP block 39 7.2.4 Step 4. Customize the Zynq block for our design 41 7.2.5 Step 5. Add the soft peripherals 45 7.2.6 Step 6. Generate HDL Design Files 47 7.2.7 Step 7. Synthesis, Implement and Generate Bitstream 48 7.3 SOFTWARE DESIGN 49 7.3.1 Step 1. Installation of the µC/OS Repository 49 7.3.2 Step 2. Generate the µC/OS BSP 50 7.3.3 Step 3. Build and Debug the Demonstration Project 54 7.3.4 Step 4. Program the AXI Timer 0 with the ucos_axitimer Driver 55 7.3.5 Step 5. Program the AXI Timer 1 with the Xilinx tmrctr Driver 58 7.4 CONCLUSION 59 8 Linux系统启动指南 59
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值