# BabyOS增加第三方库LWIP MCU项目中网络通讯是比较常用的功能,BabyOS选择增加LWIP为用户节省移植网络协议栈的时间,以及后续提供统一的网络操作接口。 目前硬件结构大概有两种: ![](../_static/eth_hard_struct.jpg) 网络数据流向: ![](../_static/eth_data_path.jpg) 整体来看,就是需要给LWIP提供以太网数据的发送接口,以及将收到的网络数据喂给LWIP。 BabyOS新增软件模块 b_mod_netif 用于LWIP与物理层的衔接。将MCU内部MAC控制器和MAC+PHY集成芯片都看作设备。通过bRead和bWrite读取和发送以太网数据。 这样有一个好处,例如这个场景: 第一版产品是STM32F107(带MAC控制器)+PHY芯片,后面更换为某国产MCU(没有MAC控制器),第二期产品使用 MCU+MAC/PHY集成芯片。只需要更换bNetif实例里面的设备号即可。 ```C bNETIF_INSTANCE(bMcuNetif, bMCUMAC); int main(void) { .... bInit(); bNetifAdd(&bMcuNetif, 0, 0, 0); while (1) { bExec(); } } ``` ![](../_static/eth_netif_uml.jpg) 打开设备后会有一个操作:通过bCtl设置内存操作接口。为什么加上这个操作呢? 数据从驱动层到达Lwip大概有这么几个方式: ① 驱动层读取以太网数据,放在静态数组A。netif软件模块从驱动层读取数据放入struct pbuf结构的内存B。将B喂给LWIP。 ② 驱动层读取以太网数据,放入动态内存A,netif软件模块从驱动层读取数据放入struct pbuf结构的内存B。将B喂给LWIP。 ③ 驱动层读取以太网数据,放入 struct pbuf 结构的内存B,netif软件模块从驱动层获取B,喂给LWIP 方式①和②的内存峰值高,对于内存紧张的MCU来说不太友好。第3种方式不足就是让驱动层直接调用了lwip里面的接口,不太合理。 最终采用如下方式: 调用Lwip的接口放在b_mod_netif里面,给驱动层传入一个数据结构用于内存操作: ```C // 为了减少峰值内存,减少数据在不同层的拷贝。将上层操作struct pbuf的方法注册给下层使用 typedef struct { // m_create 申请空间 // len: 申请空间的长度 // p : 申请空间的指针 int (*m_create)(uint16_t len, void **p); // m_next 申请的空间是链表形式,调用这个接口切换到下一块空间 // current_p: 指向当前使用的空间 // p : 得到的下一个空间的指针 int (*m_next)(void *current_p, void **p); // m_payload 获取payload空间。 // p : 指向当前空间的指针 // payload: 空间可能存在头部,那么payload指向实际数据空间 // paylaod_len: 可用空间长度 int (*m_payload)(void *p, void **payload, uint32_t *payload_len); } bHalBufList_t; ``` HAL层新增了b_hal_eth,用于适配带有MAC控制器的MCU。配合b_drv_mcumac驱动使用。 具体代码在dev分支,例程在例程仓库的hal_stm32f107分支。 TODO: ① 增加 MAC+PHY集成芯片的驱动。这样的芯片比较多,需要各位开发者一起添加。 ② 增加 统一的网络接口,方便应用层使用。 有兴趣的开发者可以看看目前的代码,以及完成TODO事项。 如有建议和问题反馈,在这个ISSUE单下评论:https://gitee.com/notrynohigh/BabyOS/issues/I8L6R6