LwIP流程

image.png
这个图最能说明白LwIP的整个工作流程。最左边包括网卡初始化和加载并创建接收线程,然后在接收线程中等待硬件中断释放信号量告诉接收线程有接收数据完成,并通过邮箱的方式告诉LwIP线程去处理。LwIP的内核线程和网卡收发数据之间是独立的,他们通过邮箱的形式进行数据的传递,也就是最右边的流程。如果是多网卡,就会有多个左边加右边的流程,netif->input(p, netif)里面带了每个网卡的结构体。我们这里只讨论的单网卡,所以所有跟网卡相关的函数都没有用netif这个参数,只是在加载网卡后把当前网卡设置成了默认网卡。

互联型RISC-V单片机CH32V307

image.png
CH32V307RCT6自带了一个千兆MAC和10M硬件PHY,10M对于物联网设备来说已经足够用了,再移植个Modbus-TCPIP协议栈,实现和PLC、机柜等场站式设备的采集。同时相对于STM32同型号的价格来说还是很有优势的。
开发环境是MounRiver,实际上是基于Eclipse的魔改,仿真器是沁恒自己研发的WCH-LINK,全开源的,某宝上一个十块上下,这样的话从MUC的ip、IDE、仿真器全部都是开源的,不用再顾虑版权的问题。

LwIP的移植

FreeRTOS不用移植,MounRiver自带的模板里面有,看了下移植的相对来说还是比较深的,与STM32的模板相比MounRiver没有对Freertos再封装一层。
官方也提供了自己的网络协议栈netlib,不过是以lib的形式提供的,试了例程用起来还是很方便的,因为源码看不到,所以还是选择了移植LwIP。
移植过程参考了野火电子的《LwIP应用开发实战指南》,最主要的是ethernetif.c中三个函数的编写:

  1. static void low_level_init(struct netif *netif)
  2. static err_t low_level_output(struct netif *netif, struct pbuf *p)
  3. static struct pbuf * low_level_input(struct netif *netif)

init就是对硬件的初始化,其中关于input、output这两个函数,当DMA接收到的数据超过了pbuf的长度时,要创建pbuf链表并把DMA数据复制到链表中;当pbuf发送的数据长度超过了DMA的buffer长度时,要把pbuf的数据分批复制到DMA链表中;如果不超就直接复制。
与操作系统相关的sys_arch.c的移植直接复制了野火电子的代码。
底层移植完成后就是网络应用的编写,至此整个移植过程完成。
LwIP相当于是通过sys_arch.c接管了Freertos,这样的话整个LwIP就和其他Freertos任务相互独立,Lwip一共创建了3个线程,一个LwIP的内核线程,一个网卡接收线程,一个网络应用线程。如果是多socket,创建多个网络应用线程即可。
整个LwIP分层还是很明显的。

移植过程中的BUG

仿真过程不能开独立看门狗

image004(06-13-1(06-15-10-16-49).jpg
仿真过程中看门狗要关闭的,独立看门狗在仿真暂停时还会工作并复位MCU,造成仿真出现错误。

关闭硬件checksum,使用LwIP软件checksum

Catch2979(06-14-(06-15-10-16-49).jpg
这个问题处理的时间比较长,ARP有回复,也能ping通板子,但是TCP握手不成功,最后用官方的代码对比了两个SYN的字节,发现是checksum的问题,参考,关闭了CH32V307的硬件checksum,使用LwIP的软件checksum,瞬间链接成功。