整体架构
硬件
对于硬件连接不需要进行过多的赘述,只需要将管脚正确连接即可。电源方面:3V3、GND和RESET管脚,对于ENC28J60有软件复位功能,但是两个RESET的功能不一致,所以将硬件RESET管脚连接起来保证功能的稳定性;数据传输SPI方面:CLK、MISO、MOSI和CS;其它:INT,这个中断的作用是当ENC28J60有数据要发送给MCU时或者存在一些状态错误的时候,这个中断会产生。当然,如果MCU软件部分采用轮询模式,循环查询ENC28J60 某个寄存器是否有数据,则不需要连接这个管脚。但是建议使用中断模式,提高主控效率。
软件
- 驱动层。实现硬件抽象和必要的数据发送接收函数。
- 协议栈。根据驱动函数实现协议栈的初始化、数据发送、数据接收接口。
- 应用层。使用协议栈的API编写Demo,包括添加网卡、网络配置、DHCP或静态IP配置、数据通讯等等。
SPI驱动
SPI驱动是依赖具体的MCU型号的,所以只需要根据提供的驱动代码进行编程即可。主要实现的函数为:SPI初始化、SPI发送数据、SPI接收数据。
- 初始化函数需要实现SPI的配置信息。ENC28J60的SPI配置信息固定:时钟相位(0,0)、MSB first(大端)、CS低有效、最大传输速率10Mb/s
- 发送接收函数直接调用驱动接口进一步是实现即可。传输方式(DMA/CPU)、接收中断(下降沿有效)
ENC28J60网卡驱动
网卡驱动厂商一般都会提供网卡驱动代码,而网卡驱动代码的流程都是有迹可循的,对照手册查看时序和寄存器操作流程即可。而需要用户实现就是基于特定的MCU平台调用spi相关函数实现 enc_readopc、enc_writeopc、enc_readbuf、enc_writebuf四个函数。
整体上,ENC28J60包括MAC和PHY,要想访问到特定的寄存器需要 bank + 地址两部分组成,对于其8K的RAM访问需要用户编程写寄存器来划分发送和接收缓冲区的边界,并读写数据。
Lwip协议栈
关于Lwip协议栈的移植在网上有比较多的示例。最终用户只需要自己实现ethernet.c文件里面的5个函数即可:
1 | static void low_level_init(struct netif *netif) |
low_level_init
网卡的初始化函数。它主要用来完成网卡复位及参数初始化,主要包含以下几个步骤
1 | static void low_level_init(struct netif *netif) |
low_level_output
网卡数据包的发送函数。将内核数据包pbuf发送出去,伪代码如下:
1 | static unsigned char send_buffer[1500]; |
low_level_input
网卡数据包接收函数。需要将网卡传过来的数据封装成pbuf的形式传给协议栈
1 | static unsigned char recv_buffer[2000]; |
ethernetif_input
调用网卡数据包接收函数 low_level_input 从网卡处读取一个数据包。
对于数据接受来讲,如果使用中断:ETH_IRQHandler() → ethernetif_input() → netif->input(p, netif) → tcpip_input() → tcpip_inpkt() → tcpip_thread()。当以太网有数据需要Master来接收,首先给主机端产生一个中断,主机端需要做的是将以太网接受数据的过程放在ethernetif_input函数中实现,后面都是通用流程一步步调用,最终将数据返回给应用层。
这里还需要注意的是,这里接受数据的过程将可能是耗时的操作,不能放在中断服务程序里面进行,要么使用delay work要么使用thread进行实现。
1 | void ethernetif_input(void* param) |
ethernetif_init
网卡初始化函数。需要完成netif字段的初始化,该函数在上层 netif_add 的时候会调用到
1 | err_t ethernetif_init(struct netif *netif) |
应用层
展示DHCP配网的基本流程
1 | static void dhcp_config(uint8_t* hwaddr) |
FAQ
分析发现MCU可以从ENC28J60接收到数据包,但是格式不正确?
分析:多次测试查看数据包内容是否一致。如果不一致可能是中断导致,中断响应不及时等原因;如果一致则可能是发送数据包不正确、数据发送或者接收流程有问题。
定位:对于ENC28J60有一个8K RAM,按照数据手册将数据从接收缓冲区全部读出验证发现数据是正确的,但是解析不正确,最终定位到ENC_readOpc接口有一个无效字节的问题,对于read buffer所有数据都是有效的,调用这个接口就把有效数据丢掉了进而导致的问题的出现。
数据不正确的调试方法?
- 信号质量。包括电压大小、信号稳定性等等
- 确认数据不正确是错的一致还是错的不一致,进而确认问题是随机产生还是可能配置导致的bug
- 对比正确的数据格式,分析可能存在的配置错误
ping命令显示主机不可达并且一段时间自行恢复?
首先可以通过 arp -n 命令查看arp缓存,这种要么是网络存在问题要么就是防火墙或者arp的问题。将设备上Lwip协议栈中 DHCP_DOES_ARP_CHECK 这个宏的配置改成1即可