0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

基于MM32F5270的Ethernet實現(xiàn)LwIP協(xié)議棧移植

靈動MM32MCU ? 來源:靈動MM32MCU ? 2024-06-21 10:28 ? 次閱讀

LwIP簡介

LwIP是輕量化的TCP/IP協(xié)議,由瑞典計算機科學院(SICS)的Adam Dunkels 開發(fā)的一個小型開源的TCP/IP協(xié)議棧。LwIP具有高度可移植性、代碼開源,提供了三種編程接口(API):RAW API、NETCONN API 和 Socket API,用于與TCP/IP代碼進行通信。

通過官網(wǎng)可獲取LwIP源碼包及contrib包。源代碼包主要包含LwIP內(nèi)核的源碼文件,contrib包中包含部分移植和應用LwIP的demo。contrib包不屬于LwIP內(nèi)核的一部分,但很有參考價值。

以lwip-2.1.2版本的源碼包為例,如圖1所示,該源碼包分為三部分, src 文件為LWIP源代碼文件, doc 文件包含LwIP相關文檔, test 為LwIP測試文件,使用時主要關注于 src 文件下的內(nèi)容。

LwIP內(nèi)核是由一系列模塊組合而成,包括 TCP/IP 協(xié)議棧的各種協(xié)議、內(nèi)存管理、數(shù)據(jù)包管理、網(wǎng)卡接口、基礎功能類模塊、API等,構成這些模塊的源文件就分布在api、apps、core、netif中,頭文件則匯總在include中。

api

NETCONN API和Socket API相關的源文件,只有在操作系統(tǒng)的環(huán)境中,才能被編譯

apps

應用程序的源文件,包括常見的應用程序,如httpd、mqtt、tftp、sntp、snmp等

core

LwIP的內(nèi)核源文件

include

LwIP所有模塊對應的頭文件

netif

與網(wǎng)卡移植有關的文件

ec69c41c-1379-11ef-b74b-92fbcf53809c.png

圖1 LwIP-2.1.2源碼包

移植接口解析

LwIP使用數(shù)據(jù)結構體netif來描述網(wǎng)卡,并提供統(tǒng)一接口,需要與以太網(wǎng)底層驅(qū)動接口函數(shù)結合使用,例如底層驅(qū)動負責完成網(wǎng)卡的初始化、網(wǎng)卡的數(shù)據(jù)收發(fā)等,當LwIP內(nèi)核需要發(fā)送一個數(shù)據(jù)包時,就會通過LWIP提供的接口函數(shù)去調(diào)用底層網(wǎng)卡的發(fā)送函數(shù),將數(shù)據(jù)由硬件接口與軟件內(nèi)核銜接在一起。

contrib文件中包含部分可使用的網(wǎng)卡移植模板文件,其中ethernetif.c文件(contrib-2.1.0examplesethernetif目錄下的ethernetif.c文件)為底層接口驅(qū)動的模板,以 LibSamples 為例,若要基于 LibSample的以太網(wǎng)驅(qū)動移植LwIP,則需參考ethernetif.c模板,根據(jù)以太網(wǎng)驅(qū)動及所需配置進行修改,將底層驅(qū)動 ethernet 相關函數(shù)填充到LwIP所需的指定功能函數(shù)中。

ethernetif.c文件中的函數(shù)通常為與硬件打交道的底層函數(shù),當有數(shù)據(jù)需要通過網(wǎng)卡接收或者發(fā)送數(shù)據(jù)的時候就會被調(diào)用,經(jīng)過LwIP協(xié)議棧內(nèi)部進行處理后,從應用層就能得到數(shù)據(jù)或者可以發(fā)送數(shù)據(jù)。該文件中包括函數(shù):low_level_init()、low_level_output()、low_level_input()、ethernetif_input()和ethernetif_init()函數(shù)。

ethernetif_init()

LwIP中默認的網(wǎng)卡初始化函數(shù),內(nèi)部封裝了low_level_init()函數(shù)

ethernetif_input()

該函數(shù)用于接收網(wǎng)卡數(shù)據(jù),內(nèi)部封裝了low_level_input()函數(shù),在接收完畢時,將數(shù)據(jù)通過pbuf遞交給上層。

low_level_init()

low_level_init()函數(shù)主要是根據(jù)實際情況對網(wǎng)卡進行一系列的初始化工作,例如:初始化MAC地址、長度, 設置最大傳輸包的大小,設置網(wǎng)卡的屬性字段等功能。

該函數(shù)中需要調(diào)用以太網(wǎng)底層驅(qū)動中的相關初始化函數(shù),以 LibSamples為例,該函數(shù)需要調(diào)用以太網(wǎng)底層驅(qū)動 hal_enet.c/.h 的 PHY、MAC、DMA相關初始化函數(shù)并進行配置。

low_level_output()

該函數(shù)用于實現(xiàn)網(wǎng)卡發(fā)送數(shù)據(jù),是一個底層驅(qū)動函數(shù),需根據(jù)以太網(wǎng)底層驅(qū)動進行相應修改,若想通過一個網(wǎng)卡發(fā)送數(shù)據(jù),則需要將該數(shù)據(jù)傳入LwIP內(nèi)核中,經(jīng)過層層封裝最后存儲在pbuf數(shù)據(jù)包中,需注意pbuf以鏈表的形式存在,數(shù)據(jù)發(fā)送時是以一整個數(shù)據(jù)包全部發(fā)送的。

low_level_input()

low_level_input()函數(shù)用于從網(wǎng)卡中接收一個數(shù)據(jù)包,并將該數(shù)據(jù)包封裝在pbuf中遞交給上層,該函數(shù)需要調(diào)用以太網(wǎng)底層驅(qū)動中的接收函數(shù)。

移植LwIP協(xié)議棧

基于LibSamples的以太網(wǎng)驅(qū)動對LwIP進行移植,需先將LwIP源文件中的部分文件添加到LibSamples中,如: src 源文件、 include 頭文件。

若想令LwIP運行,還需補充contrib文件中部分內(nèi)容,如圖2所示,由于部分源文件中使用頭文件寫法為”arch/xx”,因此,在src文件下新建arch文件,并將需要修改的模板文件及contrib中的部分接口文件放入arch文件中。

ethernetif.c網(wǎng)卡移植模板文件

cc.h文件主要完成協(xié)議棧內(nèi)部使用的數(shù)據(jù)類型的定義

lwipopts.h文件包含了用戶對協(xié)議棧內(nèi)核參數(shù)進行的配置,若未在lwipopts.h文件中進行配置,則LwIP會使用opt.h中的默認參數(shù)

perf.h文件是實現(xiàn)與系通通計和測量相關的功能,若未使用該功能,則無需修改

bpstruct.h、epstruct.h由contrib文件下的ports文件所提供,屬于堆棧的一部分,無需修改

ec83e540-1379-11ef-b74b-92fbcf53809c.png

圖2 LWIP移植所需部分文件

lwipopts.h文件中需要根據(jù)是否為操作系統(tǒng)模擬層、堆內(nèi)存大小、是否使用TCP及TCP相關配置等進行宏定義配置,例如:宏定義 NO_SYS 表示無操作系統(tǒng)模擬層,因為當前為無操作系統(tǒng)的移植,所以設置該宏定義為1。

...
/**
*NO_SYS==1:ProvidesVERYminimalfunctionality.Otherwise,
*useLwIPfacilities.
*/
#defineNO_SYS1
...

cc.h文件中包含處理器相關的變量類型、數(shù)據(jù)結構及字節(jié)對齊的相關宏,需根據(jù)處理器及編譯器進行修改。

...
#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移植接口實現(xiàn)

頭文件配置并修改完成后,需要對移植模板文件 ethernetif.c 進行修改。

在以太網(wǎng)底層驅(qū)動與LwIP初始化接口的銜接上,對low_level_init()進行修改,在對LwIP的netif結構體進行相關配置之前,需要通過以太網(wǎng)底層驅(qū)動使硬件被初始化;初始化后,配置 MAC 硬件地址,鏈接發(fā)送描述符及接收描述符并進行描述符內(nèi)容配置,配置描述符地址,配置完成后,使能以太網(wǎng) DMA 啟動傳輸,此時,初始化完成。

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移植接口實現(xiàn)

low_level_output()函數(shù)與以太網(wǎng)底層驅(qū)動的發(fā)送功能函數(shù)相結合,將LwIP要發(fā)送的數(shù)據(jù)存儲到以太網(wǎng)發(fā)送描述符中所指定的存儲區(qū)域中,再對發(fā)送描述符進行配置并進行發(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移植接口實現(xiàn)

low_level_input()函數(shù)與以太網(wǎng)底層驅(qū)動的接收功能函數(shù)相結合,將接收到的數(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<

ENET_IRQHandler中斷服務函數(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ù)實現(xiàn)

需要根據(jù)實際選用的開發(fā)板和運行參數(shù)等進行宏定義配置,如 IP 地址、端口號、MAC地址需要根據(jù)實際的網(wǎng)絡環(huá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文件中除了對Ethernet相關的時鐘引腳進行配置及使用到的系統(tǒng)時鐘對應參數(shù)申明外,也根據(jù)LwIP協(xié)議棧實際的應用需求,實現(xià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]<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é)議客戶端與服務器通信的 lwip_tcp_client、lwip_tcp_server樣例,展示 UDP 協(xié)議客戶端與服務器通信的 lwip_udp_client、lwip_udp_server。

樣例實現(xiàn)環(huán)境搭建

本文基于搭載了MM32F5277E9P MCU的開發(fā)板 PLUS-F5270 V2.0進行實現(xiàn),使用2根網(wǎng)線,分別連接電腦與路由器、開發(fā)板與路由器。

在官網(wǎng)(http://free.cmsoft.cn/reslink.php?id=205)下載網(wǎng)絡調(diào)試助手NetAssist并安裝,用于后續(xù)的樣例功能驗證。

打開電腦終端(WIN+R鍵,輸入CMD),然后輸入指令 ipconfig/all ,查看本機的以太網(wǎng)IP地址為 192.168.108.85 ;

在終端中輸入命令 netstat -na 獲取本地開放端口,這里我們獲取到可用端口號為 49153 。

LwIP_TCP_Client

LwIP_TCP_Client 樣例用于展示基于以太網(wǎng)及 LwIP使用 TCP 協(xié)議作為客戶端,進行客戶端與服務器之間的通信。

若想使用LwIP,則需要先將協(xié)議棧初始化,并設置主機的IP地址、子網(wǎng)掩碼、網(wǎng)關地址等。需注意,樣例工程中所設置的IP地址需要與路由器處于同一子網(wǎng),如圖3所示,在命令提示符(CMD)中使用命令 ipconfig/all 可查看各IP的詳細信息,例如所查出的以太網(wǎng)IPx4地址為192.168.108.85,則在樣例工程中可設置IP地址為192.168.108.98,網(wǎng)關地址與子網(wǎng)掩碼的配置需與所查出的以太網(wǎng)默認網(wǎng)關及子網(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();
...
}

ec8eb2a4-1379-11ef-b74b-92fbcf53809c.png

圖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é)議棧初始化后,需要對所使用的 TCP Client(TCP客戶端)進行初始化配置。在 LwIP中存在多個與 TCP 相關的函數(shù),LwIP_TCP_Client樣例所使用到的函數(shù)包括:

tcp_new()

創(chuàng)建一個TCP的PCB控制塊

tcp_connect()

連接遠端主機

tcp_err()

控制塊err字段注冊的回調(diào)函數(shù),遇到錯誤時被調(diào)用

tcp_write()

構造一個報文并放入控制塊的發(fā)送緩沖隊列中

tcp_recv()

控制塊rev字段注冊的回調(diào)函數(shù),當接收到新數(shù)據(jù)是被調(diào)用

tcp_recved()

當程序處理完數(shù)據(jù)后調(diào)用該函數(shù),通知內(nèi)核更新接收窗口

tcp_close()

關閉一個TCP連接

TCP 客戶端的工作流程包括:新建控制塊、建立連接、發(fā)送請求與接收數(shù)據(jù)并處理。TCP客戶端工作流程如圖4所示。

eca9cdf0-1379-11ef-b74b-92fbcf53809c.png

圖4 TCP客戶端流程圖

TCP Client(TCP客戶端)進行初始化配置時,通過 IP4_ADDR() 函數(shù)將目標服務器的IP寫入結構體;再通過 tcp_new() 函數(shù)為TCP客戶端分配一個結構,當該結構不為空時,使用 tcp_connect() 函數(shù)與目標服務器進行連接,該函數(shù)中配置目標端口和目標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ù)將接收到的服務器內(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 樣例的實驗現(xiàn)象如圖5所示,通過網(wǎng)絡調(diào)試助手可查看到連接成功后,遠端服務器收到客戶端發(fā)送的數(shù)據(jù),服務器向客戶端發(fā)送任意數(shù)據(jù)包后,客戶端回顯相同數(shù)據(jù)。

ecbd78f0-1379-11ef-b74b-92fbcf53809c.png

圖5 lwip_tcp_client樣例實驗現(xiàn)象

注意事項

在官網(wǎng)下載網(wǎng)絡調(diào)試助手NetAssist并安裝,用于后續(xù)的樣例功能驗證。

打開電腦終端(WIN+R鍵,輸入CMD),然后輸入指令` ipconfig/all `,查看本機的以太網(wǎng)IP地址。

在配置 IP 地址和端口號時,當連接了WIFI后需要注意我們選用的是以太網(wǎng)的IP地址,而非WLAN的IP地址。

在終端中輸入命令 `netstat -na` 獲取本地開放端口。

關于靈動

上海靈動微電子股份有限公司成立于 2011 年,是中國本土領先的通用 32 位 MCU 產(chǎn)品及解決方案供應商。公司基于 Arm Cortex-M 系列內(nèi)核開發(fā)的 MM32 MCU 產(chǎn)品目前已量產(chǎn)近 300 款型號,累計交付超 5 億顆,每年都有近億臺配備了靈動 MM32MCU 的優(yōu)秀產(chǎn)品交付到客戶手中,在本土通用 32 位 MCU 公司中位居前列。

靈動客戶涵蓋智能工業(yè)、汽車電子、通信基建、醫(yī)療健康、智慧家電、物聯(lián)網(wǎng)、個人設備、手機和電腦等應用領域。靈動是中國為數(shù)不多的同時獲得了 Arm-KEIL、IAR、SEGGER 官方支持的本土 MCU 公司,并建立了獨立、完整的通用 MCU 生態(tài)體系。靈動始終秉承著“誠信、承諾、創(chuàng)新、合作”的精神,為客戶提供從硬件芯片到軟件算法、從參考方案到系統(tǒng)設計的全方位支持。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 內(nèi)核
    +關注

    關注

    3

    文章

    1410

    瀏覽量

    41125
  • 計算機
    +關注

    關注

    19

    文章

    7633

    瀏覽量

    90238
  • 移植
    +關注

    關注

    1

    文章

    394

    瀏覽量

    28585
  • LwIP
    +關注

    關注

    2

    文章

    89

    瀏覽量

    28059

原文標題:靈動微課堂 (第282講)|基于MM32F5270的Ethernet實現(xiàn)LwIP協(xié)議棧移植

文章出處:【微信號:MindMotion-MMCU,微信公眾號:靈動MM32MCU】歡迎添加關注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關推薦
    熱點推薦

    MM32F5270平臺ADC注入通道的單周期采樣的實現(xiàn)

    MM32F5270的ADC可配置4個注入通道來擴展轉(zhuǎn)換通道,那么可進行轉(zhuǎn)換的通道最多可達20個。
    的頭像 發(fā)表于 06-01 17:25 ?1784次閱讀
    <b class='flag-5'>MM32F5270</b>平臺ADC注入通道的單周期采樣的<b class='flag-5'>實現(xiàn)</b>

    MM32F5270(STAR-MC1內(nèi)核)RT-Thread完整版的移植教程

    基本把rtt移植mm32f5270中。移植驗證在main.c中main函數(shù)添加點燈的代碼。如果能夠看到led在閃爍,就說明移植成功了?,F(xiàn)象shell適配適配uart驅(qū)動
    發(fā)表于 12-23 17:17

    基于ARM的LwIP協(xié)議研究與移植

    提出基于ARM的LwIP協(xié)議研究與移植
    發(fā)表于 10-14 17:50 ?65次下載
    基于ARM的<b class='flag-5'>LwIP</b><b class='flag-5'>協(xié)議</b><b class='flag-5'>棧</b>研究與<b class='flag-5'>移植</b>

    MM32F5270總線架構設計

    本文介紹了MM32F5270 中所采用的多并發(fā)總線架構,并通過帶顯示的音頻播放器的實例說明了該架構在實際應用中所能達到的吞吐率提升效果。
    的頭像 發(fā)表于 05-11 11:15 ?2412次閱讀
    <b class='flag-5'>MM32F5270</b>總線架構設計

    基于MM32F5270控制器的I2S音頻播放

    MM32F5270 系列控制器支持 I2S 總線接口,本章節(jié)在接下來會對 MM32F5270 I2S進行介紹,并使用 MM32F5270 和 CS4344 芯片進行 I2S 通信來演示播放一段聲音。
    的頭像 發(fā)表于 09-16 10:39 ?2812次閱讀

    MM32F5270】Keil開發(fā)環(huán)境搭建

    本文是對MM32F5270相關的靈動官網(wǎng)資料和社區(qū)現(xiàn)有幾篇環(huán)境搭建帖的整理和總結。詳細且完整的記錄了——如何從零搭建MM32F5270 Keil開發(fā)環(huán)境以及如何編譯運行MM32F5270 SDK中
    的頭像 發(fā)表于 11-06 16:14 ?3620次閱讀
    【<b class='flag-5'>MM32F5270</b>】Keil開發(fā)環(huán)境搭建

    MM32F5270 產(chǎn)品手冊(中文版)

    MM32F5270 產(chǎn)品手冊(中文版)
    發(fā)表于 02-23 18:45 ?0次下載
    <b class='flag-5'>MM32F5270</b> 產(chǎn)品手冊(中文版)

    MM32F5270 產(chǎn)品手冊(英文版)

    MM32F5270 產(chǎn)品手冊(英文版)
    發(fā)表于 02-23 18:45 ?0次下載
    <b class='flag-5'>MM32F5270</b> 產(chǎn)品手冊(英文版)

    MM32F5270 用戶手冊(中文版)

    MM32F5270 用戶手冊(中文版)
    發(fā)表于 02-23 18:46 ?0次下載
    <b class='flag-5'>MM32F5270</b> 用戶手冊(中文版)

    MM32F5270 用戶手冊(英文版)

    MM32F5270 用戶手冊(英文版)
    發(fā)表于 02-23 18:46 ?0次下載
    <b class='flag-5'>MM32F5270</b> 用戶手冊(英文版)

    MM32F5270 勘誤表(中文版)

    MM32F5270 勘誤表(中文版)
    發(fā)表于 02-23 18:47 ?0次下載
    <b class='flag-5'>MM32F5270</b> 勘誤表(中文版)

    MM32F5270 勘誤表(英文版)

    MM32F5270 勘誤表(英文版)
    發(fā)表于 02-23 18:48 ?0次下載
    <b class='flag-5'>MM32F5270</b> 勘誤表(英文版)

    MM32F5270平臺ADC注入通道的單周期采樣的實現(xiàn)

    MM32F5270的ADC可配置4個注入通道來擴展轉(zhuǎn)換通道,那么可進行轉(zhuǎn)換的通道最多可達20個。
    的頭像 發(fā)表于 05-26 09:31 ?956次閱讀
    <b class='flag-5'>MM32F5270</b>平臺ADC注入通道的單周期采樣的<b class='flag-5'>實現(xiàn)</b>

    MM32F5270平臺ADC注入通道的單周期采樣的實現(xiàn)

    MM32F5270的ADC可配置4個注入通道來擴展轉(zhuǎn)換通道,那么可進行轉(zhuǎn)換的通道最多可達20個。
    的頭像 發(fā)表于 05-26 09:32 ?1163次閱讀
    <b class='flag-5'>MM32F5270</b>平臺ADC注入通道的單周期采樣的<b class='flag-5'>實現(xiàn)</b>

    基于MM32F5270的I2S音頻播放

    基于MM32F5270的I2S音頻播放
    的頭像 發(fā)表于 10-30 17:13 ?1762次閱讀
    基于<b class='flag-5'>MM32F5270</b>的I2S音頻播放