11.物联网lwip,网卡原理

一。LWIP协议栈内存管理

1.LWIP内存管理方案

(1)堆heap

1.灰色为已使用内存

2.黑色为未使用内存

3.紫色为使用后内存

按照某种算法,把数据放在内存块中

(2)池pool

设置内存池,设置成大小相同的内存块。

2.LWIP内存管理

(1)内存池API

//内存池初始化

void memp_init(void);

//内存池分配

void *memp_malloc(memp_t type);

//内存池释放

void memp_free(memp_t type, void *mem);

(2)内存堆API

//内存堆初始化

void mem_init(void);

//内存堆分配内存

void *mem_malloc(mem_size_t size);

//内存堆释放内存

void mem_free(void *mem);

3.网络数据包的管理

(1)pbuf解释

1.pbuf就是一个描述协议栈中数据包的数据结构,LwIP 中在 pbuf.c和 pubf.h实现了协议栈数据包管理的所有函数与数据结构

2.pbuf结构体

struct pbuf{

//指向下一跳

struct pbuf *next;

//指向实际数据存放地址

void* payload;

//total全部,表示全部的长度

// p->tot_len == p->len + (p->next? p->next->tot_len: 0)获取长度

u16_t tot_len;

//本pbuf的长度

u16_t len;

//选择样式,因为存储TCP数据,UDP数据,数据链路数据存储所需大小是不一样

u8_t type;

//标识

u8_t flags;

//引用计数总是等于指针的数目

*指的是这个函数。这可以是来自应用程序的指针,

*堆栈本身,或者pbuf->链中的next指针。

u16_t ref;

}
pbuf类型--》选择不同类型,使用不同的物理结构存储,对数据处理更加高效

//pbuf.h

typedef enum {

PBUF_RAM,

PBUF_ROM,

PBUF_REF,

PBUF_POOL

} pbuf_type;


pbuf层--》选此类型是对不同报文的区分,比如PBUF_TRANSPORT传输层数据,PBUF_IP网络层数据,PBUF_LINK链路层数据,PBUF_RAW_TX物理层数据。

//pbuf.h

typedef enum {

PBUF_TRANSPORT,

PBUF_IP,

PBUF_LINK,

PBUF_RAW_TX,

PBUF_RAW

} pbuf_layer;
pbuf的申请与释放

1.申请--》使用上述的两个结构体

struct pbuf *pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type);

2.释放

u8_t pbuf_free(struct pbuf *p);

3.收缩链路的长度

void pbuf_realloc(struct pbuf *p, u16_t new_len);

4.调整有效负载指针以隐藏或显示有效负载中的标头。

u8_t pbuf_header(struct pbuf *p, s16_t header_size_increment);

5.将应用程序提供的数据复制到pbuf中。

err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);

二。netif相关结构体

1.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.netif结构体

//netif.h

struct netif {

/** 链表指针 */

struct netif *next;

#if LWIP_IPV4

/**

ip地址

子网掩码

网关地址

*/

ip_addr_t ip_addr;

ip_addr_t netmask;

ip_addr_t gw;

#endif /* LWIP_IPV4 */

/**

netif 数据包输入接口函数指针

*/

netif_input_fn input;

#if LWIP_IPV4

/**

netif 数据包输出接口函数指针

*/

netif_output_fn output;

#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;

/**

mac地址长度

*/

u8_t hwaddr_len;

/**

mac地址

*/

u8_t hwaddrNETIF_MAX_HWADDR_LEN;

/**

当前的netif的状态,其实就是上面的netif_flag

*/

u8_t flags;

/**

网卡驱动的名称

*/

char name2;

/**

网卡驱动的硬件编号

*/

u8_t num;

#if LWIP_IPV4 && LWIP_IGMP

/**

组播底层接口

*/

netif_igmp_mac_filter_fn igmp_mac_filter;

#endif /* LWIP_IPV4 && LWIP_IGMP */

};

2.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 */

3.netif 底层接口(跟硬件打交道)

/**

初始化 网卡驱动(会调用底层驱动)

*/

err_t ethernetif_init(struct netif *netif);
/**

网卡数据输入(会调用底层接口)

*/

void ethernetif_input(void const * argument);
/**

网卡底层驱动,主要针对硬件(STM32网卡初始化会在此调用)

*/

static void low_level_init(struct netif *netif);
/**

底层网卡的数据输出,实际的数据输出,是通过pbuf进行封装管理的

*/

static err_t low_level_output(struct netif *netif, struct pbuf *p);
/**

底层网卡的数据接口,当接收到网卡数据后,会通过此函数,封装为pbuf提供上层使用

*/

static struct pbuf * low_level_input(struct netif *netif);

三。LWIP网卡设计

1.tcpip_init

/**

* @工作在操作系统下的

* Initialize this module:

* - 初始化所有的子功能模块

* - 启动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);

}

2.补充:lwip_init这是在裸机下的初始化

/**

* @ingroup 工作在裸机模式

* Initialize all modules.

* 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 */

}

3.HAL库实现lwip的初始化

/**

* HAL库实现的lwip初始化函数

*/

void MX_LWIP_Init(void)

{

/* IP 地址初始化 */

IP_ADDRESS0 = 192;

IP_ADDRESS1 = 168;

IP_ADDRESS2 = 1;

IP_ADDRESS3 = 10;

NETMASK_ADDRESS0 = 255;

NETMASK_ADDRESS1 = 255;

NETMASK_ADDRESS2 = 255;

NETMASK_ADDRESS3 = 0;

GATEWAY_ADDRESS0 = 192;

GATEWAY_ADDRESS1 = 168;

GATEWAY_ADDRESS2 = 1;

GATEWAY_ADDRESS3 = 1;

/* 初始化lwip协议栈 */

tcpip_init( NULL, NULL );

/*

数组格式的IP地址转换为lwip格式的地址

*/

IP4_ADDR(&ipaddr, IP_ADDRESS0, IP_ADDRESS1, IP_ADDRESS2, IP_ADDRESS3);

IP4_ADDR(&netmask, NETMASK_ADDRESS0, NETMASK_ADDRESS1 , NETMASK_ADDRESS2, NETMASK_ADDRESS3);

IP4_ADDR(&gw, GATEWAY_ADDRESS0, GATEWAY_ADDRESS1, GATEWAY_ADDRESS2, GATEWAY_ADDRESS3);

/*

装载网卡驱动,并初始化网卡

*/

netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_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);

}

/* USER CODE BEGIN 3 */

/* USER CODE END 3 */

}

4.以太网的初始化 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;

}

low_level_init

/**

硬件初始化,其实就STM32 ETH外设初始化

*/

static void low_level_init(struct netif *netif)

{

uint32_t regvalue = 0;

HAL_StatusTypeDef hal_eth_init_status;

/* Init ETH */

uint8_t MACAddr6 ;

heth.Instance = ETH;

heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;

heth.Init.PhyAddress = DP83848_PHY_ADDRESS;

MACAddr0 = 0x00;

MACAddr1 = 0x80;

MACAddr2 = 0xE1;

MACAddr3 = 0x00;

MACAddr4 = 0x00;

MACAddr5 = 0x00;

heth.Init.MACAddr = &MACAddr0;

heth.Init.RxMode = ETH_RXINTERRUPT_MODE;

heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;

heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;

/* USER CODE BEGIN MACADDRESS */

/* USER CODE END MACADDRESS */

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_Buff00, ETH_TXBUFNB);

/* Initialize Rx Descriptors list: Chain Mode */

HAL_ETH_DMARxDescListInit(&heth, DMARxDscrTab, &Rx_Buff00, ETH_RXBUFNB);

#if LWIP_ARP || LWIP_ETHERNET

/*

MAC地址初始化

*/

netif->hwaddr_len = ETH_HWADDR_LEN;

netif->hwaddr0 = heth.Init.MACAddr0;

netif->hwaddr1 = heth.Init.MACAddr1;

netif->hwaddr2 = heth.Init.MACAddr2;

netif->hwaddr3 = heth.Init.MACAddr3;

netif->hwaddr4 = heth.Init.MACAddr4;

netif->hwaddr5 = heth.Init.MACAddr5;

/* 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, &regvalue);

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, &regvalue);

regvalue |= PHY_MISR_LINK_INT_EN;

/* Enable Interrupt on change of link status */

HAL_ETH_WritePHYRegister(&heth, PHY_MISR, regvalue);

/* USER CODE BEGIN PHY_POST_CONFIG */

/* USER CODE END PHY_POST_CONFIG */

#endif /* LWIP_ARP || LWIP_ETHERNET */

/* USER CODE BEGIN LOW_LEVEL_INIT */

/* USER CODE END LOW_LEVEL_INIT */

}

底层数据收发

HAL_ETH_RxCpltCallback

/**

* @brief Ethernet Rx Transfer completed callback

* @param heth: ETH handle

* @retval None

*/

void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)

{

osSemaphoreRelease(s_xSemaphore);

}

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_input

/**

*/

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;

}

low_level_output

/**

*/

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;

}

解释:lwip的移植与裁剪

1.移植文件的存放地

(1)打开工程文件,进入根目录下

(2)middlewares文件夹下就是移植所需要的文件,有下图可知有Freertos与lwip

(3)这里主要看LWIP的移植,src为经常使用的.c与.h文件,system即为移植文件存方地。

2.移植步骤

(1)网卡驱动 ETH以太网接口

<1>lwip

<2>ethernetif

(2)操作系统 Freertos配置

<1>sys.arch.h

<2>sys.arch.c

(3)配置选项

<1>lwipopt 常用的宏定义放在这里

<2>opt 规定的宏定义存放

相关推荐
久爱物联网3 小时前
个人站点(博客)开发案例 - 功能齐全,在线客服-即时通讯-物联网控制、广告位管理、按钮级权限控制
物联网·个人博客·即时通讯在线客服·物联网控制·博客发布系统·新闻发布系统
兆龙电子单片机设计4 小时前
【STM32项目开源】STM32单片机语音分类垃圾桶-蓝牙APP
stm32·单片机·嵌入式硬件·物联网·开源·自动化·毕业设计
厦门辰迈智慧科技5 小时前
管网遥测终端机赋能城市安全的核心价值
物联网·遥测终端机·管网遥测终端机·排水管网遥测终端·管网遥测终端
七仔啊6 小时前
modbus快速入门
物联网
论迹复利12 小时前
PSA Certified 是什么——物联网芯片安全认证的三级体系
物联网·struts·安全
MetrixAeroCore13 小时前
AI玩具出海用物联网卡还是eSIM?200国漫游和本地落地卡成本差多少?
物联网
数智顾问17 小时前
(84页PPT)PWC海科集团生物医药业务5年发展战略项目建议书(附下载方式)
大数据·人工智能·物联网
MetrixAeroCore18 小时前
智能快递柜格口监控物联网卡在老旧小区弱网环境下多运营商切换组网方案
物联网
腻害兔18 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro IoT 物联网模块:从协议网关到物模型,一文讲透开源 IoT 平台架构
前端·vue.js·物联网·产品经理·ai编程
北京晶数信息科技18 小时前
加油站成品油智慧监管系统项目建设实施汇报(一)
大数据·物联网·产品经理·需求分析