LWIP+UCOSIII学习笔记.docx
《LWIP+UCOSIII学习笔记.docx》由会员分享,可在线阅读,更多相关《LWIP+UCOSIII学习笔记.docx(62页珍藏版)》请在冰豆网上搜索。
LWIP+UCOSIII学习笔记
1.网络芯片比较
目前使用的网络芯片一般有以下几种:
DP83848、DM9000、enc28j60、RLD8019、w5100
网卡工作在osi的最后两层,物理层(PHY)和数据链路层(MAC)。
物理层定义了数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。
物理层的芯片称之为PHY。
数据链路层则提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层提供标准的数据接口等功能。
以太网卡中数据链路层的芯片称之为MAC控制器。
1.DP83848:
物理层(PHY),跟MII接口。
2.DM9000:
物理层(PHY)和数据链路层(MAC)(10/100M)。
跟8/16/32总线接口
3.enc28j60:
MAC+PHY(10MBaseT)。
spi接口
4.w5100:
硬件TCP/IP协议栈+MAC+PHY(10/100MBaseT)。
并行总线接口
5.RLD8019:
和w5100类似,比较老。
举个例子:
W5100里面用硬件逻辑电路实现了TCP/IP的协议栈结构,不需要向ENC28J60这样的网络控制器那样还需要一个资源较大的MCU跑软件协议栈。
你直接把W5100当外部RAM使用,MCU初始化一下I/O,寄存器等就能使用了。
2.TCP/IP协议族的四个层次
网络协议通常分不同层次进行开发,每一层分别负责不同的通信功能。
一个协议族,比如TCP/IP,是一组不同层次上的多个协议的组合。
TCP/IP通常被认为是一个四层协议系统,如下图所示:
每一层负责不同的功能:
1)链路层,有时也称作数据链路层或网络接口层,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。
它们一起处理与电缆(或其他任何传输媒介)的物理接口细节。
2)网络层,有时也称作互联网层,处理分组在网络中的活动,例如分组的选路。
在TCP/IP协议族中,网络层协议包括IP协议(网际协议),ICMP协议(Internet互联网控制报文协议),以及IGMP协议(Internet组管理协议)。
3)运输层主要为两台主机上的应用程序提供端到端的通信。
在TCP/IP协议族中,有两个互不相同的传输协议:
TCP(传输控制协议)和UDP(用户数据报协议)。
TCP为两台主机提供高可靠性的数据通信。
它所做的工作包括把应用程序交给它的数据分成合适的小块交给下面的网络层,确认接收到的分组,设置发送最后确认分组的超时时钟等。
由于运输层提供了高可靠性的端到端的通信,因此应用层可以忽略所有这些细节。
而另一方面,UDP则为应用层提供一种非常简单的服务。
它只是把称作数据报的分组从一台主机发送到另一台主机,但并不保证该数据报能到达另一端。
任何必需的可靠性必须由应用层来提供。
这两种运输层协议分别在不同的应用程序中有不同的用途,这一点将在后面看到。
4)应用层负责处理特定的应用程序细节。
几乎各种不同的TCP/IP实现都会提供下面这些通用的应用程序:
•Telnet远程登录。
•FTP文件传输协议。
•SMTP简单邮件传送协议。
•SNMP简单网络管理协议。
另外还有许多其他应用,在后面章节中将介绍其中的一部分。
假设在一个局域网(LAN)如以太网中有两台主机,二者都运行FTP协议,如下图列出了该过程所涉及到的所有协议。
3.LWIP协议
随着嵌入式系统功能的多样化以及网络在各个领域的中的广泛应用,具备网络功能的嵌入式设备拥有更高的使用价值和更强的通用性。
然而大部分嵌入式设备使用经济型处理器,受内存和速度限制,资源有限,不需要也不可能完整实现所有的TCP/IP协议,有时只需要满足实际需求就行。
LwIP是由瑞典计算机科学研究院开发的轻量型TCP/IP协议栈,其特点是保持了以太网的基本功能,通过优化减少了对存储资源的占用。
LwIP是免费、开源的,任何人可以使用,能够在裸机的环境下运行,当然设计的时候也考虑了将来的移植问题,可以很容易移植到多任务操作系统中。
LwIP含义是lightweight(轻型)IP协议,在实现时保持了TCP协议的主要功能基础上减少对RAM的占用,一般它只需要几十K的RAM和40K左右的ROM就可以运行,这使LwIP协议栈很适合在低端嵌入式系统中使用。
LwIP协议栈的设计才用分层结构的思想,每一个协议都作为一个模块来实现,提供一些与其它协议的接口函数。
所有的TCP/IP协议栈都在一个进程当中,这样TCP/IP协议栈就和操作系统内核分开了。
而应用程序既可以是单独的进程也可以驻留在TCP/IP进程中,它们之间利用ICP机制进行通讯。
如果应用程序是单独的线程可以通过操作系统的邮箱、消息队列等,与协议栈进程通讯。
如果应用程序驻留在协议栈进程中,则应用程序可以通过内部回调函数和协议栈进程通讯。
3.1LwIP源代码文件目录
3.1.1目录
root@motadou:
/home/motadou/lwip/lwip-1.4.1#tree.
├──CHANGELOG
├──COPYING
├──doc
│├──contrib.txt
│├──FILES
│├──rawapi.txt
│├──savannah.txt
│├──snmp_agent.txt
│└──sys_arch.txt
├──FILES
├──README
├──src
│├──api
││├──api_lib.c
││├──api_msg.c
││├──err.c
││├──netbuf.c
││├──netdb.c
││├──netifapi.c
││├──sockets.c
││└──tcpip.c
│├──core
││├──def.c
││├──dhcp.c
││├──dns.c
││├──init.c
││├──ipv4
│││├──autoip.c
│││├──icmp.c
│││├──igmp.c
│││├──inet.c
│││├──inet_chksum.c
│││├──ip_addr.c
│││├──ip.c
│││└──ip_frag.c
││├──ipv6
│││├──icmp6.c
│││├──inet6.c
│││├──ip6_addr.c
│││├──ip6.c
│││└──README
││├──mem.c
││├──memp.c
││├──netif.c
││├──pbuf.c
││├──raw.c
││├──snmp
│││├──asn1_dec.c
│││├──asn1_enc.c
│││├──mib2.c
│││├──mib_structs.c
│││├──msg_in.c
│││└──msg_out.c
││├──stats.c
││├──sys.c
││├──tcp.c
││├──tcp_in.c
││├──tcp_out.c
││├──timers.c
││└──udp.c
│├──FILES
│├──include
││├──ipv4
│││└──lwip
│││├──autoip.h
│││├──icmp.h
│││├──igmp.h
│││├──inet_chksum.h
│││├──inet.h
│││├──ip_addr.h
│││├──ip_frag.h
│││└──ip.h
││├──ipv6
│││└──lwip
│││├──icmp.h
│││├──inet.h
│││├──ip_addr.h
│││└──ip.h
││├──lwip
│││├──api.h
│││├──api_msg.h
│││├──arch.h
│││├──debug.h
│││├──def.h
│││├──dhcp.h
│││├──dns.h
│││├──err.h
│││├──init.h
│││├──mem.h
│││├──memp.h
│││├──memp_std.h
│││├──netbuf.h
│││├──netdb.h
│││├──netifapi.h
│││├──netif.h
│││├──opt.h
│││├──pbuf.h
│││├──raw.h
│││├──sio.h
│││├──snmp_asn1.h
│││├──snmp.h
│││├──snmp_msg.h
│││├──snmp_structs.h
│││├──sockets.h
│││├──stats.h
│││├──sys.h
│││├──tcp.h
│││├──tcp_impl.h
│││├──tcpip.h
│││├──timers.h
│││└──udp.h
││├──netif
│││├──etharp.h
│││├──ppp_oe.h
│││└──slipif.h
││└──posix
││├──netdb.h
││└──sys
││└──socket.h
│└──netif
│├──etharp.c
│├──ethernetif.c
│├──FILES
│├──ppp
││├──auth.c
││├──auth.h
││├──chap.c
││├──chap.h
││├──chpms.c
││├──chpms.h
││├──fsm.c
││├──fsm.h
││├──ipcp.c
││├──ipcp.h
││├──lcp.c
││├──lcp.h
││├──magic.c
││├──magic.h
││├──md5.c
││├──md5.h
││├──pap.c
││├──pap.h
││├──ppp.c
││├──pppdebug.h
││├──ppp.h
││├──ppp_impl.h
││├──ppp_oe.c
││├──randm.c
││├──randm.h
││├──vj.c
││└──vj.h
│└──slipif.c
├──test
│└──unit
│├──core
││├──test_mem.c
││└──test_mem.h
│├──etharp
││├──test_etharp.c
││└──test_etharp.h
│├──lwip_check.h
│├──lwipopts.h
│├──lwip_unittests.c
│├──tcp
││├──tcp_helper.c
││├──tcp_helper.h
││├──test_tcp.c
││├──test_tcp.h
││├──test_tcp_oos.c
││└──test_tcp_oos.h
│└──udp
│├──test_udp.c
│└──test_udp.h
├──tt.xtx
└──UPGRADING
24directories,151files
3.1.2api文件夹源代码结构
为方便用户编程,LwIP为用户提供两种简单的高级API接口:
协议栈sequentialAPI和socketAPI。
这两种API实现的原理都是通过引进邮箱和信号量等通信与同步机制,来实现对内核中raw/callbackAPI函数的封装和调用。
也就是说,要使用这两种API,必须基于底层操作系统提供的邮箱和信号量机制,必须要在开发板上移植好操作系统。
文件
说明
api_lib.c
包含sequentialAPI函数的实现,主要包含预留给用户的编程接口
api_msg.c
包含sequentialAPI函数的实现,主要包含API消息的封装和处理函数
netbuf.c
包含上层数据包管理函数的实现
netdb.c
包含与主机名字转换相关的函数,主要在socket中被使用到
netifapi.c
包含上层网络接口管理函数的实现
sockets.c
包含socketAPI函数的实现
tcpip.c
提供了上层API与协议栈内核交互的函数,它是整个上层API功能得以实现的一个枢纽,其实现的功能可以理解为:
从API函数处接收消息,然后将消息递交给内核函数,内核函数根据消息做出相应的处理。
3.1.3netif文件夹源代码结构
netif文件夹主要包含了与底层网络接口相关的文件。
文件
说明
etharp.c
包含ARP协议的实现代码。
主要用来实现主机以太网物理地址到IP地址的映射。
ethernetif.c
包含了与以太网网卡密切相关的初始化、发送、接收等函数的实现。
这个文件夹中的函数并不能使用,它们都是一个框架性的结构,移植者需要根据自己使用的网卡特性来完成这些函数。
loopif.c
为协议栈提供回环网络接口功能。
使用这个接口可以实现本地两个进程之间的数据传递。
slipif.c
SLIP(串行链路IP),提供一种在串行链路上传送IP数据包的函数定义,移植者需要根据自己使用的串行线路特性来实现这些函数。
3.1.4core文件夹源代码结构
core文件夹是LwIP内核源代码,包含IP、ICMP、IGMP、TCP、UDP等核心协议以及建立在它们基础上的DNS、DHCP、SNMP等上层应用协议。
内核源代码可以单独运行,且不需要操作系统的支持。
我们先看下core根目录下的文件作用说明,然后再分析子目录中过的文件。
文件
说明
dhcp.c
包含DHCP(动态主机配置协议)客户端的实现代码
dns.c
包含DNS(域名系统)客户端的实现代码
init.c
包含了一个与LwIP协议栈初始化密切相关的函数,以及一些协议栈配置信息的检查与输出
mem.c
协议栈内存堆管理函数的实现代码
memp.c
协议栈内存池管理函数的实现代码
netif.c
包含协议栈网络接口管理的相关函数,协议栈内部对每个接口(比如以太网接口、回环接口等)用一个对应的数据结构进行描述,并通过使用netif.c中的函数进行统一管理。
pbuf.c
包含协议栈内核使用的数据包管理函数,用于协议栈层次间的数据传递,避免数据拷贝
raw.c
原始套接字的实现代码,可以通过该文件中的函数直接操纵IP层数据包
stats.c
包含协议栈内部数据统计与显示的函数,比如内存使用状况、邮箱、信号量等信息
sys.c
实现对操作系统模拟层的封装,为协议栈提供统一的邮箱、信号量操作函数。
如果开发者需要使用协议栈的sequentialAPI和socketAPI,则必须使用底层操作系统提供的邮箱与信号量机制,这时内核要求移植者提供一个称为sys_arch.c的操作系统模拟层文件,这个文件主要完成对操作系统中邮箱与信号量函数的封装。
而sys.c文件的功能是将sys_arch.c中的函数再次封装,以得到具有协议栈特色的邮箱、信号量操作函数。
所谓特色,就是在这些函数中加入一种机制,以实现协议栈中各个定时事件的正确处理。
不使用sequentialAPI和socketAPI时,开发者不需要再实现sys_arch.c中的函数,sys.c对内核来说也没用了,因为此时系统不需要任何邮箱和信号量机制。
tcp.c
包含对TCP控制块操作的函数,也包含了TCP定时处理函数
tcp_in.c
包含TCP协议数据接收、处理相关的函数,以及最重要的TCP状态机函数
tcp_out.c
包含TCP协议数据发送相关函数,例如数据包发送、超时重传函数等
udp.c
包含实现UDP协议的相关函数,包括控制块管理、数据包发送函数、数据包接收函数等
core/ipv4文件夹包含了IPv4标准中与IP层数据包处理相关的所有代码
文件
说明
autoip.c
包含IP地址自动配置相关函数,若主机从DHCP服务器处获取IP地址失败,则此时主机可以选择启动AUTOIP功能来配置自身的IP地址。
AUTOIP将主机配置为169.254.0.0/16中的某个地址,并提供一套完整的机制来避免IP地址冲突
icmp.c
包含ICMP(网际控制报文协议)协议的实现代码。
ICMP协议为IP数据包传递过程中的差错报告、差错纠正以及目的地址可达性测试提供了支持,常见的Ping命令就属于ICMP应用中的一种。
igmp.c
包含IGMP(网络组管理协议)协议的实现代码。
IGMP为网络中的多播数据传输提供了支持,主机加入某个多播组后,可以接收到改组的UDP多播数据。
inet.c
包含IP层使用到的一些功能函数的定义,如IP地址的转换、网络字节序与主机字节序转换等
inet_chksum.c
包含对IP数据包校验相关的函数
ip.c
包含IPv4协议实现的相关函数,如数据包的接收、递交、发送等
ip_addr.c
包含一个判断IP地址是否为广播地址的函数
ip_frag.c
提供了IP层数据包分片与重组相关的函数
3.2LwIP协议栈开发嵌入式网络的三种方法分析
轻量级的TCP/IP协议栈LwIP,提供了三种应用程序设计方法,且很容易被移植到多任务的操作系统中。
LwIP协议栈在设计的时候就考虑到了将来的移植问题,因此把所有与硬件、操作系统、编译器有关的部分都全部独立起来,形成了一个操作系统模拟层。
操作系统模拟层用进程间的信号量、邮箱机制处理通信问题,而μC/OS是一个基于任务调度的嵌入式实时操作系统,因此移植LwIP协议栈到μC/OS,是很容易实现的。
LwIP提供了三种应用程序接口:
(1)低水平的,基于内核/回调函数的API(后面称RAWAPI)
(2)高水平的,连续的API(后面称LwIPAPI)
(3)BSD风格的套接字API(后面称BSDsocket)
可以在协议栈中通过对宏定义的不同配置,来决定使用哪种方式。
其中BSDsocket方式不是很成熟,RAWAPI需要编写回调函数,协议栈推荐使用LwIPAPI这种方式,但是三种方式到了底层都是通过回调函数实现的。
本文直接从RAWAPI入手,以建立TCP服务器端通信为例,详述底层的调用,然后再讲述后面的两种是如何封装而成的。
3.2.1基于RAWAPI的应用程序设计步骤
使用RAWAPI进行TCP/IP编程,可以使应用程序的代码和协议栈的代码很好地结合起来。
程序的执行机制是以回调函数为基础的事件驱动的,同时回调函数也是被TCP/IP代码直接调用的,回调函数、数据发送函数都需要自己编写。
这种方式是唯一的一种支持设备裸机运行,又可以完成网络通信完成系统功能。
裸机运行实际相当于是一个线程,而协议栈代码和应用程序代码通过先后次序处理,完成数据流转。
图1是使用RAWAPI方式,多任务系统实现TCP服务器端通信的步骤。
图1RAWAPI方式应用程序设计
LwIP协议栈中的tcp块结构有两种TCP_PCB和TCP_PCB_LISTEN,前者在内存池中的默认个数是5,后者是8,其中listen型的结构占用少量的内存,专门用于处理在侦听状态的tcp块结构。
tcp_listen函数中,释放tcp_new创建的块结构,而是返回一个listen型的tcp块结构。
客户端连接,到达TCP层,在tcp_listen_input函数中,重新创建一个TCP_PCB块结构,专门用于和客户端通信。
侦听到客户端连接,完成三次握手后,回调自己编写的接收函数,然后将全局的指针指向与客户端通信的块结构,在数据发送时,使用这个指针,就是在用这个块结构与客户端通信。
由上面看出,这种方式最大的特点是减少了任务之间的切换,只要数据来到协议栈线程,通过回调的方式就可以完成数据的处理。
3.2.2基于LwIPAPI的应用程序设计
LwIPAPI方式的编程,是基于上面的RAWAPI的,封装了一个netconn的结构,所有操作不在针对TCP块结构,而变成了netconn型的结构变量。
操作都需要协议栈去处理,应用程序与协议栈通信,通过发送消息方式进行,因此这种方式会造成频繁的任务切换,速度相比RAWAPI慢了许多,使用步骤如图2所示。
图2LwIPAPI方式应用程序设计
3.2.3基于BSDsocket的应用程序设计
BSDsocket相当于对LwIPAPI做了一层封装,而netconn结构有一个变量是socket,这样两者很容易结合起来。
Socket方式很容易被理解,编写应用程序也较为容易,但是效率低,消耗的资源更多,使用步骤如图3所示。
图3BSDsocket方式应用程序设计
3.3LwIP-API函数
3.3.1RawAPIfunctions
APIfunction
Description
TCPconnectionsetup
tcp_new
CreatesanewTCPPCB(protocolcontrolblock).
tcp_bind
BindsaTCPPCBtoalocalIPaddressandport.
tcp_listen
StartsthelisteningprocessontheTCPPCB.
tcp_accept
AssignsacallbackfunctionthatwillbecalledwhenanewTCPconnectionarrives.
tcp_accepted
InformsthelwIPstackthatanincomingTCPconnectionhasbeenaccepted.
tcp_connect
UsedtoconnecttoaremoteTCPhost.
SendingTCPdata
tcp_write
Queuesupdatatobesent.
tcp_sent
Assignsacallbackfunctionthatwillbecalledwhensentdataisacknowledgedbytheremotehost.
tcp_output
Forcesqueueddatatobesent.
ReceivingTCPdata
tcp_recv
Setsthec