django网站开发案例,销售网站的优秀标准,怎么用云校建设学校网站,软件工程女生的悲哀一。LWIP协议栈内存管理
1.LWIP内存管理方案
#xff08;1#xff09;堆heap 1.灰色为已使用内存 2.黑色为未使用内存 3.紫色为使用后内存 按照某种算法#xff0c;把数据放在内存块中 #xff08;2#xff09;池pool 设置内存池#xff0c;设置成大小相同的内存块。 2…一。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.网络数据包的管理
1pbuf解释 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 hwaddr[NETIF_MAX_HWADDR_LEN]; /** 当前的netif的状态其实就是上面的netif_flag */ u8_t flags; /** 网卡驱动的名称 */ char name[2]; /** 网卡驱动的硬件编号 */ 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_upnetif_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_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, 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 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; /* 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_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 */ /* dont 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 i0; /* 通过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 (i0; 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打开工程文件进入根目录下 2middlewares文件夹下就是移植所需要的文件有下图可知有Freertos与lwip 3这里主要看LWIP的移植src为经常使用的.c与.h文件system即为移植文件存方地。 2.移植步骤
1网卡驱动 ETH以太网接口
1lwip
2ethernetif
2操作系统 Freertos配置
1sys.arch.h
2sys.arch.c
3配置选项
1lwipopt 常用的宏定义放在这里
2opt 规定的宏定义存放