版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
广州市星翼电子科技淘宝淘宝技术支持(开源):资 E-咨询 传真号码团队:正点原子团队正点原子,做最全面、最优秀的嵌入式开发平台软硬件供应商。友友情提示如果您想及时免费获取“正点原子 资料,敬请关注正点原公众平台及时给您发消息和重要资料关注方法 “扫一扫”,扫描右侧,添加关注→添加朋友 →输入“正点原子”→关→添加朋友→输入“alientek_stm32”→关文档更新说明第一章LWIP无操作系统移植第二章LWIP带操作系统移植RAWTCP客户端实验RAWTCP服务器实验RAWWebServer实验第七章NETCONN编程接口简介第八章NUDP...2第一章LWIP无操作系统移 TCP/IP协议以及LWIP简 硬件设 无操作系统LWIP移 移植准备工 添加及修改ST以太网 添加网卡驱动程 LWIP数据包和网络接口管 添加LWIP源文 添加中间文 LWIP源码修 LWIP的裁剪与配 软件设 验 第二章LWIP带操作系统移 移植简 带操作系统LWIP移 UCOSII+LWIP移 UCOSIII+LWIP移 软件设 验 第三章RAW编程接口UDP实 RAW编程接口UDP简 软件设 验 第四章RAW编程接口TCP客户端实 软件设 验 第五章RAW编程接口TCP服务器实 软件设 验 第六章RAW编程接口WEBSERVER实 WebServer文件以及相关技术简 软件设 验 第七章NETCONN编程接口简 NETBUF数据缓冲 NETCONN连接结 NETCONNAPI函 第八章NETCONN编程接口UDP实 软件设 UCOSII版 UCOSIII版 验 第九章NETCONN编程接口TCP客户端实 软件设 UCOSII版 UCOSIII版 验 第十章NETCONN编程接口TCP服务器实 软件设 UCOSII版 UCOSIII版 验 第一章LWIP无操作系统移植本章,向大家介绍ALIENETK探索者STM32F4开发板以太网接口及其使用。层和TCP/IP协议栈的支持。ALIENTEK板载一颗PHY层。本章主要介绍基本的以太网知识以及LWIP在ALIENTEKSTM32F407开发板上的移植。本章将分为如下几个部分:4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。通俗而言:TCP负责发现传IP是给因特网的每一台联网设备规定一个地址。分层模型方面来讲,TCP/IP由四个层次组成:网络接口层、网络层、传输层、应用层。OSI是网络层(网络层、传输层(传输层、会话层、表示层和应用层(应用层。TCP/IP模型与OSI模型对比如表1.1.1所示。12345671.1.1TCP/IP模型与OSI在我们的LWIP实验中PHY层LAN8720相当于物理层,STM32F407自带的MAC层LAN8720是低功耗的10/100M以太网PHY层,I/O引脚电压符合IEEE802.3-2005标准。LAN8720支持通过RMIIMAC10-BASE-T/100BASE-TX全双接方式(速度和双工模式)HPAuto-MDIX自动翻转功能,无需更换网线即可将连接更改为直连或交叉连接。LAN8720的主要特点如下:支持RMIILAN8720上电或复位后就工作在主中断模式,当模式控制/状态寄存器(十进制地址为17)的ALTINT0LAN8720工作在主模式,当ALTINT1时工作在复用中断模式。我们的STM32F407开发板中并未用到中断功能,关于中断的具体用法可以参考LAN8720的29,30页。MAC层通过SMI总线对PHY进行读写操作,SMI可以控制32个PHY,通过不同的PHY地址来对不同的PHY操作。LAN8720通过设置RXER/PHYAD0引脚来设置其PHY址,也就是0X00。PHY如表1.1.3所示。我们STM32F407开发板使用的是REF_CLKOut模式。REF_CLKOutnINTSEL=REF_CLKIn1.1.3nINTSEL当工作在REF_CLKIn模式时,50MHzLAN8720的引脚上,如图为了降低成本,LAN872025MHzREF_CLK时钟。到要使用此功能时应工作在REF_CLKOut模式时。当工作在REF_CLOOut模式时REF_CLK的时钟源如图1.1.3所示。PHY是由IEEE802.3定义的,一般通过SMI对PHY进行管理和控制,也就是读写PHY内部寄存器。PHY50~3132PHY芯不讨论这种情况。IEEE802.3定义了0~15这16个寄存器的功能,16~31寄存器由制造商此处使用十进制表示:BCR(0),BSR(1),PHY特殊功能寄存器(31)这三个寄存器。位掉电(powerdown)981.1.4BCR我们配置LAN8720其实就是配置BCR寄存器,我们通过调用ST的以太网库的ETH_ReadPHYRegister和ETH_WritePHYRegister函数来完成对PHY寄存器的读写操作。在ST以太网库的stm32f4x7_eth.h中已经定义了BCR和BSR寄存器,代码如下:/**/**@defgroup*Register*/Register0/*!<TransceiverBasic1/*!<TransceiverBasicLAN8720初始化的中并没有看到我们直接操作PHY的寄存器,原因就在这里当我们调用ETH_Init()函数以后就会根据我们输入的参数配置LAN8720的相应寄存器。位类型0:不支持543211:检测到jabber01.1.5BSRBSR寄存器为LAN8720的状态寄存器,通过该寄存器的值我们可以得到当前的连接速度、双工状态和连接状态等功能。在ST以太网库中的ETH_Init函数通过PHY的BCRBSR位001:10BASE-T101:10BAST-T010:100BAST-TX速度。在ETH_Init函数中通过这3位的值来设置BCR寄存器的bit8和bit13。由于特殊功自己实际使用的PHY去修改,在stm32f4x7_eth_conf.h文件中定义,代码如下:/*/*PHY/*PHY复位延时#define /*PHY配置延时#define //PHY的状态寄存器,用户需要根据自己的来改变PHY_SR ((uint16_t)16)DP83848的PHY#define // //#define ((uint16_t)0x0004)DP83848PHY#define ((uint16_t)0x0004LAN8720PHY#define 站管理接口(SMI)允许应用程序通过2条线:时钟(MDC)和数据线(MDIO)任意PHY寄存器。该接口支持多达32个PHY。应用程序可以从32个PHY中选择一个PHY,然后从任意PHY32个寄存器中选择一个寄存器,发送控制数据或接收状态信息。任意给定时间内只能对一个PHY中的一个寄存器进行寻址。在MAC对PHY进行读写操作的时候,应参考手册(RM009)的824页。图1.1.7所示。图1.1.7STM32F407与PHY层连10Mbit/s2.5MHz100Mbit/s25MHz。10Mbit/s2.5MHz100Mbit/s25MHz。精简介质独立接口(RMII)10/100Mbit/sPHY间IEEE802.3u标准,MII16个数据和控制信号的引脚。RMII规范将引脚数减少为7个。STM32F407通过RMII接口与PHY层的连接如图1.1.8所示。因为RMII相比MII,其100Mbit/s的速度其时钟频率应为50MHz,而MII接口分别为2.5MHZ和25MHZ。我们的STM32F407开发板采用RMII接口连接LAN8720。图1.1.8STM32F407通过RMII与PHYLWIP是瑞典计算机(SICS)的AdamDunkels等开发的一个小型开源的TCP/IP协议栈。LWIP是轻量级IP协议,有无操作系统的支持都可以运行,LWIP实现的重点是在保持TCP协议主要功能的基础上减少对RAM的占用,它只需十几KBRAM40KROM就可以运行,这使LWIP协议栈适合在的嵌入式系统中使用。目前LWIP的版本是1.4.1。本采用的就是1.4.1版本的LWIP。关于LWIP的详细信息大家可以去这个去查阅,LWIP的主要特性如下打开从官网上下来的LWIP1.4.1其中包括docsrc和test三个文件夹和5个其他文件。doc文件夹下包含了几个与协议栈使用相关的文本文档,doc文件夹里面有两个比较重要的文档:rawapi.txt和sys_arch.txtrawapi.txt告诉读者怎么使用raw/callbackAPI进行编程,sys_arch.txt包含了移植说明,在移植的时候会用到。srcLWIP的源码。testLWIP提供的一些测试程序,这里用不到。打开src源码文件夹,如图1.1.10所示。硬件设计LAN8720通过自协商确定自身的工作速度以及双工模式,通过串口打印MAC地址、IP地址、子网掩码和默认网关等信息。同时DS0提示程序正在运行。下半部分为RJ45(内置网络变压器)原理图。无操作系统LWIP移植在本节和后续所有有关LWIP的中需要使用到动态内存管理实验的知识,因此请读者务必了解动态内存管理实验,此只针对LWIP,不会对其他知识做讲解。开发指南实验14USMART实验。网 这两个文件,如图所示为我们 的两个文件。其中lwip-1.4.1.zip为LWIP的 源码,contrib-1.4.1.zip包含的一些中。目前的版本是1.4.1,本所有的实验均使用1.4.1版本的LWIP。因为STM32F407带有以太网MAC模块,因此ST也提供了以太网库,我们可以在ST官 界面如图所示。解压后为名为:STM32F4x7_ETH_LwIP_V1.1.0,我们已经将解压后的以太网库放到光盘中,在软件资料文件夹下的LWIP学习资料文件夹中。STSTM32F4x7_ETH_LwIP_V1.1.0Libraries文件夹下的STM32F4x7_ETH_Driver文件到我们基础工程的FWLIB文件夹下,如图添加STSTM32F4x7_ETH_Driver3个文件,stm32f4x7_eth.h、stm32f4x7_eth.c和stm32f4x7_eth_conf_temte.h。stm32f4x7_eth.h为头文件,这个很好理解,stm32f4x7_eth.c里面定义了一些关于操作PHY的信息,为了方便移植stm32f4x7_eth_conf_temte.h文件路径,添加完成后的基础工程如图所示。ST提供给我们的stm32f4x7_eth_conf.h文件只是一个参考文件,在这个文件是以DP83848定的修改,针对LAN8720修改后的stm32f4x7_eth_conf.h文件代码如下。#ifndef#ifndefSTM32F4x7_ETH_CONF_H#defineSTM32F4x7_ETH_CONF_H#include"stm32f4xx.h"#define #ifdefUSE_Delay#include"main.h" #define #defineETH_TX_BUF_SIZE #defineETH_RXBUFNB #define #ifdefUSE_Delay#define #define #defineETH_REG_WRITE_DELAY((uint32_t)0x #define #define #definePHY_SR //LAN8720的PHY状态寄存器地址#definePHY_SPEED_STATUS //LAN8720PHY速度状态值掩码#definePHY_DUPLEX_STATUS((uint16_t)0x00010) //LAN8720PHY连接状态值掩码在上面代码中我们定义了操作PHY时需要用到的延时配置,也定义了LAN8720的状态寄存器地址和LAN8720中的速度值、连接状态的掩码。通过LAN8720的状态寄存器并2)stm32f4x7_eth.c在stm32f4x7_eth.c文件中针对不同的平台定义了四个数组:Rx_Buff[]、Tx_Buff[]、DMARxDscrTab[]DMATxDscrTab[]RAM。我们在这里将这四个这样我们就可以使用外部SRAM,减少对STM32F407内部RAM的使用。STM32F4x7DMA描述符,STM32F407以太网模块中的接收/FIFODMADMA描述符ETH_DMARDLAR寄存器和ETH_DMATDLAR寄存器中。图展示了DMA描述符的两种结构:环形结构和结构,在ST提供的以太网驱 结2、一个DMA体定义了DMA描述符,ETH_DMADESCTypeDef结构体代码如下。typedeftypedef IO #ifdef}DMA描述符分为增强描述符和常规描述符,本中我们使用的是常规描述符,如果使符和增强描述符的区别大家可以参考STM32F4xx中文参考手册的865869页。STM32F407的描述符分为发送描述符和接收描述符,发送描述符和接收描述符都用ETH_DMADESCTypeDef这个结构体来定义,我们这里只以发送描述符(常规TxDMA描图来表示。常规TxDMA存器”并不是STM32F407STM32F4074增强TxDMA1增强TxDMA增强TxDMA增强TxDMATxDMA描述符,常规RxDMA描述符与之类似。同我们操个位的含意大家可以参考STM32F4xx中文参考手册的865页(RxDMA描述符在873页)。我们前面ST的以太网库stm32f4x7_eth.c中使用结构的DMA描述符,那么在以太网描述符结构体ETH_DMADESCTypeDef中Buffer1Addr就是缓冲区的地址,Buffer2NextDescAddr就是下一个描述符的地址,如图所示那样。 这两个数组是不是看起来有点眼熟,没错!这两个数组就是我们前面掉并且采用内存分配函数给其分配内存的4个数组中的其中两个。我们前面是由于它们占用RAM过大,我们ST以太网驱动库stm32f4x7_eth.c中我们采用的是DMA结构,但是 文件中有两个函数 ETH_DMARxDescChainInit()和uint8_tuint8_t这两个数组也是被我们掉采用内存分配函数给其分配内存的4个数组中的另外两个,RAM过大了,因为他们被用做数据RAM必然很大,大家也可以实际测量一下这两个数组的大小。在上面这四个数组中使用了四个宏定义:ETH_RXBUFNB、ETH_TXBUFNB、ETH_RX_BUF_SIZE和
IO IO IO IO STM32F407DMA描述符讲解就晚了,关于DMA描述符的详细内容大家可以参考STM32F4xx中文参考手册。lan8720.clan8720.hLAN8720和STM32F407MAC的驱动程序,大家在移植的时候将ETHERNET文件拷贝到自己的HARDWARE文件中,并且将lan8720.c添加到工程中。在这里我们还要使用到lan8720.c和stm32f4xx_syscfg.c文件后的工程如图所示。MACMAC和DMA 释放Rx_Buff[]、Tx_Buff[]、DMARxDscrTab[]lan8720.cu8u8{u8GPIO_InitTypeDefGPIO_InitStructure;RCC_AHB1Periph_GPIOG,ENABLE);//使能GPIO时钟RMII接口//MAC和PHY之间使用RMIIGPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_PinAFConfig(GPIOAGPIO_PinSource1,GPIO_AF_ETH引脚复用到网络接口上GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_ETH);GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_ETH);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5;GPIO_Init(GPIOC,&GPIO_InitStructure);GPIO_PinAFConfig(GPIOC,GPIO_PinSource1,GPIO_AF_ETH引脚复用到网络接口上GPIO_PinAFConfig(GPIOC,GPIO_PinSource4,GPIO_AF_ETH);GPIO_PinAFConfig(GPIOC,GPIO_PinSource5,GPIO_AF_ETH);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11|GPIO_Pin_13|GPIO_Pin_14;GPIO_Init(GPIOG,&GPIO_InitStructure);GPIO_PinAFConfig(GPIOG,GPIO_PinSource11,GPIO_AF_ETH);GPIO_PinAFConfig(GPIOG,GPIO_PinSource13,GPIO_AF_ETH);GPIO_PinAFConfig(GPIOG,GPIO_PinSource14,GPIO_AF_ETH);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;GPIO_Init(GPIOD,&GPIO_InitStructure); //配置MAC及DMAreturn }的配置。配置完成后复位LAN8720DMA中断方式来接收数据,也可采用轮ETHERNET_NVICConfiguration()ETH_MACDMA_Config()DMA中断优先级、MAC以及DMA的配置,在这里DMA中断优先级设置为组2中最高优先级(这点!ETHERNET_NVICConfigurationDMA中断优先级,大家可u8u8{u8return} u8ETH_MACDMA_Config(void){u8//使能以太网MAC以及MAC接收和发送时钟RCC_AHB1Periph_ETH_MAC_Tx|RCC_AHB1Periph_ETH_MAC_Rx,ENABLE); = #ifdefCHECKSUM_BY_HARDWARETH_InitStructure.ETH_ChecksumOffload=ETH_ChecksumOffload_Enable; //开启丢弃TCP/IP错误帧 ETH_InitStructure.ETH_TxDMABurstLength=ETH_TxDMABurstLength_32Beat;ETH_InitStructure.ETH_DMAArbitrationETH_DMAArbitration_RoundRobin_RxTx_2_1; //配置ETH{}return}MACDMAMACDMAETH_InitETH_Init中有很ETH_StructInit函数来将网络配置为默认值,然后在根据我果调用ETH_Init()函数配置成功的话就开启了DMA总中断和DMA接收中断。void{void{{});//}00的时候就调用收中断标志位和DMA总中断标志位。ETH_Rx_Packet()函数是从以太网中接收数据,并将接收到的数据打包成一个FrameTypeDef类型的结构体返回,FrameTypeDefstm32f4x7_eth.h中有定义。前面讲过STM32F407的网络模块是将接收到的数据放到DMA描述符中的。所以在FrameTypeDef结构体中有一个指向DMA描述符的指针变量,ETH_Rx_Packet()函数代码如下。{u32framelength=0;{{}}}((DMARxDescToGet->StatusÐ_DMARxDesc_LS)!=(u32)RESET)&&((DMARxDescToGet->StatusÐ_DMARxDesc_FS)!=(u32)RESET)){//得到接收包帧长度(不包含4字节CRC)=}elseframelength=ETH_ERROR;//错误//为下一次buffer return}u8ETH_Tx_Packet(u16u8ETH_Tx_Packet(u16{ETH_ERROR;//错误,OWN位被设置了//设置最后一个和第一个位段置位(1个描述符传输一帧)//设置Tx描述符的OWN位,buffer重归ETHDMA{}//为下一次buffer发送设置下一个DMATx描述符returnETH_SUCCESS;} u8{ u8{Rx_Buff=mymalloc(SRAMIN,ETH_RX_BUF_SIZE*ETH_RXBUFNB);//申请内存Tx_Buff=mymalloc(SRAMIN,ETH_TX_BUF_SIZE*ETH_TXBUFNB);//申请内存{ return1;}return }和收中断服务函数ETH_IRQHandler()中调用了lwip_pkt_handle()函数,而这个函数没有在lan8720.c文件中定义,这个错误提示不用管,lwip_pkt_handle()函数后续会讲解。LWIPLWIP协议深LWIP协议栈使用pbuf结构体来描述协议栈使用中的数据包,pbuf结构体在pbuf.h中有定structstructpbuf void u16_t u16_t u8_t u8_t u16_tu16_tpayload:指向该pbuf的数据区的首地址,STM32F407内部网络模块接收到数据,并将数据提交给LWIP的时候,就是将数据在payload指定的区中。同样在发送数据的时候将payload所指向的区数据转给STM32F407的网络模块去发送。PBUF_RAMpbuf是通过内存堆分配得到的,PBUF_RAMpbufpayload并未指向数据区的起始地址,而是隔了一段区域,这段区域就是offset,在里面通常存放TCP报文首部,IP首部、以太网帧首部等等。PBUF_POOLpbuf,PBUF_POOL是通过内存池分配的到的,PBUF_POOL类型的pbufpbufpbuf的payload未指向数据区的起始位置,原因同PBUF_RAM一样,用来存放一些首部的,pbufpbuf结构体中payload字段就指向了数据区的起始位置。中有定义,定义如下,由于netif结构体比较大,这里只保留了一些比较重要的字段,具体的netif结构体定义请查阅netif.h文件。structnetifstructnetif*next;ip_addr_tip_addr;ip_addr_tnetmask;ip_addr_tgw;netif_input_fninput;
void*state; u16_t u8_t u8_tflags; charname[2]; u8_tnum; }有多个网络接口的话LWIPnetif结构体组成链表来管理这些网络接口。ipaddr,netmaskgwIP地址、子网掩码和默认网关。input:此字段为一个函数,这个函数将网卡接收到的数据交给IP层。outputIP层向接口发送一个数据包时调用此函数。这个函数通常linkoutputARP模块调用,完成网络数据的发送。上面说的etharp_outputIP数据包封装成以太网数据帧以后就会调用linkoutput函数将数据发送能,ARP使能等。本节中LWIP源码添加到我们的工程中,我们在工程中新建一个LWIP文件夹,贝到LWIP文件夹下,如图所示。.cLWIP源码中的头文件路径,头文件路径按照图所示添加,这时如果我们编译的话会有很多错误提示,这个不用管。1LWIPLWIParch文如果使用操作系统的话还有临界代码区保护等等,cc.h文件内容如下所示。typedeftypedeftypedeftypedeftypedef#define#defineSYS_ARCH_PROTECT(lev) #defineSYS_ARCH_UNPROTECT(lev) #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_END#defineU16_F#defineU16_F"4d"#defineS16_F"4d"#defineX16_F"4x"#defineU32_F"8ld"#defineS32_F"8ld"#defineX32_F"8lx"#ifndefLWIP_ #defineLWIP_ do\ printf("Assertion\"%s\"failedatline%din%s\r\n",x,LINE,FILE);#ifndef #defineLWIP_ TFORM_DIAG(x)do{printfx;}while(0)#ifndefCPU_H #define#ifndefCPU_H #define perf.h是和系统测量与统计相关的文件,我们不使用任何的测量和统计,因此这个文件中#ifndef#ifndefPERF_H#define#definePERF_START//空定义#definePERF_STOP(x)//空定义sys_arch.h和sys_arch.c是在使用操作系统的时候才使用到的文件,在这里我们只是在为一个全局变量,用来为LWIP提供时钟,这个变量我们会在下面的移植中介绍。u32_tu32_t{return}加相应的头文件路径,如图所示。打开我们的网络实验1LWIP无操作系统移植实验的LWIP文件夹可以发现有一个lwip_app文件夹,将这个文件夹到自己工程中,lwip_app文件夹用来放我们以后所有实验的代码。在lwip_app下有一个m文件夹,这个文件中有 m.h和lwipopts.h这三个文件,m.c和m.h是将LWIP源码和前面的以太网驱动库结合起来的的文件,以后我们想要使用LWIP的什么功能的话就在这个文件中配置就行了。LWIP-APP分组,并将m.c文件添加到这个分组中并且添加相应的头文件路径,如图所示typedef{u8 u8 u8typedef{u8 u8 u8 u8 u8 vu8vu8 //0,未获取DHCP地址//1,进入DHCP//2,成功获取DHCP}MACIPIPDHCP状态这几个成员变量。在 m.c中定义了一个lwip_dev结构体类型变量lwipdev,m_default_ip_set(lwip_devvoidm_default_ip_set(lwip_devvoidlwip_pkt_handle(void)void {u32 //为ram_heap申请内存{ return}return}
m_default_ip_set()函数用来设置默认地址,我们前面提过lwip_dev MAC地址不会重复就行。IP地址、子网掩码、默认网关地址可以自行设置,这里我们设置IP地址为:0,子网掩码:,默认网关:。接下来要介绍的m_initLWIP内 {structnetif*Netif_Init_Flag;structip_addr structip_addr structip_addrgw; if(ETH_Mem_Malloc())return1; //LWIP#ifipaddr.addr=netmask.addr=printf("网卡en的MAC地址 printf("静 地 #ifLWIP_DHCP //如果使用DHCP的话 //DHCP标记为0dhcp_start(&lwip_netif);//开启DHCP服务{ }}1ETH_Mem_Malloc()和m_mem_malloc()这两个函数完成前面提到的那四个数组和内存堆ram_heap和内存池memp_memory的内存分配。初始化,LAN8720_Init()函数在LAN8720.c文件中,由ALIENTEK提供。3lwip_initLWIP的内核初始化,lwip_init()通过调用各个模块的初始化函函数、IPTCP等的初始化函数,lwip_init()init.c文件中,属于LWIP源码。 5、判断是否使用DHCP,如果使用DHCPDHCPIP地址、子网掩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层数据包输入函数。当程序中调用m_init()函数后,网卡就可以工作了。STM32F407内部网络模块接收数据。前面我们介绍过DMA中断服务函数ETH_IRQHandler(),在中断服务函数中我们调用lwip_pkt_handle()函数来接收数据,lwip_pkt_handle()函数代码如下。{}可以看出lwip_pkt_handle()函数其实只是对ethernetif_input()函数的简单封装,通过调用LWIP内核中有许多的周期性的定时器,相对应的定时处理函数也需要被周期性调用的,STM32F407定时器3提供这个系统时钟,定时器3定时周期为10ms,定时器3的中断服务函数如下,lwip_localtime是一个全局变量,在m.ctimer.c文件放关于定时器3的程序,然后将timer.c添加到HARDWARE组下面,并且添加相应的头文件路径。void{{} }LWIPTCP定时器、ARP定时器、IP如果使用DHCP的话还有DHCP定时器等等,我们可以将这些定时器封装在一个函数里面,然后周期性这个函数就行了,LWIP250msTCP5S处理处理。我们就根据这个要求来编写lwip_periodic_handle()函数,函数代码如下,最后我们只要lwip_periodic_handle()LWIP内核的定时处理函数的周期性调void{#if{TCPTimer= }{ARPTimer= }//500ms调用一次{DHCPfineTimer=lwip_localtime;{ }}{DHCPcoarseTimer=lwip_localtime;}}voidvoid{u32{case lwipdev.dhcpstatus=1;printf("正在查找DHCP服务器,请稍 case { { //DHCP获取到的子网掩码地址 {//使用静态IP地址 }}}default:}}当DHCP完成以后让dhcpstatus=2,表示DHCP成功。但是当DHCP重试次数大于并且使用静态IP地址。打开我们的网络实验1LWIP无操作系统移植实验,LWIP->lwip1.4.1->src->include->netif中我们会发现有个ethernetif.h文件,这个文件在LWIP源码中是不存在的,这个文件由ALIENTEK提供,将ethernetif.h文件到自己工程的相应位置,如图所示。LWIPLWIP底层LWIPLWIP的源码做出一点小的改动,下面我们就讲解如何修改LWIP源码。我们按路径:lwip-1.4.1->src->core可以发现在core文件下有一个sys.c文件,按路径lwip-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”代码也要改掉,更改为#include统移植实验中的ethernetif.c文件替代LWIP源码中的这个文件,在这个文件中有5个函数。 staticstaticerr_tlow_level_output(structnetif*netif,structpbuf*p)staticstructpbuf*low_level_input(structnetif*netif)符链表的初始化,硬件帧校验的开启并且打开以太网。low_level_init函数如下:由 {#ifdefnetif->mtu=1500;最大允许传输单元,允许该网卡广播和ARP功能//向STM32F4的MAC地址寄存器中写入MAC地址ETH_DMATxDescChainInit(DMATxDscrTab,Tx_BuffETH_TXBUFNB);#ifdef {}returnreturn}low_level_output函数用于发送数据,将LWIP的pbuf数据拷贝到这个缓冲区中,然后调用ETH_Tx_Packet函数将数据发送出去,low_level_output函数代码如下:staticstaticerr_tlow_level_output(structnetif*netif,structpbuf{u8intl=0;u8*buffer=(u8*)ETH_GetCurrentTxBuffer();{memcpy((u8_t*)&buffer[l],q->payload,q->len);} returnERR_OK;}staticstructpbuf*low_level_input(structnetif{structstaticstructpbuf*low_level_input(structnetif{structpbuf*p,*q;u16_tlen;intl=0;u8*buffer;p=buffer=(u8*)frame.buffer;//得到包数据地址{{}}//Rx描述符OWN位,buffer重归ETHDMA{}return}数主要是对low_level_input()函数做封装,然后将接收到的数据送入指定的网卡结构中。 {err_terr; {LWIP_DEBUGF(NETIF_DEBUG,("ethernetif_input:IPinputerror\n")); p=}return}{#if returnERR_OK;}memp_memory,这两个是LWIP的内存来源。这两个分别在mem.cmemp.c中,我们将这两个数组改用ALIENTEK的内存分配函数对其进行内存分配。在mem.c文件中ram_heap数组注销掉,定义为指向u8_t的指针,如图所示。我们还要在memp.c文件中添加memp_get_memorysize()函数用来获取memp_memory数组的大小,memp_get_memorysize()函数代码如下。memp_memory我们会在m.c文件中使u32_tu32_t{u32_tlength=0;#defineLWIP_MEMPOOL(name,num,size,desc)+((num)*\#include#includereturn}图 我们在其他地方有定义过的话那么在opt.h里面的定义不起作用了。所以我们可以新建一个.h的文件来裁剪和配置LWIP,我们前面提过在LWIP->lwip_app-> m下有一个lwipopts.h的文件,这个文件就是用来裁剪与配置lwipopts.h的,lwipopts.h配置代码如下。 #define#define#defineNO_SYS#define0 //不使用UCOS4#define //MEMP_NUM_PBUF:memp结构的pbuf数量,如果应用从ROM或者静态区发送大量数据#define //MEMP_NUM_UDP_PCB:UDP协议控制块(PCB)数量.每个活动的UDP"连接"需要一个PCB.#defineMEMP_NUM_UDP_PCB #define #define #define Pbuf选 //PBUF_POOL_SIZE:pbuf内存池个数.#definePBUF_POOL_SIZE //PBUF_POOL_BUFSIZE:每个pbuf内存池大小.#definePBUF_POOL_BUFSIZE TCP选 #define /*当TCP的数据段超出队列时的控制位,当设备的内存过小的时候此项应为0*/#defineTCP_QUEUE_OOSEQ (1500 //TCP_MSSMTUIPTCP#define ://(2*#define (2*#define /*----------ICMP选 #define /*----------DHCP选 //当使用DHCP时此位应该为1,LwIP0.5.1版本中没有DHCP服务.#defineLWIP_DHCP UDP选 #define #define /*----------Statistics //#ifdef#define #define #define #define #define //CHECKSUM_CHECK_TCP==0://CHECKSUM_CHECK_TCP==0:硬件检查输入的TCP#define #define CHECKSUM_GEN_UDP==1:软件生成UDOP#define #define #define #define //CHECKSUM_CHECK_TCP==1:软件检查输入的TCP#define #defineLWIP_NETCONN //LWIP_SOCKET==1:使能SocketAPI(要求使用sockets.c)#defineLWIP_SOCKET #define11//LWIP_SO_RCVTIMEO#definelwipopts.h中有很多的宏定义,每个宏定义后面已经给出了具体的解释,大需求来编写lwipopts.h里面的内容软件设计经过上面几节的讲解,LWIPmian.c文件来测试int{int{//LED LCD_ShowString(30,30,200,16,16,"ExplorerSTM32F4");LCD_ShowString(30,50,200,16,16,"EthernetlwIPTest"); m_init())//lwip初始化{);//} #if {} {}}main函数中首先完成对外设的初始化,然后调用m_initLWIP,如获取地址失败的话将使用我们的默认的地址,最后我们在一个while循环中循环调用lwip_periodic_handle()函数。在main函数中我们要注意到不管是在等待DHCP完成还是DHCP成功后我们都要周期性调用lwip_periodic_handle()函数,因为在这个函数中周期性调用协议栈内核的一些定时函数以满足LWIP的内核要求。通过USMART组件就可以或者改写LAN8720内部寄存器的配置,这是非常好的一种调试stm32f4x7_eth.cPHYETH_ReadPHYRegister()和ETH_WritePHYRegister(),这两个函数添加到usmart_config.c1.4.1添加读写PHY函数(usmart_config.g文件中是没有使用,这两个Warning我们不用管他。1.5验以连接电脑RJ45RJ45接口上那么开发板就不能使用DHCPIP地址:0,子网掩脑的网络设置,步骤如下,此处以WIN7系统为例。1.5.3图1.5.4本地连接框图1.5.5本地连接属性IPIP地址在一个网络内!以后我们的实验都是连接到路由器上的,如果没有路由器的话都可以使用上面这种方法这种方法完成实验,只是不能使用DHCP服务。框关闭DHCP使用静态IP地址,完成后LCD显示如图1.5.7所示。1.5.7LCD1.5.8DHCP分配到的IP地址为:18,子网掩码为:。在电脑上开发板的IP地址,结果如图1.5.9所图1.5.9测调试助手(这里以我们的串口调试助手为例)打印出USMART支持的函数,如图1.5.10所示,从图中可以看出函数有6个函数,其中就有我们前面添加过的ETH_ReadPHYRegister()ETH_WritePHYRegister()这两个函数。ETH_ReadPHYRegister()函数测试,ETH_ReadPHYRegister()PHYaddress和地址为0X00,这里我们LAN8720的寄存器31也就是特殊功能寄存器,所以串口调试助0X1058。图1.5.11LAN8720寄存器31的bit2-4代表着网络的连接速度和双工状态,可以看出“110100M全双工连接,bit121LAN8720的其他寄存器,也可以使ETH_WritePHYRegister()函数改写某些寄存器的值。LWIPLWIPLWIPAPIrawAPI,raw编程难度很大,需LWIPLWIPLWIP使用非常重要,本章就讲解带有UCOS的LWIP移植。移植简介本章的移植是面无操作系统的基础上修改的,本章不讲解UCOS的移植,关于UCOS的移植请大家参考我们的《ALIENTEKSTM32F4UCOS使用》。我们只需要对lwipopts.h和m.c文件做简单修改,然后完成sys_arch.h和sys_arch.c这两个文件的编写。sys_arch.c要实现的宏定义和函数如表2.1.1所示。2.1.1hd2.1.1中可以看出,sys_arch.c中要实现的主要是与消息邮箱、信号量和创建进程有关sys_thread_new函数创建一个进程,因此这个函数必须实现,实现过程也是对UCOS中的进程创建函数做简单的封装。带操作系统LWIP移植#ifOS_CRITICAL_METHOD==1#define#define #defineSYS_ARCH_UNPROTECT(lev) #defineu32_t#define//UCOSII中退出A临界区,开中断lev=includes.h头文件(cc.h文件中我们lwipopts.h是用来裁剪和配置LWIP的,那么我们要使用操作系统的话就需要对LWIPlwipopts.h中的配置有点不同,本章中的#ifndef#ifndefLWIPOPTS_H #define #ifndef#undef#undef//当TCP的数据段超出队列时的控制位,当设备的内存过小的时候此项应为0#defineTCP_QUEUE_OOSEQ #undef #define #undef#define#undef #define52#defineSYS_LIGHTWEIGHT_PROT#defineNO_SYS #defineMEM_ALIGNMENT //使用4字节对齐模式#defineMEM_SIZE 16000//内存堆heap大小#define //MEMP_NUM_UDP_PCB:UDP协议控制块(PCB)数量.每个活动的UDP"连接"需要一个PCB#defineMEMP_NUM_UDP_PCB #define #define #define #define #define#define1#define //TCP分段,TCP_MSSMTUIPTCP (1500-#define ://最小为(2*#define (2*#define#define1#define1#define1#define#ifdefCHECKSUM_BY_HARDWARE#define #define #define #define #define //CHECKSUM_CHECK_TCP==0:硬件检查输入的TCP#defineCHECKSUM_CHECK_TCP #define CHECKSUM_GEN_UDP==1:软件生成UDOP#define #define #define #define#define1#define #define#define1LWIP_SOCKET==1:使能SicketAPI( #define #define#defineDEFAULT_UDP_RECVMBOX_SIZE#defineDEFAULT_THREAD_STACKSIZE#defineLWIP_DEBUG#define01、因为我们要使用UCOSII系统,所以NO_SYS02LWIP3、如果使用NETCONNLWIP_NETCONN1上面只是我们列举的一小部分配置,使用UCOS的话还需要很多的配置,具体配置大家参考我们的lwipopts.h文件。这里就不一一解释了。sys_arch.h4中数据类型:sys_sem_t、sys_mutex_t、sys_mbox_tsys_thread_t。分别为信号量、互斥信号这里不能直接使用UCOS的消息邮箱,使用的是消息队列,移植的时候我们需要自己定义在LWIP中使用的邮箱类型结构体,sys_arch.h文件代码如下。#ifndef#ifndefARCH_SYS_ARCH_H#defineARCH_SYS_ARCH_H#include<includes.h>#include"arch/cc.h"#include#defineSYS_ARCH_EXT#define#defineSYS_ARCH_EXTextern#define#definetypedefstruct }TQ_DESCR, typedefPQ_DESCR typedefINT8Usys_thread_t; 移植手册sys_arch.txt,相关代码如下。 err_tsys_mbox_new(sys_mbox_t*mbox,int{(*mbox)=mymalloc(SRAMIN,sizeof(TQ_DESCR));//为消息邮箱申请内存 //清除mbox的内存{if((*mbox)->pQ!=NULL)returnERR_OK;{ return }}elsereturn }void (sys_mbox_t*{u8_t }{//msgmsg等于pvNullPointer} {//msgmsg等于pvNullPointerif((OSQPost((*mbox)->pQ,msg))!=OS_ERR_NONE)returnERR_MEM;returnERR_OK;} {u8_tu32_tucos_timeout,timeout_new;void*temp;{//转换为节拍数,UCOS延时使用的是节拍数,而LWIPms}elseucos_timeout=//请求消息队列,等待时限为ucos_timeout{if(temp==(void*)&pvNullPointer)*msg=NULL;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}{}} {u8_tucErr;intOS_Q_DATAq_data;ucErr=OSQQuery(m_box->pQ,&q_data);returnret;}{}这里我们对sys_arch_mbox_fetch()函数做一下简单的讲解,根据LWIP协议栈要求当sys_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);LWIP_ASSERT("OSSemCreate",*sem!=NULL);returnERR_OK;} ,{u8_tu32_tucos_timeout,timeout_new; {//转换为节拍数,UCOS延时使用的是节拍数,而LWIPmsucos_timeout=(timeout*OS_TICKS_PER_SEC)/1000;if(ucos_timeout<1)ucos_timeout=}elseucos_timeout=0;timeout=OSTimeGet();{timeout_new=if(timeout_new>=timeout)timeout_new=timeout_new-timeout;elsetimeout_new=0xffffffff-timeout+timeout_new;}return}{} {u8_tucErr;*sem=} {OS_SEM_DATA}{}sys_arch_sem_wait函数为等待信号量函数,和sys_arch_mbox_fetch()函数一样,sys_arch_sem_wait()函数也要返回待信号量所用的时间,这里我们的处理方法和在有操作系统的支持下,LWIPsys_thread_new函数来创建一个内核进程处理协调用创建tcpip内核线程,如果要创建其他任务一定要使用UCOS的OSTaskCreate()函数!sys_thread_new代码如下。sys_thread_tsys_thread_new(constchar*name,lwip_thread_fnthread,void*arg,intstacksize,int{{{ //创建TCPIP内核任务 }return}然后将其转换为LWIP使用的ms,并返回这个时间值。 m.c文m.c文件基本与前面介绍的不带操作系统相同,只有 DHCP的3个函数不同, {structnetif*Netif_Init_Flag;structip_addr structip_addr structip_addrgw; if(ETH_Mem_Malloc())return1; #ifipaddr.addr=netmask.addr=printf("printf("网卡en的MAC地址 { }}化函数tcpip_init()和netif_add()函数。在无操作系统中使lwip_init()函数来初LWIP内核,在这里使用了tcpip_init()来初始化LWIP内核,我们查看tcpip_init()函数可以看出其实在tcpip_init()lwip_init()netif_add()函数,与前面不同的是,这里将netif_add函数最后一个参数改为函数tcpip_input()。 m.c文件中我们把DHCP作为一个任务来处理,当DHCP任务执行完成后就删除掉这个任务。关于DHCP有三个函数: m_dhcp_creat、m_dhcp_delete和lwip_dhcp_task。前两个函数比较好理解,就是建立和删除DHCP任{u32ip=0,netmask=0,gw=0; printf("正在查找DHCP服务器,请稍 {printf("正在获取地址..\r\n"); { printf("网卡en的MAC地址 //DHCP获取到的子网掩码地址 printf("网卡en的MAC地址 }}}lwipdevdhcpstatusDHCP的处理状态。当表示DHCP失败,并且使用静态IP地址。注意最后在DHCP任务执行完成后调用m_dhcp_delete()函数删除DHCPUCOSIII+LWIP2.2.1小节中的UCOSII+LWIP的移植类似,最主要的区别就是文sys_arch.csys_arch.h2.2.1小节的基础上来完成UCOSIII+LWIP的移植。移植所使用的模板工程就使用“网络实验2LWIP带UCOSII里LWIP中的cpu.h文件名字修改为lwip_cpu.h,直接在模板工程中修改。文件路径为:LWIP->arch->cpu.h,如图所示:UCOSIII系统。方法很简单,将《STM32F407UCOSUCOSIII移植例程中的UCOSIII文件夹过来。完成以后如图所示:添加UCOSIII向工程中添加UCOSIII UCOSIIIUCOSIIAPIsys_arch.csys_arch.h这两个也要做相应的修改,修改后的sys_arch.h文件如下:#ifndef#ifndefARCH_SYS_ARCH_H#defineARCH_SYS_ARCH_H#include<includes.h>#include#include#defineSYS_ARCH_EXT#define#define//typedef typedefOS_MUTEX typedefOS_Q typedefCPU_INT08U#define/*lwIPincludes.*/#include"lwip/debug.h"#include"lwip/def.h"#include#define/*lwIPincludes.*/#include"lwip/debug.h"#include"lwip/def.h"#include"lwip/lwip_sys.h"#include"lwip/mem.h"#include"includes.h"#include"delay.h"#include"arch/sys_arch.h"#include"malloc.h"#include"os_cfg_app.h"//在本函数中把NULL变成一个常量指针0Xffffffffconstvoid*constpvNullPointermem_ptr_t*)0xffffffff; err_tsys_mbox_new(sys_mbox_t*mbox,int{OS_ERR (CPU_CHAR*)"LWIPQuiue", if(err==OS_ERR_NONE)returnERR_OK;return}void (sys_mbox_t*{OS_ERR#ifOS_CFG_Q_FLUSH_EN>0uOSQDel((OS_Q*(OS_OPT }{OS_ERRerr; { if(err==OS_ERR_NONE)break;延时}} {OS_ERR//当msg为空时msg等于pvNullPointer指向的值 if(err!=OS_ERR_NONE)returnERR_MEM;returnERR_OK;} {OS_ERRerr;u32_tucos_timeout,timeout_new;void*temp;{//转换为节拍数,UCOS延时使用的是节拍数,而LWIPmsif(ucos_timeout<1)ucos_timeout=1;//至少1个节拍}elseucos_timeout= (OS_MSG_SIZE*)&size, {if(temp==(void*)&pvNullPointer)*msgNULL;else} {if(timeout_new>=timeout)timeout_new=timeout_new-timeout;elsetimeout_new=0xfffffff
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 护理技术操作培训:导尿管护理
- 基于云计算的搬运机器人管理平台开发
- 旅游公司导游部经理培训要点
- 2026海南卫星海洋应用研究院有限公司招聘7人备考题库一套附答案详解
- 2026贵州贵阳观山湖中学招聘中小学教师备考题库带答案详解(黄金题型)
- 2026广西百色市右江区城东社区卫生服务中心招聘公益性岗位2人备考题库(真题汇编)附答案详解
- 2026西安银行总行科技部、数据管理部相关岗位招聘备考题库附完整答案详解【各地真题】
- 2026海南海口市美兰区校园招聘教师45人备考题库(一)附参考答案详解【达标题】
- 2026中国农业科学院油料作物研究所油料基因工程与转基因安全评价创新团队科研助理招聘1人备考题库(培优a卷)附答案详解
- 2026四川成都市龙泉驿区东山国际小学教师招聘12人备考题库及参考答案详解(黄金题型)
- CQI-17Solder钎焊系统评估(中文版)
- AQ 1071-2009 煤矿用非金属瓦斯输送管材安全技术要求(正式版)
- 电子行业专题报告:先进封装专题八CoWoS-L-下一代大尺寸高集成封装方案
- 著作权授权书模板
- 《景阳冈》-部编版语文五年级下册
- 学校文印室外包服务 投标方案(技术方案)
- 综合实践活动(4年级下册)第4课时 换季衣物巧收纳-课件
- GB/T 42903-2023金属材料蠕变裂纹及蠕变-疲劳裂纹扩展速率测定方法
- 幼儿园优质公开课:中班健康《健康精灵》课件
- 肾囊肿围手术期护理查房
- GB/T 43091-2023粉末抗压强度测试方法
评论
0/150
提交评论