ministm32开发板光盘alientek v3.0stm32f1lwip手册enc28j60版_第1页
ministm32开发板光盘alientek v3.0stm32f1lwip手册enc28j60版_第2页
ministm32开发板光盘alientek v3.0stm32f1lwip手册enc28j60版_第3页
ministm32开发板光盘alientek v3.0stm32f1lwip手册enc28j60版_第4页
ministm32开发板光盘alientek v3.0stm32f1lwip手册enc28j60版_第5页
已阅读5页,还剩142页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

淘宝淘宝技术支持(开源): →添加朋友 →输入“正点原子”→关→添加朋友→输入“alientek_stm32”→第一章LWIP无操作系统移植第二章LWIP带操作系统移植RAWTCP客户端实验RAWTCP服务器实验RAWWebServer实验第七章NETCONN编程接口简介第八章NUDP TCP/IP协议以及LWIP简 ENC28J60简 第二章LWIP带操作系统移 修改cc.h文 修 第三章RAW编程接口UDP实 RAW编程接口UDP简 第四章RAW编程接口TCP客户端实 第五章RAW编程接口TCP服务器实 第六章RAW编程接口WEBSERVER实 CGI实 SSI实 NETCONNAPI函 第八章NETCONN编程接口UDP实 8.2验 第九章NETCONN编程接口TCP客户端实 9.2验 第十章NETCONN编程接口TCP服务器实 10.2验 本章,向大家介绍ALIENTEK的ENC28J60以太网模块及其使用。ALIENTEK的MiniSTM32STM32精英板都没有网络模块,如果要想在这两款开发板上实现网络功能的话就需要使用外置的网络,并且需要TCP/IP协议栈的支持。我们可以使用ALIENTEK的TCP/IPLWIP结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。通俗而言:TCP负责发现传IP是给因特网的每一台联网设备规定一个地址。分层模型方面来讲,TCP/IP由四个层次组成:网络接口层、网络层、传输层、应用层。OSI是网络层(网络层、传输层(传输层、会话层、表示层和应用层(应用层。TCP/IP模型与OSI模型对比如表1.1.1所示。12345671.1.1TCP/IP模型与OSILWIP实验中DM9000相当于PHY+MACLWIP提供的就是网络层、传输LWIP是瑞典计算机(SICS)的AdamDunkels等开发的一个小型开源的TCP/IP协议栈。LWIP是轻量级IP协议,有无操作系统的支持都可以运行,LWIP实现的重点是在保持TCP协RAMKBRAM40KROM就可以运行,这使LWIP协议栈适合在的嵌入式系统中使用。目前LWIP的版本是1.4.1。1.4.1版本的LWIPLWIP这个去查阅,LWIP的主要特性如下打开从官网上下来的LWIP1.4.1其中包括docsrc和test三个文件夹和5个其他文件。doc文件夹下包含了几个与协议栈使用相关的文本文档,doc文件夹里面有两个比较重要的文档:rawapi.txt和sys_arch.txtrawapi.txt告诉读者怎么使用raw/callbackAPI进行编程,sys_arch.txttestLWIP提供的一些测试程序,这里用不到。打开src源码文件夹,如图1.1.2所示。ENC28J60是带有行业标准串行外设接口(SerialPeripheralInterface,SPI)的独立以太网SPI的控制器的以太网接口。ENC28J60IEEE802.3的全部规范,采用了一系列滤机制以对传入数据包进行限制。它还提供了一个内部DMA模块,现,数据传输速率高达10Mb/s。两个的引脚用于连接LED,进行网络活动状态指示。8K数据接收和发送双端口RAMTTLENC28J601.2.1ENC28J60、SPIENC28J60RAM、判优器,当DMA、发送和接收模块发出请求时对RAM缓冲器的进行控制SPI、MAC(MediumAccessControl)IEEE802.3MAC5VI/O引脚)和系统控制逻辑。ENC28J601.2.2

ALIENTEKENC28J60网络模块采用ENC28J60作为主,单即可实现以太网接入,利用该模块,基本上只要是个单片机,就可以实现以太网连接。ALIENTEKENC28J60网络模块原理图如图1.3.1所示:ALIENTEKENC28J601.3.2该模块通过一个8个引脚的排针与外部电路连接,这8个引脚分别是:GNDRST、MISOSCKMOSIINTCS和V3.3GNDV3.3MISO/MOSI/SCK用于SPI通信,CS是片选信号,INT为中断输出引脚,RST为模块复位信号。在本节和后续所有有关LWIP的中需要使用到动态内存管理实验的知识,因此请读者务必了解动态内存管理实验,此只针对LWIP,不会对其他知识做讲解。发指南中的USMART实验章节(战舰,精英,Mini任何一个开发手册都可以)。网 这两个文件,如图1.4.1所示为我们需的两个文件。其中lwip-1.4.1.zip为LWIP的源码,contrib-1.4.1.zip包含的一些例我们已经好了这两个文件放在光盘中,路径为:6、软件资料->4、LWIP学习资料。目前的版本是1.4.1,本所有的实验均使用1.4.1版本的LWIP。1LWIPHARDWAREENC28J60enc28j60.cenc28j60.hENC28J60ENC28J60HARDWARE文件中,并且将enc28j60.c添加到工程中,添加完成以后如图1.4.2所示。NRF24L01STM32MiniLWIP的例IO口不同,程序思路是一模一样的,在下面的所有章节中的讲解都是以精英板为例,如果是在Mini板上使用ENC28J60的话请注意IO配置的不同。enc28j60.h中还有另一个结构体dev_strucrt,结构体定义如下:typedef{u8enc28j60bank;u8macaddr[6];

dev_strucrtENC28J60的一些参数信息,比如当前使用的控制寄存器块、读指针地址、MACenc28j60.c文件,enc28j60.c16个函数,如表1.4.1所示。设置ENC28J60Bank获取ENC28J60通过ENC28J601.4.1enc28j60.c u8{u8version;u32temp;GPIO_InitTypeDefGPIO_InitStructure; EXTI_InitTypeDefEXTI_InitStructure; //PB12上拉防止W25XGPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);ALIENTEKENC28J60ALIENTEKENC28J60LWIP //PG87 GPIO_Init(GPIOG,&GPIO_InitStructure);//PG6外部中断,中断线6EXTI_InitStructure.EXTI_Line=EXTI_Line6;EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_LineCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //初始化SPISPI_Cmd(SPI2DISABLE);SPI外设不使能SPI_InitStructure.SPI_Direction= SPI_InitStructure.SPI_Mode=SPI_Mode_Master; //SPI主机SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b; //发送接收8位帧结构SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge; //数据捕获于第1个时钟沿SPI_InitStructure.SPI_NSS=SPI_NSS_Soft; //NSS信号由软件控制SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_256;//定义波特率预分频SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;//数据传输从MSB位开始SPI_InitStructure.SPI_CRCPolynomial=7; //CRC值计算的多项式 ;// {}if(retry>=250)return1;//ENC28J60初始化失败 printf("ENC28J60Version:%d\r\n",version); //ERXWRPTH:ERXWRPTLFIFO//FIFO内剩余空间的大小8K-1500//ANDOR1//1MAC//0= //ANDOR0//1MAC//0= //CRCEN:后过滤器CRC//1=CRC//0=不考虑CRC//ANDOR1//1//0= //ANDOR0//1//0= //bit0MARXEN:MAC//1=MAC//0= //1=MAC发送暂停控制帧(用于全双工模式下的流量控制//0= //1=当接收到暂停控制帧时 发送(正常操作//0=;//将MACON2中的MARST ,使MAC退出复位状态//bit7-5PADCFG2:PACDFG0:自动填充和CRC//111=064字节长,并追加一个有效的//110=//101MAC8100hVLAN64//VLAN60字节长。填充后还要追加一个有效的//100=//011=064字节长,并追加一个有效的//010=//001=060字节长,并追加一个有效的//000=//bit4TXCRCEN:发送CRC=//CRC,则必须将TXCRCEN1//bit0FULDPX:MAC//0=MAC工作在半双工模式下。PHCON1.PDPXMD位必须 //最大帧长度1518//设置MAC地址//PHY为全双工LEDB //PHCON1.PDPXMD1或PHCON1.PLOOPBK1//PHCON1.PDPXMD0且PHCON1.PLOOPBK0//1//0MAC//ECON1//3-1所示为ECON1//ENC28J60ECON1//送请求、DMA控制 //bit7INTIEINT//1=允许中 驱动INT引//0 INT引脚的活动(引脚始终被驱动为高电平//1=//0= //1//0=return0;}IO口,然后就是配置ENC28J60ENC28J60_Read_Op()和ENC28J60_Write_Op()分别为ENC28J60内部寄存器和u8ENC28J60_Read_Op(u8op,u8{u8dat=0;和向ENC28J60 ALIENTEKENC28J60LWIPreturn}{u8dat=0;}函数ENC28J60_Read_Buf()是从ENC28J60接收数据缓冲区中网络数据,函 voidENC28J60_Read_Buf(u32len,u8*data){{}}{u16 //向MIREGADR写入PHY寄存器地址 //写入数据的低8字节 //写入数据的高8字节 ALIENTEKENC28J60LWIP{{}u8ENC28J60_Read(u8{ENC28J60_Set_Bank(addrreturn}{}ENC28J60_PHY_Read和ENC28J60_PHY_Write()这两个函数是用来读写ENC28J60的PHYALIENTEKENC28J60LWIP}//ENC28J60的PHY寄存{u8u16 //低8 //高8return}voidENC28J60_Packet_Send(u32len,u8*{ {}//设置TXND指针,以对应给定的数据包大小ALIENTEKENC28J60LWIP//复位发送逻辑的问题。参见Rev.B4SiliconErratapoint12. }、ENC28J60在发送数据的时候不能被打断,包括中断也不行,这里调用函数{u32len; T)==0)return0; //读下一个包的指针 ALIENTEKENC28J60LWIP{}}函数ENC28J60_ISRHandlerDM9000的中断处理函数,注意!不是中断服务函数,函数ENC28J60_ISRHandler()会在STM32的中断服务函数中调用的。void{u8u16temp; { } {} { ALIENTEKENC28J60LWIP } {} }、进入中断以后先关闭ENC28J60void{{}}LWIP协议栈使用pbuf结构体来描述协议栈使用中的数据包,pbuf结构体在pbuf.hstructpbufvoid*payload;u16_ttot_len;u16_tlen;u8_ttype;u8_t

ALIENTEKALIENTEKENC28J60LWIPu16_tpayload:指向该pbuf的数据区的首地址,STM32F407内部网络模块接收到数据,并将数据提交给LWIP的时候,就是将数据在payload指定的区中。同样在发送数据的时候将payload所指向的区数据转给STM32F407的网络模块去发送。就是offset,在里面通常存放TCP报文首部,IP首部、以太网帧首部等等。 PBUF_POOLpbuf,PBUF_POOL是通过内存池分配的到的,PBUF_POOL类型的起始位置,原因同PBUF_RAM一样,用来存放一些首部的,pbuf链表后面的pbuf结构体中payload字段就指向了数据区的起始位置。

中有定义,定义如下,由于netifnetifnetif.hstructnetifstructnetif*next;ip_addr_tip_addr;ip_addr_tnetmask;ip_addr_tgw;netif_input_fninput;

ALIENTEKENC28J60LWIPALIENTEKENC28J60LWIPu16_t u8_t u8_tflags; charname[2]; u8_tnum; }LWIPnetif结构体组成链表来管理这些网络接口。ipaddr,netmaskgwIP地址、子网掩码和默认网关。input:此字段为一个函数,这个函数将网卡接收到的数据交给IP层。outputIP层向接口发送一个数据包时调用此函数。这个函数通常linkoutputARP模块调用,完成网络数据的发送。上面说的etharp_outputIP数据包封装成以太网数据帧以后就会调用linkoutput函数将数据发送能,ARP使能等。本节中LWIP源码添加到我们的工程中,我们在工程中新建一个LWIP文件夹,贝到LWIP文件夹下,如图1.4.5所示。照图1.4.7所示添加,这时如果我们编译的话会有很多错误提示,这个不用管。

打开我网络实验1LWIP无操作系统移植实验的LWIP文件夹可以发现有一个arch CC_H #include"cpu.h"#include"stdio.h"typedeftypedef CC_H #include"cpu.h"#include"stdio.h"typedeftypedeftypedeftypedeftypedeftypedeftypedefu32_t typedefint #ifOS_CRITICAL_METHOD==1#define#defineSYS_ARCH_PROTECT(lev) #defineSYS_ARCH_UNPROTECT(lev) #ifOS_CRITICAL_METHOD==#define u32_t#define lev=#defineSYS_ARCH_UNPROTECT(lev) #ifdefined(ICCARM)#definePACK_STRUCT_BEGIN#definePACK_STRUCT_STRUCT#definePACK_STRUCT_END#define#elifdefined #definePACK_STRUCT_BEGIN #definePACK_STRUCT_STRUCT#definePACK_STRUCT_END#elifdefined(GNUC)#definePACK_STRUCT_STRUCT ((packed))#definePACK_STRUCT_END#elifdefined(TASKING)#definePACK_STRUCT_BEGIN#definePACK_STRUCT_STRUCT#definePACK_STRUCT_ENDTEK#defineU16_F"4d"#defineS16_F"4d"#defineX16_F"4x"#defineU32_F"8ld"#defineS32_F"8ld"#defineX32_F#ifndefLWIP_TFORM_ASSERTdo\ printf("Assertion\"%s\"failedatline%din%s\r\n",,); #define #define #defineBYTE_ORDERperf.h是和系统测量与统计相关的文件,我们不使用任何的测量和统计,因此这个文件中#ifndef#ifndefPERF_H#definePERF_H#definePERF_START#defineu32_t{return}sys_arch.h和sys_arch.c是在使用操作系统的时候才使用到的文件,在这里我们只是在u32_t{return}加相应的头文件路径,如图1.4.8所示。ALIENTEKALIENTEKENC28J60LWIP打开我们的网络实验1LWIP无操作系统移植实验的LWIP文件夹可以发现有一个文件夹,将这个文件夹到自己工程中,lwip_app文件夹用来放我们以后所有实验的代码。在lwip_app下有一个m文件夹,这个文件中有 m.h和lwipopts.h这三个文件,m.c和m.h是将LWIP源码和前面的以太网驱动库结合起来的的文件,以后我们想要使用LWIP的什么功能的话就在这个文件中配置就行了。LWIP-APP分组,并将m.c文件添加到这个分组中并且添加相应的头文件路径,如图1.4.9所示在m.hALIENTEK定义了一个重要的结构体lwip_devALIENTEKENC28J60LWIPtypedef{u8 u8 u8 u8 u8 vu8 //dhcp//0,未获取DHCP地址//1,进入DHCP//2,成功获取DHCP MACIPIP地址、子网掩码、默认网关和DHCP状态这几个成员变量。在m.c中定义了一个lwip_dev结构体类型变量lwipdev,m_default_ip_set(lwip_devm_default_ip_set(lwip_devvoidlwip_pkt_handle(void)voidvoid首先是m_mem_malloc()函数,m_mem_malloc()mem.c {u32mempsize; //为ram_heap申请内存{ returnALIENTEKENC28J60LWIP}return}m_default_ip_set()函数用来设置默认地址,我们前面提过lwip_dev结构体变量lwipdev,m_default_ip_set()lwipdevMACMAC32、0、0。后三个字节我们MAC地址不会重复就行。IP地址、子网掩码、默认网关地址可以自行设置,这里我们设置IP地址为:0,子网掩码:,默认网关://lwip//lwipIP m_default_ip_set(lwip_dev{ALIENTEKENC28J60LWIPlwipx->dhcpstatus=0;//没有} {structnetif //netif_add()函数时的返回值,structip_addr structip_addr structip_addr m_mem_malloc())return1;//内存申请失败if(ENC28J60_Init())return2; //初始化ENC28J60 //初始化LWIP内 ipaddr.addr=0;netmask.addr= printf("静态IP地址 ALIENTEKENC28J60LWIP#ifLWIP_DHCP //DHCP标记为0dhcp_start(&lwip_netif);//开启DHCP服务{ }}1、调 m_mem_malloc()函数完成前面提到的内存堆ram_heap和内存2、调用ENC28J60_Init()函数完成对ENC28J60的初始化,ENC28J60_Init函数在enc28j60.c文件中,由ALIENTEK提供,前面已经介绍过了。3lwip_initLWIP的内核初始化,lwip_init()通过调用各个模块的初始化函函数、IPTCP等的初始化函数,lwip_init()init.c文件中,属于LWIP源码。 5、判断是否使用DHCP,如果使用DHCP的话就通过DHCP服务来获取IP地址、子网掩IP地址的话就用m_default_ip_se()函数设置的地址6、我们通过netif_add()函数来完成网卡的,netif_add()在netif.c文件中,此函数属于LWIP源码。netif_add()lwip_netif是我们定义的一个网络接口,这个函数除了使用上面说的IP地址,子网掩码和默认网关作为参数外,还使用了两个函数地址作为参数:ethernetif_init和ethernet_input,这两个函数地址会被赋值给netif结构体的相关字段,ethernetif_init()ALIENTEK提供,ethernet_input()etharp.c文件中,属于LWIP源码,是ARP层数据包输入函数。7、如果使用DHCPDHCPdhcp_start()DHCP{ }可以看出lwip_pkt_handle()函数其实只是对ethernetif_input()ALIENTEKENC28J60LWIPALIENTEKENC28J60LWIPvoid{)//{} }LWIP内核中有许多的周期性的定时器,相对应的定时处理函数也需要被周期性调用的,STM32F103定时器3提供这个系统时钟,定时器void{)//{} }LWIPTCP定时器、ARP定时器、IP如DHCP的话还有DHCP定时器等等,我们可以将这些定时器封装在一个函数里面,然后周期性这个函数就行了,LWIP250msTCP5S处理一ARP500msDHCP60sDHCP的粗糙处理。我们就根据这个要求来编写lwip_periodic_handle()函数,函数代码如下,最后我们只要周期性的调用lwip_periodic_handle()LWIP内核的定时处理函数的周期性调用。voidvoid{#if{TCPTimer= }{ARPTimer= }ALIENTEKENC28J60LWIP{DHCPfineTimer= { }}{DHCPcoarseTimer= }void{{case0: //开启DHCPprintf("正在查找DHCP服务器,请稍 case { { ALIENTEKALIENTEKENC28J60LWIP //DHCP获取到的子网掩码地址 {//使用静态IP地址 }ALIENTEKENC28J60LWIP}default:}}当DHCP完成以后让dhcpstatus=2,表示DHCP成功。但是当DHCP重试次数大于并且使用静态IP地址。打开我们的网络实验1LWIP无操作系统移植实验,LWIP->lwip1.4.1->src->include中我们会发现有个ethernetif.h文件,这个文件在LWIP源码中是不存在的,这个文件由ALIENTEK提供,将ethernetif.h文件到自己工程的相应位置,如图1.4.10所示

1.4.10ethernetif.hLWIPLWIP底层LWIPLWIP的源码做出一点小的改动,下面我们就讲解如何修改LWIP源码。我们按路径:lwip-1.4.1->src->core可以发现在core文件下有一个sys.clwip-1.4.1->src->include->lwipsys.h文件。sys.csys.h这两个文件和我们的SYSTEM文件中的sys.c和sys.h重名,因此LWIP中sys.c和sys.h改为lwip_sys.c和lwip_sys.h,然后在工程中将LWIP源码里面的#include“sys.h”代码也要改掉,更改为#includestatic low_level_init(structnetifstaticstatic low_level_init(structnetifstatic low_level_output(structnetif*netif,structpbufALIENTEKENC28J60LWIPstaticstructpbuf*low_level_input(structnetif*netif) ethernetif_input(structnetif*netif) ethernetif_init(structnetif由 staticerr_tlow_level_init(struct由 staticerr_tlow_level_init(structnetif{netif->mtu=1500;最大允许传输单元,允许该网卡广播和ARPreturn} staticerr_tlow_level_output(structnetif*netif,structpbuf{intl=0;u8 {ALIENTEKENC28J60LWIP} returnERR_OK;}staticstructpbuf*low_level_input(structnetif{staticstructpbuf*low_level_input(structnetif{u32len;intl=0; {returnp;} {{}} returnp;}ethernetif.c中还剩下两个函数:ethernetif_input()ethernetif_init()。ethernetif_input()函数主要是对low_level_input()函数做封装,然后将接收到的数据送入指定的网卡结构中。ALIENTEKENC28J60LWIP {err_terr; {p=}return} {#ifLWIP_NETIF_HOSTNAME//netif的name return}mem.cmemp.c将这两个数组改用ALIENTEK的内存分配函数对其进行内存分配。在mem.c文件中ram_heap数组注销掉,定义为指向u8_t的指针,如图1.4.11所示。1.4.11ram_heap数组改为指针(mem.c文件中1.4.12memp_memory数组改为指针(memp.c文件中u32_t{#includereturn}我们还要在u32_t{#includereturn}1.4.13memp_get_memorysize()函数(memp.c文件中 #define #define0#defineNO_SYS1//不使用UCOS#define4#define // #define #define0#defineNO_SYS1//不使用UCOS#define4#define //#define #define #define 的TCP连接数#define #define Pbuf选 //PBUF_POOL_SIZE:pbuf内存池个数.#definePBUF_POOL_SIZE #definePBUF_POOL_BUFSIZE TCP选 #define #define #defineTCP_QUEUE_OOSEQ #define (1500 //TCP_MSSMTUIPTCP//TCP发送缓冲区大小#define 发送缓冲区大小(pbuf).这个值最小为(2 #define (4*#define /*----------ICMP选 #define /*----------DHCP选 //当使用DHCP时此位应该为1,LwIP0.5.1版本中没有DHCP服务.#defineLWIP_DHCP UDP选 #define #define /*----------Statistics #defineLWIP_NETCONN //LWIP_SOCKET==1:使能SocketAPI(要求使用sockets.c)#defineLWIP_SOCKET #define 1//通过定义LWIP_SO_RCVTIMEO//#define ALIENTEKENC28J60LWIP#define #endif lwipopts.h中有很多的宏定义,每个宏定义后面已经给出了具体的解释,大需求来编写lwipopts.h里面的内容经过上面几节的讲解,LWIPmian.c文件来测试intint{u32i; //定时器3频率为100hz //初始化USMART //初始化外部SRAM POINT_COLOR=RED;LCD_ShowString(30,30,200,16,16,"ENC28J60+STM32");LCD_ShowString(30,50,200,16,16,"ENC28J60+STM32");{);//}LCD_ShowString(30,110,200,20,16,"LWIPInitSuccess!");#if ALIENTEKENC28J60LWIP{} {{}}}main函数中首先完成对外设的初始化,然后调用m_initLWIP,如获取地址失败的话将使用我们的默认的地址,最后我们在一个while循环中循环调用lwip_periodic_handle()lwip_pkt_handle()main函数中我们要注意到不管是在等待DHCP完成还是DHCP成功后我们都要周期性调用lwip_periodic_handle()和LWIP的内核要求。没有路由器的话也可以直接连接到电脑的RJ45接口上,由于DM9000具有自动翻转功能,所以连接电脑RJ45RJ45接口上那么开发板就不能使用DHCPIP地址:0,子网掩脑的网络设置,步骤如下,此处以WIN7系统为例。1.6.3 图1.6.4本地连接框 1.6.5本地连接属性IPIP地址在一个网络内!以后我们的实验都是连接到路由器上的,如果没有路由器的话都可以使用上面这种方法这种方法完成实验,只是不能使用DHCP服务。打开串口调试助手,复位一下开发板。这里我们开启了DHCP关闭DHCP使用静态IP地址,完成后LCD显示如图1.6.7所示1.6.7LCD1.6.8可以看到此时通过路由器的DHCP分配到的IP地址为:25.,默认网关为:,子网掩码为:。在电脑上开发板的IP地址,结果如图1.6.9所USMARTENC28J60USMART,打开串(ALIENTEKXCOM串口调试助手为例)USMART支持的函数,在USMARTENC28J60_Read()ENC28J60_Write()、ENC28J60_PHY_Read()和ENC28J60_PHY_Write(),如图1.6.10所示。我们在这里一下ENC28J60的PHCON1寄存器(0X00),可以查看一下当前网络的连接addr是需要的寄存器编号,在串口调试助手发送框输入ENC28J60_PHY_Read(0X00),点图1.5.11所示,我们可以看出此时ENC28J60的PHCON1寄存器值为0X100。图1.5.11ENC28J60的PHCON1寄存0X4C的二进制为,通过查阅ENC28J60的,我们知道寄存器PHCON1第二章LWIPLWIPLWIPAPIrawAPI,raw编程难度很大,需LWIPLWIPLWIPUCOS的LWIP移植。本章的移植是面无操作系统的基础上修改的,本章不讲解UCOS的移植,关于UCOS和m.c文件做简单修改,然后完成sys_arch.h和sys_arch.c这两个文件的编写。sys_arch.c要实现的宏定义和函数如表2.1.1所示。2.1.12.1.1中可以看出,sys_arch.c中要实现的主要是与消息邮箱、信号量和创建进程有关sys_thread_new函数创建一个进程,因此这个函数必须实现,实现过程也是对UCOS中的进程创建函数做简单的封装。cc.h#ifOS_CRITICAL_METHOD==1#define#defineSYS_ARCH_PROTECT(lev) #defineSYS_ARCH_UNPROTECT(lev) #define#define//UCOSII中退出A临界区,开中断

u32_t2.2.1includes.h头文件(cc.h文件中 #define #define1#define #define #define1#define#defineMEM_SIZE#define ALIENTEKALIENTEKENC28J60LWIP#define #define //MEMP_NUM_TCP_PCB: #define //MEMP_NUM_TCP_SEG:#define //pbuf#define #define //#define #define #undef#define #undef#define //tcpip#undef#define #undef#define #define (1500 //TCP#define #define (4* //(pbuf).这个值最小为(2#define #define #define ALIENTEKENC28J60LWIP#define #define #define #define #define //LWIP_NETCONN==1:NETCON#define #define #defineTCPIP_THREAD_PRIO 5 #defineTCPIP_THREAD_STACKSIZE 1000 #defineDEFAULT_UDP_RECVMBOX_SIZE #define #define #define 1、因为我们要使用UCOSII系统,所以NO_SYS02LWIP3、如果使用NETCONNLWIP_NETCONN1上面只是我们列举的一小部分配置,使用UCOS的话还需要很多的配置,具体配置大家参考我们的lwipopts.h文件。这里就不一一解释了。sys_arch.h#ifndefARCH_SYS_ARCH_H#defineARCH_SYS_ARCH_H#include<includes.h>#include#include中数据类型:sys_sem_t、sys_mutex_t、sys_mbox_tsys_thread_t。分别为信号量、互斥信号#ifndefARCH_SYS_ARCH_H#defineARCH_SYS_ARCH_H#include<includes.h>#include#includeALIENTEKENC28J60LWIP#defineSYS_ARCH_EXT#define //#define //typedefstruct ];////MAX_QUEUE_ENTRIES}TQ_DESCR,typedef typedef typedef //LWIP使用的消息邮箱,UCOStypedefINT8Usys_thread_t; err_tsys_mbox_new(sys_mbox_t*mbox,int{(*mbox)=mymalloc(SRAMIN,sizeof(TQ_DESCR));//为消息邮箱申请内存 //清除mbox的内存{ALIENTEKENC28J60ALIENTEKENC28J60LWIPif((*mbox)->pQ!=NULL)returnERR_OK;{ return }}elsereturn }void (sys_mbox_t*{u8_tsys_mbox_t }{//msgmsg等于pvNullPointer} err_tsys_mbox_trypost(sys_mbox_t*mbox,void{//msgmsg等于pvNullPointerreturn} u32_tsys_arch_mbox_fetch(sys_mbox_t*mbox,void**msg,u32_t{u8_tvoid*temp;{//转换为节拍数,UCOS延时使用的是节拍数,而LWIPmsif(ucos_timeout<1)ucos_timeout=1;//至少1个节拍}elseucos_timeout=//请求消息队列,等待时限为ucos_timeout{else*msg=temp;} {if(timeout_new>timeout)timeout_new=timeout_new-timeout;elsetimeout_new=0xffffffff-timeout+timeout_new;timeout=timeout_new*1000/OS_TICKS_PER_SEC+1;}return}u32_tsys_arch_mbox_tryfetch(sys_mbox_t*mbox,void{} {u8_tucErr;intOS_Q_DATAq_data;ucErr=OSQQuery(m_box->pQ,&q_data);returnret;}{}这里我们对sys_arch_mbox_fetch()函数做一下简单的讲解,根据LWIPsys_arch_mbox_fetch()timeout0时需要我们返回等待消息所使用的时间(ms),UCOSIIOSQPend()函数并没有这个功能,因此需要我们自行实现。因为在timeoutUCOS使用的节拍数。为了记录等待消息时用的此最后要将这个系统节拍数转化为ms,并返回该值。如果等待消息超时的话就直接返回 err_tsys_sem_new(sys_sem_t*sem,u8_t{u8_tif(*sem==NULL)returnERR_MEM;OSEventNameSet(*sem,"LWIPSem",&err);returnERR_OK;} ,{u8_t {//转换为节拍数,UCOS延时使用的是节拍数,而LWIPmsucos_timeout=(timeout*OS_TICKS_PER_SEC)/1000;if(ucos_timeout<1)ucos_timeout=}elseucos_timeout=0;timeout={timeout_new=if(timeout_new>=timeout)timeout_new=timeout_new-timeout;elsetimeout_new=0xffffffff-timeout+timeout_new;}return}{}void {u8_tucErr;*sem=} {OS_SEM_DATA}{}sys_arch_sem_wait函数为等待信号量函数,和sys_arch_mbox_fetch函数一样,sys_arch_sem_wait()函数也要返回待信号量所用的时间,这里我们的处理方法和在有操作系统的支持下,LWIPsys_thread_new函数来创建一个内核进程处理协调用创建tcpip内核线程,如果要创建其他任务一定要使用ALIENTEKENC28J60LWIPsys_thread_tsys_thread_new(constchar*name,lwip_thread_fnthread,void*arg,intstacksize,int{{ //创建TCPIP内核任务 }return}然后将其转换为LWIP使用的ms,并返回这个时间值。 m.c文创建DHCP删除DHCP表 {u32mempsize; //得到memp_memory数组大小 //为memp_memory申请内存printf("memp_memory内存大小为:%d\r\n",mempsize);ALIENTEKENC28J60LWIP+MEM_ALIGNMENT;//得到ramheap大小 printf("ram_heap内存大小为:%d\r\n",ramheapsize);{return}return} 与LWIP地址有关的信息。 {u8err;structnetif structip_addr structip_addr structip_addr m_mem_malloc())return1;//内存申请失败if(ENC28J60_Init())return2; //初始化ENC28J60 #ifLWIP_DHCP ipaddr.addr=0;netmask.addr=ALIENTEKENC28J60ALIENTEKENC28J60LWIP printf("静态IP地址 != { } #ifLWIP_DHCP }在这里使用了tcpip_init()来初始化LWIP内核,我们查看tcpip_init()函数可以看出其实在tcpip_init()lwip_init()netif_add()函数,与前面不同的是,这里将netif_add函数最后一个参数改为函数tcpip_input()。 行完成后就删除掉这个任务。关于DHCP有三个函数: m_dhcp_creat、m_dhcp_delete和lwip_dhcp_task。前两个函数比较好理解,就是建立和删除DHCP任务,lwip_dhcp_taskDHCP{u32 printf("正在查找DHCP服务器,请稍 {printf("正在获取地址..\r\n"); 到IP地址的时{ //解析通过DHCP获取到的子网掩码地址 ALIENTEKENC28J60LWIP }}m_dhcp_delete();//删除DHCP}lwipdevdhcpstatusDHCP的处理状态。当表示DHCP失败,并且使用静态IP地址。注意最后在DHCP任务执行完成后调用m_dhcp_delete()DHCPint{ int{ //初始化USMART POINT_COLOR=ALIENTEKENC28J60LWIP m_initlwip{);//}OSStart开启}start{pdata=pdata; start{pdata=pdata; );// }2.4.1LCD2.4.2可以看到此时通过路由器的DHCP分配到的IP地址为:25,默认网关为:,子网掩码为:。在电脑上开发板的IP地址,结果如图2.4.3所图2.4.2测第三章RAWUDPUDPTCP/IPTCP协议。UDP不提供数据包分组、组装,不能对数据包进频和普通数据传输时使用UDP较多。UDP数据报结构如图3.1.1所示。端表示发送和接收进程,UDP协议使用端为不同的应用保留各自的数据传输通,UDP和TCP协议都是采用端对同一时刻内多项应用同时发送和接收数据,而数据接收方则通过目标端口接收数据。有的网络应用只能使用预先为其预留或的静态端口;而另外一的有效范围是从0到65535。一般来说,大于49151的端都代表动态端口。的不同而各异。从理论上说,包含报头在内的数据报的最大长度为65535字节。LWIP的源码做了非常详细的讲解,想要研究LWIP源码的这本书,不过这本书中采uLWIP中RAWAPIUDPLWIP的RAWAPI编程方式是基于回调机制的,当我们初始化应用的时候须为内UDP的PCB通过一个PCB3.1.1UDP的RAWAPI不一一列举,关于UDP的其他函数大家可以参考udp.c文件。PCUDP协议连接起来,PC端使用网发板上的按键发送数据给PCudp_demo.c和udp_demo.h,这udp_demo.c这个文件,在这个文件中我们有5个函数,如表3.2.1所示。我们首先看一下voidvoid{u8*tbuf;u16xoff;u8key;LCD_ShowString(30,50,200,16,16,"UDPTest");LCD_ShowString(30,70,200,16,16,"RemoteIPSet"); ALIENTEKENC28J60LWIP{elseif(key){if(key==KEY1_PRES)lwipdev.remoteip[3]++;//IP增加if(key==KEY0_PRES)lwipdev.remoteip[3]--;//IP减少}}}过按键KEY0KEY1来设置需要连接的远端IP地址,这个函数很简单。bit5pNULL的时候,表示接收到数据,LWIP数据,接收函数为udp_demo_recv()voidudp_demo_recv(void*arg,structudp_pcbbit5pNULL的时候,表示接收到数据,LWIP数据,接收函数为udp_demo_recv()voidudp_demo_recv(void*arg,structudp_pcb*upcb,structpbuf*p,structip_addr*addr,u16_t{structpbuf*q;if(p!=NULL)//{ //遍历完整个pbuf链表{ALIENTEKALIENTEKENC28J60LWIPdata_len+=q->len;{}

lwipdev.remoteip[1]=(upcb->remote_ip.addr>>8)&0xff;//IADDR3udp_demo_flag|=1<<6;//标记接收到数据了 POINT_COLOR=RED;LCD_ShowString(30,50,200,16,16,"UDPTest");LCD_ShowString(30,90,200,16,16,"Connectbreak!");udp_demo_flag&=~(1<<5);//标记连接断开从上面的代码中可以看出当接收到数据以后我们先清除UDP的接收缓冲区数组:udp_demo_recvbuf。LWIP在接收数据的时候会通过pbuf_alloc()函数来申请内存,申请到的内组中。最后我们记录下远端主机的IP地址和端,标记接收到数据。voidudp_demo_senddata(structudp_pcb{structpbufvoidudp_demo_senddata(structudp_pcb{structpbuf{ALIENTEKENC28J60LWIP}}{ //断开UDP连接udp_demo_flag&=~(1<<5);//标记连接断开 POINT_COLOR=RED;LCD_ShowString(30,50,200,16,16,"UDPTest");}{ { {if(err==ERR_OK)//{ udp_demo_flag| ALIENTEKALIENTEKENC28J60LWIP}else}else}elseres=1;{{}{}{}}1。4、绑定成功以后使用udp_recv()函数UDP的接收函数,并且置位udp_demo_flag的的话令res等1。ALIENTEKENC28J60LWIPALIENTEKENC28J60LWIP两个函数那么LWIP的内核就不能运行,那么网络肯定不会工作!int{u8int{u8POINT_COLOR=RED; m_initlwip{);//}LCD_ShowString(30,110,200,20,16,"LWIPInitSuccess!");#ifLWIP_DHCP //使用DHCP{}//UDPALIENTEKENC28J60LWIP{key=if(key {elseudp_demo_test(); //当断开连接后,调用udp_demo_test()函数}}}main函数一开始完成外设的初始化,如果开启DHCPDHCPIP地址,IP地udp_demo_test()UDPudp_demo_test()函数while()KEY1按下并且UDPudp_demo_test()函数重新开始UDP实验。3.3验完代码后,打开网络调试助手,等待开发板的LCD3.3.7所示界面,在这个KEY_UP确认,确认完了以后LCD就如图3.3.8所示的数据接收界面。图 远端IP地址设3.3.8网络调试助手如图3.3.9所示,3.3.9 LCDKEY0,向电脑端发送数据:第四章RAWTCP本章采用RAWAPITCPTCPRAW编程接口TCPRAW编程接口TCPTCPIPTCPTCP层发送用于网间传输通常受该计算机连接的网络的数据链路层的最大传送单元(MTU)限制。之后TCP把数据包传给IP层,由它来通过网络将包传送给接收端的TCP层。TCP为了保证报文传输的可靠,就给每个包一个序号,同时序号也保证了传送到接收端的在数据正确性与上,TCP用一个校验和函数来检验数据是否有错误,在发送和接收动窗口协议,协议中规定,对于窗口内TCP拥TCP个值加上IP首部中的源IP地址和目的IP地址唯一确定一个TCP连接。TCPTCP接收端发送的数据字节流,它表示在这个报文ISN(InitialSequenceNumber),当连接建立好以后发送方要发送的第一个字节序号为ISN+1。ACK1的时候才有用,确认号中包含发送确认的一方所期望收到的32bit4字节为单位,4bitTCP60字节的首部,如果没有任何的选项字段,正常的首部长度是20字节。TCP16bit6553516位的校验和和UDP的校验和的计算过原理相同,这是一个强制性的字段,校验和覆盖了整个TCP报文段:TCP首部和TCP数据。URG1时有效,紧急指针是一个正的偏移量,和序号字段中的值相加LWIP中RAWAPITCPtcp.c、tcp.h、tcp_in.ctcp_out.cLWIPTCP协议的文件,TCP4.1.3API创建一个TCP的PCBTCP的PCBIP地址和端开始TCP的PCB发送TCP数接收TCP数用关闭一个TCP中断TCP

LWIP中关于TCP的函数,本节中我们就用这几个函数编写我们本章的例程,本章实验的目标是PC端和开发板通过TCP协议连接起来,开发板做TCP客户端,PCLCD上显文件tcp__demo.c和tcp__demo.h。在tcp__demo.c文件中我们一共定义了9个函数,如表4.2.1所示。tcp_ tcp_ tcp_tcp_tcp_tcp_关闭TCPerr_terr_t _connected(void*arg,structtcp_pcb*tpcb,err_t{structtcp_ {es=(struct //{ //estpcb//LwIPtcp_sent//LwIPtcp_poll{ }{ }return} TCP控制块中相关字段的回调函数,这样当有 {_recv(void*arg,structtcp_pcb*tpcb,structpbuf*p,err_tu32data_len=cp__e函数是当接收到数据时的回调函数,在这个函数中我们根据不同的状态有历的buf到cp__cbuf{_recv(void*arg,structtcp_pcb*tpcb,structpbuf*p,err_tu32data_len=ALIENTEKALIENTEKENC28J60LWIPstructpbufstructtcp_ err_tret_err; { ={ }elseif(es {{ //遍历完整个pbuf链表{ elsememcpy(tcp_ data_len+=q->len; } } { ALIENTEKENC28J60LWIP}return}{_poll(void*arg,structtcp_pcberr_tret_err;structtcp_{{_struct {_poll(void*arg,structtcp_pcberr_tret_err;structtcp_{{_struct { }{}return}_flag。要讲的tcp__senddata()这个函数,tcp__sent()函数代码如下。ALIENTEKENC28J60LWIPerr_ttcp__sent(void*arg,structtcp_pcb*tpcb,u16_tlen){structtcp__struct*es;returnERR_OK;}void{_senddata(structtcp_pcb*tpcb,void{_senddata(structtcp_pcb*tpcb,struct_struct*structpbuferr_t{{ //pbufref// }}void{_connection_close(structtcp_pcb*tpcb,structvoid{_connection_close(structtcp_pcb*tpcb,structALIENTEKENC28J60LWIP}tcppcb=tcp_new();//创建一个新的 {}elseres=1;{tcppcb=tcp_new();//创建一个新的 {}elseres=1;{{ } { } { }else {ALIENTEK}{{tcppcb=tcp_new();// {}}}}_connection_close(tcppcb,0);//关闭TCP连脑发送数据。通过判断tcp__flag的bit6来确定是否接收到数据,如果接收到数据的话就果不调用这两个函数那么LWIP的内核就不能运行,那么网络肯定不会工作!intint{u8 ALIENTEKALIENTEKENC28J60LWIP //初始化USMART //初始化外部SRAM POINT_COLOR=RED; //lwip初始化{);//}LCD_ShowString(30,110,200,20,16,"LWIPInitSuccess!");#ifLWIP_DHCP //使用DHCPwhile((lwipdev.dhcpstatus!=2)&&(lwipdev.dhcpstatus!=0XFFDHCP获取{}

{key=if(key { _flag&1<<5))printf("TCP客户端连接已经建立,不能重复连接\r\n");elsetcp_ //当断开连接后,调tcp_ }}}打开网络调试助手软件设置为如图4.3.1所示。4.3.1Connected的时候就可以和网络调试助手互相发送数据了。4.3.2IP4.3.3 ,此时开发板LCD上显示接收到的数据如图4.3.4所示,按下KEY0键向网络调试助手发送数据,网络调试助手接收到数据如图4.3.5所示。图 4.3.5第五章RAWTCP本章采用RAWAPITCPTCPRAW编程接口TCPRAW编程接口TCP写我们本章的例程,本章实验的目标是PC端和开发板通过TCP协议连接起来,开发板做TCPPC。本章实验中我们主要有两个文件tcp_server_demo.c和tcp_server_demo.h。tcp_server_demo.c文件中我们一共定义了9个函数,如表5.2.1所示。关闭TCPtcp_server_accept()函数为控制块a {err_tstructtcp_server_struct*es;));//if(es!=NULL){

////TCPServer主动断开时的timewaitvoidtcp_server_remove_timewait(void){structtcp_pcb*pcb,*pcb2;{ALIENTEKENC28J60LWIP returnret_err;}参考TCP客户端实验中关于这三个函数的讲解。tcp_server_flagbit5tcp_server_flagbit5{ }最后一个函数是tcp_server_remove_timewait()这个函数用来强制删除处于TIME-WATI状态ALIENTEKENC28J60LWIP}{}}tcppcbnew=tcp_new();// {tcppcbnew=tcp_new();// {if(err==ERR_OK)//绑定完成{ );//}else}elseres=1;{{}{ALIENTEKENC28J60ALIENTEKENC28J60LWIP}{ }}e

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论