LwIP簡介
LwIP是輕量化的TCP/IP協(xié)議,由瑞典計(jì)算機(jī)科學(xué)院(SICS)的Adam Dunkels 開發(fā)的一個(gè)小型開源的TCP/IP協(xié)議棧。LwIP具有高度可移植性、代碼開源,提供了三種編程接口(API):RAW API、NETCONN API 和 Socket API,用于與TCP/IP代碼進(jìn)行通信。
通過官網(wǎng)可獲取LwIP源碼包及contrib包。源代碼包主要包含LwIP內(nèi)核的源碼文件,contrib包中包含部分移植和應(yīng)用LwIP的demo。contrib包不屬于LwIP內(nèi)核的一部分,但很有參考價(jià)值。
以lwip-2.1.2版本的源碼包為例,如圖1所示,該源碼包分為三部分, src 文件為LWIP源代碼文件, doc 文件包含LwIP相關(guān)文檔, test 為LwIP測(cè)試文件,使用時(shí)主要關(guān)注于 src 文件下的內(nèi)容。
LwIP內(nèi)核是由一系列模塊組合而成,包括 TCP/IP 協(xié)議棧的各種協(xié)議、內(nèi)存管理、數(shù)據(jù)包管理、網(wǎng)卡接口、基礎(chǔ)功能類模塊、API等,構(gòu)成這些模塊的源文件就分布在api、apps、core、netif中,頭文件則匯總在include中。
api
NETCONN API和Socket API相關(guān)的源文件,只有在操作系統(tǒng)的環(huán)境中,才能被編譯
apps
應(yīng)用程序的源文件,包括常見的應(yīng)用程序,如httpd、mqtt、tftp、sntp、snmp等
core
LwIP的內(nèi)核源文件
include
LwIP所有模塊對(duì)應(yīng)的頭文件
netif
與網(wǎng)卡移植有關(guān)的文件
圖1 LwIP-2.1.2源碼包
移植接口解析
LwIP使用數(shù)據(jù)結(jié)構(gòu)體netif來描述網(wǎng)卡,并提供統(tǒng)一接口,需要與以太網(wǎng)底層驅(qū)動(dòng)接口函數(shù)結(jié)合使用,例如底層驅(qū)動(dòng)負(fù)責(zé)完成網(wǎng)卡的初始化、網(wǎng)卡的數(shù)據(jù)收發(fā)等,當(dāng)LwIP內(nèi)核需要發(fā)送一個(gè)數(shù)據(jù)包時(shí),就會(huì)通過LWIP提供的接口函數(shù)去調(diào)用底層網(wǎng)卡的發(fā)送函數(shù),將數(shù)據(jù)由硬件接口與軟件內(nèi)核銜接在一起。
contrib文件中包含部分可使用的網(wǎng)卡移植模板文件,其中ethernetif.c文件(contrib-2.1.0examplesethernetif目錄下的ethernetif.c文件)為底層接口驅(qū)動(dòng)的模板,以 LibSamples 為例,若要基于 LibSample的以太網(wǎng)驅(qū)動(dòng)移植LwIP,則需參考ethernetif.c模板,根據(jù)以太網(wǎng)驅(qū)動(dòng)及所需配置進(jìn)行修改,將底層驅(qū)動(dòng) ethernet 相關(guān)函數(shù)填充到LwIP所需的指定功能函數(shù)中。
ethernetif.c文件中的函數(shù)通常為與硬件打交道的底層函數(shù),當(dāng)有數(shù)據(jù)需要通過網(wǎng)卡接收或者發(fā)送數(shù)據(jù)的時(shí)候就會(huì)被調(diào)用,經(jīng)過LwIP協(xié)議棧內(nèi)部進(jìn)行處理后,從應(yīng)用層就能得到數(shù)據(jù)或者可以發(fā)送數(shù)據(jù)。該文件中包括函數(shù):low_level_init()、low_level_output()、low_level_input()、ethernetif_input()和ethernetif_init()函數(shù)。
ethernetif_init()
LwIP中默認(rèn)的網(wǎng)卡初始化函數(shù),內(nèi)部封裝了low_level_init()函數(shù)
ethernetif_input()
該函數(shù)用于接收網(wǎng)卡數(shù)據(jù),內(nèi)部封裝了low_level_input()函數(shù),在接收完畢時(shí),將數(shù)據(jù)通過pbuf遞交給上層。
low_level_init()
low_level_init()函數(shù)主要是根據(jù)實(shí)際情況對(duì)網(wǎng)卡進(jìn)行一系列的初始化工作,例如:初始化MAC地址、長度, 設(shè)置最大傳輸包的大小,設(shè)置網(wǎng)卡的屬性字段等功能。
該函數(shù)中需要調(diào)用以太網(wǎng)底層驅(qū)動(dòng)中的相關(guān)初始化函數(shù),以 LibSamples為例,該函數(shù)需要調(diào)用以太網(wǎng)底層驅(qū)動(dòng) hal_enet.c/.h 的 PHY、MAC、DMA相關(guān)初始化函數(shù)并進(jìn)行配置。
low_level_output()
該函數(shù)用于實(shí)現(xiàn)網(wǎng)卡發(fā)送數(shù)據(jù),是一個(gè)底層驅(qū)動(dòng)函數(shù),需根據(jù)以太網(wǎng)底層驅(qū)動(dòng)進(jìn)行相應(yīng)修改,若想通過一個(gè)網(wǎng)卡發(fā)送數(shù)據(jù),則需要將該數(shù)據(jù)傳入LwIP內(nèi)核中,經(jīng)過層層封裝最后存儲(chǔ)在pbuf數(shù)據(jù)包中,需注意pbuf以鏈表的形式存在,數(shù)據(jù)發(fā)送時(shí)是以一整個(gè)數(shù)據(jù)包全部發(fā)送的。
low_level_input()
low_level_input()函數(shù)用于從網(wǎng)卡中接收一個(gè)數(shù)據(jù)包,并將該數(shù)據(jù)包封裝在pbuf中遞交給上層,該函數(shù)需要調(diào)用以太網(wǎng)底層驅(qū)動(dòng)中的接收函數(shù)。
移植LwIP協(xié)議棧
基于LibSamples的以太網(wǎng)驅(qū)動(dòng)對(duì)LwIP進(jìn)行移植,需先將LwIP源文件中的部分文件添加到LibSamples中,如: src 源文件、 include 頭文件。
若想令LwIP運(yùn)行,還需補(bǔ)充contrib文件中部分內(nèi)容,如圖2所示,由于部分源文件中使用頭文件寫法為”arch/xx”,因此,在src文件下新建arch文件,并將需要修改的模板文件及contrib中的部分接口文件放入arch文件中。
ethernetif.c網(wǎng)卡移植模板文件
cc.h文件主要完成協(xié)議棧內(nèi)部使用的數(shù)據(jù)類型的定義
lwipopts.h文件包含了用戶對(duì)協(xié)議棧內(nèi)核參數(shù)進(jìn)行的配置,若未在lwipopts.h文件中進(jìn)行配置,則LwIP會(huì)使用opt.h中的默認(rèn)參數(shù)
perf.h文件是實(shí)現(xiàn)與系通通計(jì)和測(cè)量相關(guān)的功能,若未使用該功能,則無需修改
bpstruct.h、epstruct.h由contrib文件下的ports文件所提供,屬于堆棧的一部分,無需修改
圖2 LWIP移植所需部分文件
lwipopts.h文件中需要根據(jù)是否為操作系統(tǒng)模擬層、堆內(nèi)存大小、是否使用TCP及TCP相關(guān)配置等進(jìn)行宏定義配置,例如:宏定義 NO_SYS 表示無操作系統(tǒng)模擬層,因?yàn)楫?dāng)前為無操作系統(tǒng)的移植,所以設(shè)置該宏定義為1。
... /** *NO_SYS==1:ProvidesVERYminimalfunctionality.Otherwise, *useLwIPfacilities. */ #defineNO_SYS1 ...
cc.h文件中包含處理器相關(guān)的變量類型、數(shù)據(jù)結(jié)構(gòu)及字節(jié)對(duì)齊的相關(guān)宏,需根據(jù)處理器及編譯器進(jìn)行修改。
... #defineLWIP_NO_STDINT_H1 typedefunsignedcharu8_t; typedefsignedchars8_t; typedefunsignedshortu16_t; typedefsignedshorts16_t; typedefunsignedlongu32_t; typedefsignedlongs32_t; typedefu32_tmem_ptr_t; typedefintsys_prot_t; #defineU16_F"hu" #defineS16_F"d" #defineX16_F"hx" #defineU32_F"u" #defineS32_F"d" #defineX32_F"x" #defineSZT_F"uz" ... #elifdefined(__GNUC__) #definePACK_STRUCT_BEGIN #definePACK_STRUCT_STRUCT__attribute__((__packed__)) #definePACK_STRUCT_END #definePACK_STRUCT_FIELD(x)x ...
low_level_init移植接口實(shí)現(xiàn)
頭文件配置并修改完成后,需要對(duì)移植模板文件 ethernetif.c 進(jìn)行修改。
在以太網(wǎng)底層驅(qū)動(dòng)與LwIP初始化接口的銜接上,對(duì)low_level_init()進(jìn)行修改,在對(duì)LwIP的netif結(jié)構(gòu)體進(jìn)行相關(guān)配置之前,需要通過以太網(wǎng)底層驅(qū)動(dòng)使硬件被初始化;初始化后,配置 MAC 硬件地址,鏈接發(fā)送描述符及接收描述符并進(jìn)行描述符內(nèi)容配置,配置描述符地址,配置完成后,使能以太網(wǎng) DMA 啟動(dòng)傳輸,此時(shí),初始化完成。
staticvoid low_level_init(structnetif*netif) { structethernetif*ethernetif=netif->state; /*setMAChardwareaddresslength*/ netif->hwaddr_len=ETHARP_HWADDR_LEN; /*setMAChardwareaddress*/ netif->hwaddr[0]=BOARD_MAC_ADDR0; netif->hwaddr[1]=BOARD_MAC_ADDR1; netif->hwaddr[2]=BOARD_MAC_ADDR2; netif->hwaddr[3]=BOARD_MAC_ADDR3; netif->hwaddr[4]=BOARD_MAC_ADDR4; netif->hwaddr[5]=BOARD_MAC_ADDR5; /*maximumtransferunit*/ netif->mtu=1500; /*devicecapabilities*/ /*don'tsetNETIF_FLAG_ETHARPifthisdeviceisnotanethernetone*/ netif->flags=NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP; #ifLWIP_IPV6&&LWIP_IPV6_MLD /* *Forhardware/netifsthatimplementMACfiltering. *All-nodeslink-localishandledbydefault,sowemustletthehardwareknow *toallowmulticastpacketsin. *Shouldsetmld_mac_filterpreviously.*/ if(netif->mld_mac_filter!=NULL){ ip6_addr_tip6_allnodes_ll; ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll); netif->mld_mac_filter(netif,&ip6_allnodes_ll,NETIF_ADD_MAC_FILTER); } #endif/*LWIP_IPV6&&LWIP_IPV6_MLD*/ ETH_GPIOInit(); SysTick->CTRL|=((uint32_t)0x00000004); SysTick_Config(120000000/1000); ETH_InitTypeDefptr; ETH_StructInit(&ptr); ptr.ETH_AutoNegotiation=ETH_AutoNegotiation_Disable; ETH_Init(&ptr,ENET_PHY_ADDR); ETH->DMAOMR&=~ETH_DMAOMR_OSF; /*EnableETHDMAinterrupt.*/ ETH_DMAITConfig(ETH_DMA_IT_NIS|ETH_DMA_IT_R,ENABLE); NVIC_InitTypeDefNVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel=ENET_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStruct); /*Configmacdfilteraddress.*/ ENET_SetupMacAddrFilter(0x1u<<31|0x1u<<5,?ENET_ADDR_FILTER_NUM,?0u,?netif->hwaddr); /*Settxdmadesplink.*/ memset(enet_txdma_desp_tbl,0,sizeof(enet_txdma_desp_tbl)); for(uint32_ti=0u;iDMATXDSAR=(uint32_t)(&enet_txdma_desp_tbl[0]); enet_usable_txdma_desp=&enet_txdma_desp_tbl[0]; /*Setrxdmadesplink.*/ memset(enet_rxdma_desp_tbl,0,sizeof(enet_rxdma_desp_tbl)); for(uint32_ti=0;iDMARXDSAR=(uint32_t)enet_rxdma_desp_tbl; enet_first_rxdma_desp=&enet_rxdma_desp_tbl[0]; ETH_Start(); }
low_level_output移植接口實(shí)現(xiàn)
low_level_output()函數(shù)與以太網(wǎng)底層驅(qū)動(dòng)的發(fā)送功能函數(shù)相結(jié)合,將LwIP要發(fā)送的數(shù)據(jù)存儲(chǔ)到以太網(wǎng)發(fā)送描述符中所指定的存儲(chǔ)區(qū)域中,再對(duì)發(fā)送描述符進(jìn)行配置并進(jìn)行發(fā)送。
staticerr_t low_level_output(structnetif*netif,structpbuf*p) { structethernetif*ethernetif=netif->state; structpbuf*q; /*Getcurrentdestinationaddress.*/ ETH_DMADESCTypeDef*txdma_desp=enet_usable_txdma_desp; if(0u!=(txdma_desp->CS&TXDMA_DES0_OWN)){ returnERR_USE; } #ifETH_PAD_SIZE pbuf_remove_header(p,ETH_PAD_SIZE);/*dropthepaddingword*/ #endif uint32_te_offset=0;/*recordenetmodulebufoffset.*/ for(q=p;q!=NULL;q=q->next){ /*Sendthedatafromthepbuftotheinterface,onepbufata time.Thesizeofthedataineachpbufiskeptinthe->len variable.*/ for(uint32_ti=0;ilen;i++){ ((uint8_t*)(txdma_desp->BUF1ADDR))[e_offset]=((uint8_t*)(q->payload))[i]; e_offset++; if(e_offset==ENET_TX_BUFLEN){ txdma_desp=(ETH_DMADESCTypeDef*)(txdma_desp->BUF2NDADDR); if((txdma_desp->CS&TXDMA_DES0_OWN)!=0u){ returnERR_USE; } e_offset=0; } } } if(p->tot_len<=?ENET_TX_BUFLEN)?{ ????enet_usable_txdma_desp->CS|=TXDMA_DES0_TFS|TXDMA_DES0_TLS|TXDMA_DES0_OWN; enet_usable_txdma_desp->BL&=~0x1FFF; enet_usable_txdma_desp->BL|=p->tot_len;/*TBS1!BUF2NDADDR; }else{ enet_usable_txdma_desp->CS|=TXDMA_DES0_TFS;/*TFS=1u.*/ enet_usable_txdma_desp->CS&=~TXDMA_DES0_TLS;/*TLS=0u.*/ enet_usable_txdma_desp->BL&=~0x1FFF; enet_usable_txdma_desp->BL|=ENET_TX_BUFLEN;/*!BUF2NDADDR; for(uint32_ti=ENET_TX_BUFLEN;itot_len-ENET_TX_BUFLEN;i+=ENET_TX_BUFLEN){ enet_usable_txdma_desp->CS&=~TXDMA_DES0_TFS;/*TFS=0u.*/ enet_usable_txdma_desp->CS&=~TXDMA_DES0_TLS;/*TLS=0u.*/ enet_usable_txdma_desp->BL&=~0x1FFF; enet_usable_txdma_desp->BL|=ENET_TX_BUFLEN; enet_usable_txdma_desp=(ETH_DMADESCTypeDef*)enet_usable_txdma_desp->BUF2NDADDR; } enet_usable_txdma_desp=(ETH_DMADESCTypeDef*)enet_usable_txdma_desp->BUF2NDADDR; enet_usable_txdma_desp->CS&=~TXDMA_DES0_TFS;/*TFS=0u.*/ enet_usable_txdma_desp->CS|=TXDMA_DES0_TLS;/*TLS=1u.*/ enet_usable_txdma_desp->BL&=~0x1FFF; enet_usable_txdma_desp->BL|=(p->tot_len%ENET_TX_BUFLEN); } if(0!=(ETH->DMASRÐ_DMA_TransmitProcess_Suspended)){ ETH_ResumeDMATransmission(); } MIB2_STATS_NETIF_ADD(netif,ifoutoctets,p->tot_len); if(((u8_t*)p->payload)[0]&1){ /*broadcastormulticastpacket*/ MIB2_STATS_NETIF_INC(netif,ifoutnucastpkts); }else{ /*unicastpacket*/ MIB2_STATS_NETIF_INC(netif,ifoutucastpkts); } /*increaseifoutdiscardsorifouterrorsonerror*/ #ifETH_PAD_SIZE pbuf_add_header(p,ETH_PAD_SIZE);/*reclaimthepaddingword*/ #endif LINK_STATS_INC(link.xmit); returnERR_OK; }
low_level_input移植接口實(shí)現(xiàn)
low_level_input()函數(shù)與以太網(wǎng)底層驅(qū)動(dòng)的接收功能函數(shù)相結(jié)合,將接收到的數(shù)據(jù)存入LwIP的pbuf鏈中。ethernetif_input()函數(shù)調(diào)用low_level_input()函數(shù)。
staticstructpbuf* low_level_input(structnetif*netif) { structethernetif*ethernetif=netif->state; structpbuf*p,*q; u16_tlen; ETH_DMADESCTypeDef*rxdma_desp=enet_first_rxdma_desp; for(uint32_ti=0;iCS&RXDMA_DES0_RLS)!=0){ len=(uint32_t)(rxdma_desp->CS&RXDMA_DES0_FL)>>16; break; }elseif((rxdma_desp->CS&RXDMA_DES0_OWN)!=0){ returnNULL; }else{ rxdma_desp=(ETH_DMADESCTypeDef*)(rxdma_desp->BUF2NDADDR); } } #ifETH_PAD_SIZE len+=ETH_PAD_SIZE;/*allowroomforEthernetpadding*/ #endif /*Weallocateapbufchainofpbufsfromthepool.*/ p=pbuf_alloc(PBUF_RAW,len,PBUF_POOL); if(p!=NULL){ #ifETH_PAD_SIZE pbuf_remove_header(p,ETH_PAD_SIZE);/*dropthepaddingword*/ #endif /*Weiterateoverthepbufchainuntilwehavereadtheentire *packetintothepbuf.*/ uint32_te_offset=0; rxdma_desp=enet_first_rxdma_desp; for(q=p;q!=NULL;q=q->next){ /*Readenoughbytestofillthispbufinthechain.The *availabledatainthepbufisgivenbytheq->len *variable. *Thisdoesnotnecessarilyhavetobeamemcpy,youcanalsopreallocate *pbufsforaDMA-enabledMACandafterreceivingtruncateittothe *actuallyreceivedsize.Inthiscase,ensurethetot_lenmemberofthe *pbufisthesumofthechainedpbuflenmembers. */ for(uint32_ti=0;ilen;i++){ ((uint8_t*)q->payload)[i]=((uint8_t*)rxdma_desp->BUF1ADDR)[e_offset]; e_offset++; if(e_offset==ENET_RX_BUFLEN){ rxdma_desp=(ETH_DMADESCTypeDef*)(rxdma_desp->BUF2NDADDR); e_offset=0; } } } MIB2_STATS_NETIF_ADD(netif,ifinoctets,p->tot_len); if(((u8_t*)p->payload)[0]&1){ /*broadcastormulticastpacket*/ MIB2_STATS_NETIF_INC(netif,ifinnucastpkts); }else{ /*unicastpacket*/ MIB2_STATS_NETIF_INC(netif,ifinucastpkts); } #ifETH_PAD_SIZE pbuf_add_header(p,ETH_PAD_SIZE);/*reclaimthepaddingword*/ #endif LINK_STATS_INC(link.recv); }else{ LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); MIB2_STATS_NETIF_INC(netif,ifindiscards); } do{ enet_first_rxdma_desp->CS|=RXDMA_DES0_OWN;/*SetOWNbit.*/ enet_first_rxdma_desp=(ETH_DMADESCTypeDef*)enet_first_rxdma_desp->BUF2NDADDR; }while((enet_first_rxdma_desp->CS&RXDMA_DES0_OWN)==0); if(RESET!=(ETH_GetDMAFlagStatus((0x4<17))?)?){?/*!
ENET_IRQHandler中斷服務(wù)函數(shù)實(shí)現(xiàn)
/*ENETIRQHandler.*/ voidENET_IRQHandler() { if(0!=ETH_GetDMAFlagStatus(ETH_DMA_FLAG_R)) { ethernetif_input(gnetif); ETH_DMAClearFlag(ETH_DMA_FLAG_R); } }
自定義參數(shù)聲明及函數(shù)實(shí)現(xiàn)
需要根據(jù)實(shí)際選用的開發(fā)板和運(yùn)行參數(shù)等進(jìn)行宏定義配置,如 IP 地址、端口號(hào)、MAC地址需要根據(jù)實(shí)際的網(wǎng)絡(luò)環(huán)境進(jìn)行配置,這里以LwIP_TCP_Client樣例為例,將這些參數(shù)定義到了 lwip_tcp_client.h 文件中。
/*initializationenet.*/ #defineENET_PHY_ADDR0x01/*SelectPHYaddress.*/ #defineENET_PHY_CONTROLREG0u/*PHYcontrolregisteraddress.*/ #defineENET_PHY_STATUSREG1u/*PHYstatusregistersddress.*/ #defineENET_PHY_RESET0x8000/*SetPHYreset,useinENET_PHY_CRregisters*/ #defineENET_PHY_SPEED100M0x2000/*SetPHYspeed.*/ #defineENET_PHY_FULLDUPLEX0x0100/*SetPHYduplexmodeaboutfullduplex.*/ #defineENET_PHY_LINK0x0004/*PHYlink-up.*/ #defineENET_PHY_UNIDIRECTIONAL0x0080/*PHYhastheabilitytoencodeandtransmitdatafromPHYthroughMIIinterface,regardlessofwhetherPHYhasdeterminedthataneffectivelinkhasbeenconnectedandestablished.*/ #defineENET_PHY_AUTONEGOTIATION0x1000/*PHYautonegotiation.*/ #defineENET_TX_BUFLEN1500u/*Txbufferlength.*/ #defineENET_TX_NUM4u/*Thenumberoftx.*/ #defineENET_RX_BUFLEN1500u/*Configuretheframelengthofareceivedframe.*/ #defineENET_RX_NUM4u/*Theconfigurednumberofreceiveddescriptorthatcanbeusedforreceiving.*/ #defineENET_ADDR_FILTER_NUM5u/*SelectMACaddressfilternumberfrom0~5.*/ #defineBOARD_MAC_ADDR02u #defineBOARD_MAC_ADDR11u #defineBOARD_MAC_ADDR20u #defineBOARD_MAC_ADDR30u #defineBOARD_MAC_ADDR40u #defineBOARD_MAC_ADDR50u #defineBOARD_IP_ADDR0192u #defineBOARD_IP_ADDR1168u #defineBOARD_IP_ADDR2105u #defineBOARD_IP_ADDR398u #defineBOARD_NETMASK_ADDR0255u #defineBOARD_NETMASK_ADDR1255u #defineBOARD_NETMASK_ADDR2255u #defineBOARD_NETMASK_ADDR30u #defineBOARD_GW_ADDR0192u #defineBOARD_GW_ADDR1168u #defineBOARD_GW_ADDR21u #defineBOARD_GW_ADDR31u #defineBOARD_TCP_SERVER_IPADDR0192u #defineBOARD_TCP_SERVER_IPADDR1168u #defineBOARD_TCP_SERVER_IPADDR2105u #defineBOARD_TCP_SERVER_IPADDR385u #defineBOARD_TCP_SERVER_PORT6800u #defineTXDMA_DES0_TCH0x01u<<20 #define?TXDMA_DES0_TFS??????????????????????0x01u<<28 #define?TXDMA_DES0_TLS??????????????????????0x01u<<29 #define?TXDMA_DES0_OWN??????????????????????0x01u<<31 #define?RXDMA_DES0_RLS??????????????????????0x01u<<8 #define?RXDMA_DES0_FL???????????????????????0x3FFFu<<16 #define?RXDMA_DES0_OWN??????????????????????0x01u<<31 #define?RXDMA_DES1_RCH??????????????????????0x01u<<14 #define?RXDMA_DES1_RBS1?????????????????????0x1FFFu #define?FILTERS_RECEIVE_ALL?????????????????0x01u<<31 #define?FILTERS_BOARDCAST_FILTER????????????0x01u<<5
在lwip_tcp_client.c文件中除了對(duì)Ethernet相關(guān)的時(shí)鐘引腳進(jìn)行配置及使用到的系統(tǒng)時(shí)鐘對(duì)應(yīng)參數(shù)申明外,也根據(jù)LwIP協(xié)議棧實(shí)際的應(yīng)用需求,實(shí)現(xiàn)了關(guān)于MAC地址過濾器的函數(shù)。
voidENET_SetupMacAddrFilter(uint32_tfilter,uint32_taddr_id,uint32_taddr_mask,uint8_t*addr) { ETH->MACAFR|=filter; if((0u!=(filterÐ_SourceAddrFilter_Normal_Enable))||(0u!=(filter&0x100)))/*Setsourceaddressfilter.*/ { ETH->MACA0HR=(0x1u<<31?|?0x1u<<30?|?(uint32_t)addr[4u]?|?((uint32_t)addr[5u]<<8u)?);; ????????ETH->MACA0LR=((uint32_t)addr[0u]|((uint32_t)addr[1u]<8u)?|?((uint32_t)addr[2u]?<16u)?|?((uint32_t)addr[3u]?<24u)?);; ????} ????else?if?(?(0u?!=?(filter?&?0x10))?||?(0u?!=?(filter?&?0x100))??)?/*?Set?destination?address?filter.?*/ ????{ ????????ETH->MACAFR&=~(0x1u<<4?|?0x1u<<1); ????} ????if?(0u?!=?addr_mask) ????{ ????????ETH->MACA0HR|=addr_mask; } }
/*Returnsthecurrenttimeinmilliseconds,thisAPIfromlwip/sys.h*/ uint32_tsys_now(void) { returnsystime_ms; } uint32_tsys_jiffies(void) { returnsystime_ms*1000000; }
樣例說明
基于移植的 LwIP協(xié)議,LibSamples還提供了展示 TCP 協(xié)議客戶端與服務(wù)器通信的 lwip_tcp_client、lwip_tcp_server樣例,展示 UDP 協(xié)議客戶端與服務(wù)器通信的 lwip_udp_client、lwip_udp_server。
樣例實(shí)現(xiàn)環(huán)境搭建
本文基于搭載了MM32F5277E9P MCU的開發(fā)板 PLUS-F5270 V2.0進(jìn)行實(shí)現(xiàn),使用2根網(wǎng)線,分別連接電腦與路由器、開發(fā)板與路由器。
在官網(wǎng)(http://free.cmsoft.cn/reslink.php?id=205)下載網(wǎng)絡(luò)調(diào)試助手NetAssist并安裝,用于后續(xù)的樣例功能驗(yàn)證。
打開電腦終端(WIN+R鍵,輸入CMD),然后輸入指令 ipconfig/all ,查看本機(jī)的以太網(wǎng)IP地址為 192.168.108.85 ;
在終端中輸入命令 netstat -na 獲取本地開放端口,這里我們獲取到可用端口號(hào)為 49153 。
LwIP_TCP_Client
LwIP_TCP_Client 樣例用于展示基于以太網(wǎng)及 LwIP使用 TCP 協(xié)議作為客戶端,進(jìn)行客戶端與服務(wù)器之間的通信。
若想使用LwIP,則需要先將協(xié)議棧初始化,并設(shè)置主機(jī)的IP地址、子網(wǎng)掩碼、網(wǎng)關(guān)地址等。需注意,樣例工程中所設(shè)置的IP地址需要與路由器處于同一子網(wǎng),如圖3所示,在命令提示符(CMD)中使用命令 ipconfig/all 可查看各IP的詳細(xì)信息,例如所查出的以太網(wǎng)IPx4地址為192.168.108.85,則在樣例工程中可設(shè)置IP地址為192.168.108.98,網(wǎng)關(guān)地址與子網(wǎng)掩碼的配置需與所查出的以太網(wǎng)默認(rèn)網(wǎng)關(guān)及子網(wǎng)掩碼相同。
voidapp_lwip_init(void) { ip4_addr_tipaddr; ip4_addr_tnetmask; ip4_addr_tgw; IP4_ADDR(&ipaddr,BOARD_IP_ADDR0,BOARD_IP_ADDR1,BOARD_IP_ADDR2,BOARD_IP_ADDR3); IP4_ADDR(&netmask,BOARD_NETMASK_ADDR0,BOARD_NETMASK_ADDR1,BOARD_NETMASK_ADDR2,BOARD_NETMASK_ADDR3); IP4_ADDR(&gw,BOARD_GW_ADDR0,BOARD_GW_ADDR1,BOARD_GW_ADDR2,BOARD_GW_ADDR3); lwip_init(); ... }
圖3 在CMD界面通過命令查詢以太網(wǎng)IP信息
在配置完IP地址等必要信息后,需掛載網(wǎng)卡,在LwIP中網(wǎng)卡掛載函數(shù)為 netif_add() 函數(shù),將所配置的數(shù)據(jù)傳入該函數(shù)中。
voidapp_lwip_init(void) { ... netif_add(&gnetif,&ipaddr,&netmask,&gw,NULL,ðernetif_init,ðernet_input); netif_set_default(&gnetif); if(netif_is_link_up(&gnetif)) { netif_set_up(&gnetif); } else { netif_set_down(&gnetif); } }
LwIP協(xié)議棧初始化后,需要對(duì)所使用的 TCP Client(TCP客戶端)進(jìn)行初始化配置。在 LwIP中存在多個(gè)與 TCP 相關(guān)的函數(shù),LwIP_TCP_Client樣例所使用到的函數(shù)包括:
tcp_new()
創(chuàng)建一個(gè)TCP的PCB控制塊
tcp_connect()
連接遠(yuǎn)端主機(jī)
tcp_err()
控制塊err字段注冊(cè)的回調(diào)函數(shù),遇到錯(cuò)誤時(shí)被調(diào)用
tcp_write()
構(gòu)造一個(gè)報(bào)文并放入控制塊的發(fā)送緩沖隊(duì)列中
tcp_recv()
控制塊rev字段注冊(cè)的回調(diào)函數(shù),當(dāng)接收到新數(shù)據(jù)是被調(diào)用
tcp_recved()
當(dāng)程序處理完數(shù)據(jù)后調(diào)用該函數(shù),通知內(nèi)核更新接收窗口
tcp_close()
關(guān)閉一個(gè)TCP連接
TCP 客戶端的工作流程包括:新建控制塊、建立連接、發(fā)送請(qǐng)求與接收數(shù)據(jù)并處理。TCP客戶端工作流程如圖4所示。
圖4 TCP客戶端流程圖
TCP Client(TCP客戶端)進(jìn)行初始化配置時(shí),通過 IP4_ADDR() 函數(shù)將目標(biāo)服務(wù)器的IP寫入結(jié)構(gòu)體;再通過 tcp_new() 函數(shù)為TCP客戶端分配一個(gè)結(jié)構(gòu),當(dāng)該結(jié)構(gòu)不為空時(shí),使用 tcp_connect() 函數(shù)與目標(biāo)服務(wù)器進(jìn)行連接,該函數(shù)中配置目標(biāo)端口和目標(biāo)IP參數(shù)并調(diào)用連接完成回調(diào)函數(shù)。
voidapp_tcp_client_init(void) { structtcp_pcb*tcp_client_pcb; ip_addr_tapp_server_ip; /*WritetheIPofthetargetserverintoastructure,whichisthelocalconnectionIPaddressofthepc.*/ IP4_ADDR(&app_server_ip,BOARD_TCP_SERVER_IPADDR0,BOARD_TCP_SERVER_IPADDR1,BOARD_TCP_SERVER_IPADDR2,BOARD_TCP_SERVER_IPADDR3); /*AssignastructuretotheTCPclient*/ tcp_client_pcb=tcp_new(); if(tcp_client_pcb!=NULL) { /*Connectwiththetargetserver,andtheparametersincludethetargetportandthetargetIP.*/ tcp_connect(tcp_client_pcb,&app_server_ip,BOARD_TCP_SERVER_PORT,app_tcp_client_connected); /*Registeredconnectionerrorhandlingcallbackfunction.*/ tcp_err(tcp_client_pcb,app_tcp_client_connecterror); } }
在連接完成回調(diào)函數(shù)中,使用 tcp_write() 函數(shù)發(fā)送問候字符串以建立連接,并使用 tcp_recv() 函數(shù)配置接收回調(diào)函數(shù)。
staticerr_tapp_tcp_client_connected(void*arg,structtcp_pcb*pcb,err_terr) { /*Sendagreetingstringtoestablishaconnection*/ tcp_write(pcb,clientstring,strlen(clientstring),1u); /*Configurethereceivecallbackfunction*/ tcp_recv(pcb,app_tcp_client_xfer); returnERR_OK; }
在TCP客戶端接收數(shù)據(jù)后的數(shù)據(jù)處理回調(diào)函數(shù)中,接收到有效數(shù)據(jù)后,通過tcp_recved()更新接收窗口,使用 tcp_write() 函數(shù)將接收到的服務(wù)器內(nèi)容回顯。
staticerr_tapp_tcp_client_xfer(void*arg,structtcp_pcb*pcb,structpbuf*tcp_recv_pbuf,err_terr) { if(tcp_recv_pbuf!=NULL) { /*Updatethereceivingwindow*/ tcp_recved(pcb,tcp_recv_pbuf->tot_len); tcp_write(pcb,tcp_recv_pbuf->payload,tcp_recv_pbuf->len,1u); pbuf_free(tcp_recv_pbuf); } elseif(err==ERR_OK) { tcp_close(pcb); app_tcp_client_init(); returnERR_OK; } returnERR_OK; }
lwip_tcp_client 樣例的實(shí)驗(yàn)現(xiàn)象如圖5所示,通過網(wǎng)絡(luò)調(diào)試助手可查看到連接成功后,遠(yuǎn)端服務(wù)器收到客戶端發(fā)送的數(shù)據(jù),服務(wù)器向客戶端發(fā)送任意數(shù)據(jù)包后,客戶端回顯相同數(shù)據(jù)。
圖5 lwip_tcp_client樣例實(shí)驗(yàn)現(xiàn)象
注意事項(xiàng)
在官網(wǎng)下載網(wǎng)絡(luò)調(diào)試助手NetAssist并安裝,用于后續(xù)的樣例功能驗(yàn)證。
打開電腦終端(WIN+R鍵,輸入CMD),然后輸入指令` ipconfig/all `,查看本機(jī)的以太網(wǎng)IP地址。
在配置 IP 地址和端口號(hào)時(shí),當(dāng)連接了WIFI后需要注意我們選用的是以太網(wǎng)的IP地址,而非WLAN的IP地址。
在終端中輸入命令 `netstat -na` 獲取本地開放端口。
關(guān)于靈動(dòng)
上海靈動(dòng)微電子股份有限公司成立于 2011 年,是中國本土領(lǐng)先的通用 32 位 MCU 產(chǎn)品及解決方案供應(yīng)商。公司基于 Arm Cortex-M 系列內(nèi)核開發(fā)的 MM32 MCU 產(chǎn)品目前已量產(chǎn)近 300 款型號(hào),累計(jì)交付超 5 億顆,每年都有近億臺(tái)配備了靈動(dòng) MM32MCU 的優(yōu)秀產(chǎn)品交付到客戶手中,在本土通用 32 位 MCU 公司中位居前列。
靈動(dòng)客戶涵蓋智能工業(yè)、汽車電子、通信基建、醫(yī)療健康、智慧家電、物聯(lián)網(wǎng)、個(gè)人設(shè)備、手機(jī)和電腦等應(yīng)用領(lǐng)域。靈動(dòng)是中國為數(shù)不多的同時(shí)獲得了 Arm-KEIL、IAR、SEGGER 官方支持的本土 MCU 公司,并建立了獨(dú)立、完整的通用 MCU 生態(tài)體系。靈動(dòng)始終秉承著“誠信、承諾、創(chuàng)新、合作”的精神,為客戶提供從硬件芯片到軟件算法、從參考方案到系統(tǒng)設(shè)計(jì)的全方位支持。
-
內(nèi)核
+關(guān)注
關(guān)注
3文章
1372瀏覽量
40276 -
計(jì)算機(jī)
+關(guān)注
關(guān)注
19文章
7488瀏覽量
87849 -
移植
+關(guān)注
關(guān)注
1文章
379瀏覽量
28124 -
LwIP
+關(guān)注
關(guān)注
2文章
86瀏覽量
27149
原文標(biāo)題:靈動(dòng)微課堂 (第282講)|基于MM32F5270的Ethernet實(shí)現(xiàn)LwIP協(xié)議棧移植
文章出處:【微信號(hào):MindMotion-MMCU,微信公眾號(hào):靈動(dòng)MM32MCU】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論