BabyOS增加第三方库LWIP

MCU项目中网络通讯是比较常用的功能,BabyOS选择增加LWIP为用户节省移植网络协议栈的时间,以及后续提供统一的网络操作接口。

目前硬件结构大概有两种:

../_images/eth_hard_struct.jpg

网络数据流向:

../_images/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实例里面的设备号即可。

bNETIF_INSTANCE(bMcuNetif, bMCUMAC);

int main(void)
{
    ....
    bInit();
    bNetifAdd(&bMcuNetif, 0, 0, 0);
    while (1)
    {
        bExec();
    }
}

../_images/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里面,给驱动层传入一个数据结构用于内存操作:

// 为了减少峰值内存,减少数据在不同层的拷贝。将上层操作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