1.LWIP協議棧內存管理
1.1 內存管理需求
內存管理需求分為兩類:
- 常用內存管理需求:靜態分配的變量(RAM),任務堆棧,動態存儲器管理malloc/free
- LWIP內存管理需求:協議棧各層封裝的數據
1.2 內存管理方案
LWIP內存管理有兩種方案:堆(heap)和池(pool)
堆:堆內存管理機制會根據需要分配的內存大小在空閑的內存塊中找到最佳擬合(best fit)的內存區域
LWIP內存堆管理API函數:
//內存堆初始化
void mem_init(void);
//內存堆分配內存
void *mem_malloc(mem_size_t size);
//內存堆釋放內存
void mem_free(void *mem);
池:池內存管理機制將內存分配成多個大小不一的內存池,每個內存池中又被分為N個相同大小的內存塊。 程序可根據需要使用的內存大小直接到不同的內存池中取用即可。 池內存管理機制分配內存更快,效率更高
LWIP內存池管理API函數:
//內存池初始化
void memp_init(void);
//內存池分配
void *memp_malloc(memp_t type);
//內存池釋放
void memp_free(memp_t type, void *mem);
1.3 網絡數據包管理
pbuf就是一個描述協議棧中數據包的數據結構,LWIP 中在 pbuf.c和 pubf.h實現了協議棧數據包管理的所有函數與數據結構
pbuf數據結構
pbuf結構體
//在pbuf.h中定義
struct pbuf {
/** 指向下一個pbuf結構體,每個pbuf存放的數據有限,若應用有大量的數據的話
就需要多個pbuf來存放,可以將同一個數據包的pbuf連接在一起構成一個鏈表 */
struct pbuf *next;
/** 指向該pbuf真正的數據存儲區的首地址。STM32F4內部網絡模塊收到數據,并
將數據提交給LWIP時,就是將數據存儲在payload指定的存儲區中;同樣在發送數
據的時候將payload指向的存儲區數據轉給STM32F4的網絡模塊去發送 */
void *payload;
/** 在接收或發送數據的時候數據會存放在pbuf鏈表中,tot_Len表示當前pbuf和鏈
表中后面所有pbuf的數據長度,它們屬于一個數據包 */
u16_t tot_len;
/** 當前pbuf總數據的長度 */
u16_t len;
/** 當前pbuf類型,共有四種:PBUF_RAM/PBUF_ROM/PBUF_REF/PBUF_POOL */
u8_t type;
/** 保留位 */
u8_t flags;
/** 該pbuf被引用的次數,當還有其他指針指向這個pbuf的時候ref字段就加一 */
u16_t ref;
};
pbuf類型:共有四種PBUF_RAM、PBUF_ROM、PBUF_REF、PBUF_POOL
//在pbuf.h中定義
typedef enum {
/** PBUF_RAM類型的pbuf是通過內存堆分配來的,其payload并未指向數據區的起始
地址,而是隔了一段區域,在這個區域(offset)里通常存放TCP報文首部、IP首部
、以太網幀首部等等 */
PBUF_RAM,
/** PBUF_ROM的數據指針payload指向外部存儲區,外部存儲區指不由TCP/IP協議
棧管理的存儲區,它可以是應用程序管理的存儲器為用戶數據分配的緩存,也可以是
ROM區域,如靜態網頁中的字符串常量等 */
PBUF_ROM,
/** PBUF_REF和PBUF_ROM的特性非常相似,都可以實現數據的零拷貝 */
PBUF_REF,
/** PBUF_POOL類型的pbuf是通過內存池分類來的,pbuf鏈表的第一個pbuf的payload
未指向數據區的起始位置,原因通PBUF_RAM一樣,用來存放一些首部,pbuf鏈表后面
的pbuf結構體中的payload就指向了數據區的起始位置 */
PBUF_POOL
} pbuf_type;
pbuf層:由于LWIP各層禁止數據拷貝,所以存在不同層次對數據包pbuf的alloc,前面的offest就是為不同層預留的頭部字段,下面枚舉了4種層次,分配時除了要知道大小、類型還要傳入這個層次
//在pbuf.h中定義
typedef enum {
/** 傳輸層,預留以太首部+IP首部+TCP首部 */
PBUF_TRANSPORT,
/** 網絡層,預留以太首部+IP首部 */
PBUF_IP,
/** 鏈路層,預留以太首部 */
PBUF_LINK,
/** 原始層,不預留空間 */
PBUF_RAW_TX,
/** Use this for input packets in a netif driver when calling netif->input()
* in the most common case - ethernet-layer netif driver. */
PBUF_RAW
} pbuf_layer;
pbuf_alloc:內存申請函數
struct pbuf *pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type);
//layer 申請的pbuf所在的層,以此來確定offset值
//length 存放數據的大小
//type pbuf的類型
pbuf_free:數據包釋放函數
u8_t pbuf_free(struct pbuf *p);
//p 要釋放的pbuf數據包
pbuf_realloc:調整收縮pbuf的大小,在相應pbuf(鏈表)尾部釋放一定的空間,將數據包pbuf中的數據長度減少為某個長度值
void pbuf_realloc(struct pbuf *p, u16_t new_len);
//p 要收縮的pbuf數據包
//new_len 新的長度值
pbuf_header:調整payload指針和長度字段以便為pbuf中的數據預置包頭,常用于實現對pbuf預留孔間的操作
u8_t pbuf_header(struct pbuf *p, s16_t header_size_increment);
//p 要操作的pbuf數據包
//header_size_increment 大于0,payload前移,數據傳遞下層;
// 小于0,表示payload后移,數據傳遞上層
pbuf_take:用于向pbuf的數據區域拷貝數據
err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
//buf 要填充數據的pbuf
//dataptr 應用程序中的數據緩沖區
//len 數據緩沖區的長度
2.LWIP網卡設計與實現
2.1 LWIP網絡接口管理
在LWIP中對于網絡接口的描述是通過一個netif結構體完成的,netif結構體在netif.h文件中有定義
netif結構體
//在netif.h中定義
struct netif {
struct netif *next; //指向下一個netif結構體
#if LWIP_IPV4
ip_addr_t ip_addr; //ip地址
ip_addr_t netmask; //子網掩碼
ip_addr_t gw; //網關地址
#endif /* LWIP_IPV4 */
netif_input_fn input; //netif數據包輸入接口函數指針
#if LWIP_IPV4
netif_output_fn output;//netif數據包輸出接口函數指針
#endif /* LWIP_IPV4 */
netif_linkoutput_fn linkoutput;//鏈路層數據輸出接口函數指針
#if LWIP_NETIF_STATUS_CALLBACK
//當netif狀態發生變化時,此接口函數會調用
netif_status_callback_fn status_callback;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
#if LWIP_NETIF_LINK_CALLBACK
/* PHY必須和交換機或者路由器或者其他具備網卡的主機相連接,
我們才可能正常通信比如路由器突然斷電,這個函數就會被調用 */
netif_status_callback_fn link_callback;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_NETIF_REMOVE_CALLBACK
//netif移除網絡驅動接口,這個函數會被調用
netif_status_callback_fn remove_callback;
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
void *state; //主機的狀態
#if LWIP_NETIF_HOSTNAME
const char* hostname; //自定義的主機名稱
#endif /* LWIP_NETIF_HOSTNAME */
#if LWIP_CHECKSUM_CTRL_PER_NETIF
u16_t chksum_flags;
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
u16_t mtu; //數據鏈路層最大傳輸大小
u8_t hwaddr_len; //mac地址長度
u8_t hwaddr[NETIF_MAX_HWADDR_LEN];//mac地址
u8_t flags; //當前的netif的狀態,其實就是下面的netif_flag
char name[2]; //網卡驅動的名稱
u8_t num; //網卡驅動的硬件編號
#if LWIP_IPV4 && LWIP_IGMP
netif_igmp_mac_filter_fn igmp_mac_filter;//組播底層接口
#endif /* LWIP_IPV4 && LWIP_IGMP */
};
netif flag宏定義
/** netif網絡接口,可以進行正常使用(即lwIP可以正常使用)了 */
#define NETIF_FLAG_UP 0x01U
/** 廣播通訊的標志 */
#define NETIF_FLAG_BROADCAST 0x02U
/** STM32 MAC和PHY可以正常使用 */
#define NETIF_FLAG_LINK_UP 0x04U
/** ARP標志 */
#define NETIF_FLAG_ETHARP 0x08U
/** TCP/IP協議正常通信 */
#define NETIF_FLAG_ETHERNET 0x10U
2.2 netif典型API函數
netif的API函數是供應用層調用的函數
netif_add:添加網卡驅動到lwip
struct netif *netif_add(struct netif *netif,
const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
void *state,
netif_init_fn init,
netif_input_fn input);
netif_set_default:把網卡恢復出廠設置,目前lwip有一套默認參數
void netif_set_default(struct netif *netif);
netif_set_up&netif_set_down:設置我們網卡工作狀態,是上線還是離線
void netif_set_up(struct netif *netif);
void netif_set_down(struct netif *netif);
callback:需要自己實現link_callback函數
#if LWIP_NETIF_LINK_CALLBACK
void netif_set_link_callback(struct netif *netif,
netif_status_callback_fn link_callback);
#endif /* LWIP_NETIF_LINK_CALLBACK */
2.3 netif底層接口函數
netif底層接口即與硬件打交道的函數接口,主要包括以下函數
ethernetif_init:初始化網卡驅動(會調用底層驅動)
err_t ethernetif_init(struct netif *netif){
#if LWIP_IPV4
#if LWIP_ARP || LWIP_ETHERNET
//arp相關的函數接口賦值
#if LWIP_ARP
netif->output = etharp_output;
#else
netif->output = low_level_output_arp_off;
#endif /* LWIP_ARP */
#endif /* LWIP_ARP || LWIP_ETHERNET */
#endif /* LWIP_IPV4 */
//鏈路層數據輸出函數接口賦值
netif->linkoutput = low_level_output;
/* 底層接口初始化 */
low_level_init(netif);
return ERR_OK;
}
ethernetif_input:網卡數據輸入(會調用底層接口)
void ethernetif_input(void const * argument){
struct pbuf *p;
struct netif *netif = (struct netif *) argument;
for( ;; )
{
if (osSemaphoreWait(s_xSemaphore, TIME_WAITING_FOR_INPUT) == osOK)
{
do
{
p = low_level_input( netif );
if (p != NULL)
{
if (netif->input( p, netif) != ERR_OK )
{
pbuf_free(p);
}
}
} while(p!=NULL);
}
}
}
low_level_init:網卡底層驅動,主要針對硬件(STM32網卡初始化會在此調用)
/** 硬件初始化,其實就STM32 ETH外設初始化 */
static void low_level_init(struct netif *netif){
uint32_t regvalue = 0;
HAL_StatusTypeDef hal_eth_init_status;
/* Init ETH */
uint8_t MACAddr[6] ;
heth.Instance = ETH;
heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
heth.Init.PhyAddress = DP83848_PHY_ADDRESS;
MACAddr[0] = 0x00;
MACAddr[1] = 0x80;
MACAddr[2] = 0xE1;
MACAddr[3] = 0x00;
MACAddr[4] = 0x00;
MACAddr[5] = 0x00;
heth.Init.MACAddr = &MACAddr[0];
heth.Init.RxMode = ETH_RXINTERRUPT_MODE;
heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
hal_eth_init_status = HAL_ETH_Init(&heth);
if (hal_eth_init_status == HAL_OK)
{
/* 當初始化成功后,會置位flag,同時在tcp/ip初始化完畢后,會
進行判斷,此標志位決定網卡驅動是否 可以正常使用 */
netif->flags |= NETIF_FLAG_LINK_UP;
}
/* Initialize Tx Descriptors list: Chain Mode */
HAL_ETH_DMATxDescListInit(&heth, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
/* Initialize Rx Descriptors list: Chain Mode */
HAL_ETH_DMARxDescListInit(&heth, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);
#if LWIP_ARP || LWIP_ETHERNET
/* MAC地址初始化 */
netif->hwaddr_len = ETH_HWADDR_LEN;
netif->hwaddr[0] = heth.Init.MACAddr[0];
netif->hwaddr[1] = heth.Init.MACAddr[1];
netif->hwaddr[2] = heth.Init.MACAddr[2];
netif->hwaddr[3] = heth.Init.MACAddr[3];
netif->hwaddr[4] = heth.Init.MACAddr[4];
netif->hwaddr[5] = heth.Init.MACAddr[5];
/* maximum transfer unit */
netif->mtu = 1500;
/* Accept broadcast address and ARP traffic */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
#if LWIP_ARP
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
#else
netif->flags |= NETIF_FLAG_BROADCAST;
#endif /* LWIP_ARP */
/* 二值信號量,用于信息同步,當網卡接口到數據后,會釋放二值信號量讓其他任務進行解析 */
osSemaphoreDef(SEM);
s_xSemaphore = osSemaphoreCreate(osSemaphore(SEM), 1);
/* 創建網卡數據接收解析任務-ethernetif_input */
osThreadDef(EthIf, ethernetif_input, osPriorityRealtime, 0, INTERFACE_THREAD_STACK_SIZE);
osThreadCreate (osThread(EthIf), netif);
/* 使能 網卡 發送和接口 */
HAL_ETH_Start(&heth);
/* 上面的都是針對STM32 ETH外設進行初始化
但是實際網絡交互是用過PHY,下面就是初始化PHY */
/* Read Register Configuration */
HAL_ETH_ReadPHYRegister(&heth, PHY_MICR, ®value);
regvalue |= (PHY_MICR_INT_EN | PHY_MICR_INT_OE);
/* Enable Interrupts */
HAL_ETH_WritePHYRegister(&heth, PHY_MICR, regvalue );
/* Read Register Configuration */
HAL_ETH_ReadPHYRegister(&heth, PHY_MISR, ®value);
regvalue |= PHY_MISR_LINK_INT_EN;
/* Enable Interrupt on change of link status */
HAL_ETH_WritePHYRegister(&heth, PHY_MISR, regvalue);
#endif /* LWIP_ARP || LWIP_ETHERNET */
}
low_level_output:底層網卡的數據輸出,實際的數據輸出,是通過pbuf進行封裝管理的
static err_t low_level_output(struct netif *netif, struct pbuf *p){
err_t errval;
struct pbuf *q;
uint8_t *buffer = (uint8_t *)(heth.TxDesc->Buffer1Addr);
__IO ETH_DMADescTypeDef *DmaTxDesc;
uint32_t framelength = 0;
uint32_t bufferoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t payloadoffset = 0;
DmaTxDesc = heth.TxDesc;
bufferoffset = 0;
/* copy frame from pbufs to driver buffers */
for(q = p; q != NULL; q = q->next)
{
/* Is this buffer available? If not, goto error */
if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
{
errval = ERR_USE;
goto error;
}
/* Get bytes in current lwIP buffer */
byteslefttocopy = q->len;
payloadoffset = 0;
/* Check if the length of data to copy is bigger than Tx buffer size*/
while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE )
{
/* Copy data to Tx buffer*/
memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) );
/* Point to next descriptor */
DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
/* Check if the buffer is available */
if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
{
errval = ERR_USE;
goto error;
}
buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
/* Copy the remaining bytes */
memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), byteslefttocopy );
bufferoffset = bufferoffset + byteslefttocopy;
framelength = framelength + byteslefttocopy;
}
/* 把pbuf里面的數據,發送到ETH外設里面 */
HAL_ETH_TransmitFrame(&heth, framelength);
errval = ERR_OK;
error:
/* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
if ((heth.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET)
{
/* Clear TUS ETHERNET DMA flag */
heth.Instance->DMASR = ETH_DMASR_TUS;
/* Resume DMA transmission*/
heth.Instance->DMATPDR = 0;
}
return errval;
}
low_level_input:底層網卡的數據接口,當接收到網卡數據后,會通過此函數,封裝為pbuf提供上層使用
static struct pbuf * low_level_input(struct netif *netif){
struct pbuf *p = NULL;
struct pbuf *q = NULL;
uint16_t len = 0;
uint8_t *buffer;
__IO ETH_DMADescTypeDef *dmarxdesc;
uint32_t bufferoffset = 0;
uint32_t payloadoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t i=0;
/* 通過HAL庫,獲取網卡幀數據 */
if (HAL_ETH_GetReceivedFrame_IT(&heth) != HAL_OK)
return NULL;
/* 獲取網卡數據超度,及內存地址 */
len = heth.RxFrameInfos.length;
buffer = (uint8_t *)heth.RxFrameInfos.buffer;
//網卡中數據有效
if (len > 0){
/* 網卡數據不能大于1500,屬于原始層接口 */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
}
//如果pbuf創建成功,則從ETH中拷貝數據到pbuf里,最終把pbuf返回給上層應用
if (p != NULL){
dmarxdesc = heth.RxFrameInfos.FSRxDesc;
bufferoffset = 0;
for(q = p; q != NULL; q = q->next){
byteslefttocopy = q->len;
payloadoffset = 0;
/* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE ){
/* Copy data to pbuf */
memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));
/* Point to next descriptor */
dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
buffer = (uint8_t *)(dmarxdesc->Buffer1Addr);
byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
/* Copy remaining data in pbuf */
memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy);
bufferoffset = bufferoffset + byteslefttocopy;
}
}
/* Release descriptors to DMA */
/* Point to first descriptor */
dmarxdesc = heth.RxFrameInfos.FSRxDesc;
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
for (i=0; i< heth.RxFrameInfos.SegCount; i++){
dmarxdesc->Status |= ETH_DMARXDESC_OWN;
dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
}
/* Clear Segment_Count */
heth.RxFrameInfos.SegCount =0;
/* When Rx Buffer unavailable flag is set: clear it and resume reception */
if ((heth.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) {
/* Clear RBUS ETHERNET DMA flag */
heth.Instance->DMASR = ETH_DMASR_RBUS;
/* Resume DMA reception */
heth.Instance->DMARPDR = 0;
}
return p;
}
tcpip_init:工作在操作系統下的初始化
/**
* @工作在操作系統下的初始化:
* - 初始化所有的子功能模塊
* - 啟動tcp/ip任務(tcp/ip網絡協議棧的實現是一個任務里面執行的)
* @param 用于用戶初始化的函數指針,在lwip初始化完成,tcp/ip任務開始執行就是進行調用
* @param 用戶初始化相關參數傳入
*/
void tcpip_init(tcpip_init_done_fn initfunc, void *arg){
//lwip的初始化---初始化lwip所有功能模塊
lwip_init();
//用戶初始化函數指針賦值,參數賦值
tcpip_init_done = initfunc;
tcpip_init_done_arg = arg;
//消息郵箱(freeRTOS是通過消息隊列實現),任務與任務間消息通信,網卡收到數據,網絡分層解析
if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
}
#if LWIP_TCPIP_CORE_LOCKING
//創建互斥鎖(互斥信號量),保護共享資源的
if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
LWIP_ASSERT("failed to create lock_tcpip_core", 0);
}
#endif /* LWIP_TCPIP_CORE_LOCKING */
//這是標準的cmis接口,其實內部調用的freeRTOS的創建任務接口
sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}
lwip_init:工作在裸機模式下的初始化
/**
* @工作在裸機模式下的初始化
* Use this in NO_SYS mode. Use tcpip_init() otherwise.
*/
void lwip_init(void){
/* Modules initialization */
//狀態初始化
stats_init();
#if !NO_SYS
//與操作系統相關的初始化
sys_init();
#endif /* !NO_SYS */
//內存堆 內存池 pbuf netif初始化
mem_init();
memp_init();
pbuf_init();
netif_init();
#if LWIP_IPV4
//ip層初始化
ip_init();
#if LWIP_ARP
//arp+以太網相關的初始化
etharp_init();
#endif /* LWIP_ARP */
#endif /* LWIP_IPV4 */
#if LWIP_RAW
//原生接口初始化
raw_init();
#endif /* LWIP_RAW */
#if LWIP_UDP
udp_init();
#endif /* LWIP_UDP */
#if LWIP_TCP
tcp_init();
#endif /* LWIP_TCP */
#if LWIP_IGMP
igmp_init();
#endif /* LWIP_IGMP */
#if LWIP_DNS
dns_init();
#endif /* LWIP_DNS */
#if PPP_SUPPORT
ppp_init();
#endif
#if LWIP_TIMERS
//lwip內部有很多超時機制,是通過timeouts實現的(一個軟件定時器)
sys_timeouts_init();
#endif /* LWIP_TIMERS */
}
MX_LWIP_Init:HAL庫實現的lwip初始化函數
void MX_LWIP_Init(void){
/* IP 地址初始化 */
IP_ADDRESS[0] = 192;
IP_ADDRESS[1] = 168;
IP_ADDRESS[2] = 1;
IP_ADDRESS[3] = 10;
NETMASK_ADDRESS[0] = 255;
NETMASK_ADDRESS[1] = 255;
NETMASK_ADDRESS[2] = 255;
NETMASK_ADDRESS[3] = 0;
GATEWAY_ADDRESS[0] = 192;
GATEWAY_ADDRESS[1] = 168;
GATEWAY_ADDRESS[2] = 1;
GATEWAY_ADDRESS[3] = 1;
/* 初始化lwip協議棧 */
tcpip_init( NULL, NULL );
/* 數組格式的IP地址轉換為lwip格式的地址 */
IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);
/* 裝載網卡驅動,并初始化網卡 */
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
/* gnetif注冊為默認網卡驅動 */
netif_set_default(&gnetif);
// 判斷phy和mac層是否正常工作
if (netif_is_link_up(&gnetif)){
/* netif 網卡驅動可以正常使用,上線 */
netif_set_up(&gnetif);
}
else{
/* netif 網卡驅動下線 */
netif_set_down(&gnetif);
}
}
-
存儲器
+關注
關注
38文章
7514瀏覽量
164017 -
網卡
+關注
關注
4文章
312瀏覽量
27405 -
內存管理
+關注
關注
0文章
168瀏覽量
14161 -
LwIP
+關注
關注
2文章
87瀏覽量
27235 -
協議棧
+關注
關注
2文章
143瀏覽量
33665
發布評論請先 登錄
相關推薦
評論