搜索
bottom↓
回复: 229

ENC28J60学习笔记——AVRNET项目

  [复制链接]

出0入0汤圆

发表于 2013-2-10 19:32:55 | 显示全部楼层 |阅读模式
本帖最后由 xukai871105 于 2013-2-10 19:34 编辑

1前言
嵌入式以太网开发,可以分为两个部分,一个是以太网收发芯片的使用,一个是嵌入式以太网协议栈的实现。以太网收发芯片的使用要比串口收发芯片的使用复杂的多,市面上流通比较广泛的以太网收发芯片种类还不少,有SPI接口的ENC28J60,也有并口形式的RTL8019S,CS8900A等。嵌入式以太网协议栈有著名的uIP协议栈,Lwip协议栈,还有其他嵌入式高手开发的协议栈。无论是硬件还是软件,都无法分出高下,适合项目需求的才是最好的。
1.1 写作理由
        在前言的最后,再说明一下我写作的理由。以前从淘宝上购买过ENC28J60,店家信誓旦旦地说能提供51 AVR LPC STM32等多个平台的代码,可以实现一个网页控制LED。头脑一热买了回来,买回来才发现,店家提供的资料零零散散,非常难懂,虽然不贵仅仅需要40多元,现在只需要20多元。但是总感觉有欺骗的嫌疑,这也可以映射出中国人做技术买卖的原则,产品多是实物而非服务。几经周转,发现原来这些ENC28J60的代码都出自一个地方——AVRNET,源自老外的一个开源项目。把最原始的代码拿来细细品味,以太网协议就不那么神秘了。在这里说一下ENC28J60的使用,熟悉了ENC28J60的驱动可以分几步走。第一步,通过ENC28J60移植uIP或者lwIP协议栈,实现TCP或是UDP通信,第二,顺着AVRNET项目走,实现一个简单的web服务器,运行静态或者动态网页。嵌入式以太网和计算机以太网开发不同,对于TCP通信而言没有windwos socke用,对于网页编程而言也没有ISS或PHP,所示实现起来会比较麻烦,但是也非常有乐趣。
1.2 平台说明
硬件平台 Atmega32 + proteus 7.10+WinPcap
编译平台 AVR Studio 6
        关于硬件平台,由于AVRNET项目采用ATmega32,分析的时候也采用Atmega32。就ENC28J60而言,对于其他的平台,例如STM32或是MSP而言只需要修改SPI操作即可。由于没有硬件平台,所以使用proteus仿真,注意仿真以太网是proteus需要安装WinPcap。
        关于编译平台,AVRNET项目使用的是AVR Stdui 4.XX。这个版本稍显老旧,我就进行了相关修改,在AVR Studio 6中重新编译,并修正了几个错误。当然其他的编译平台也适用。
        总结一句,平台选用原则——“求同存异”。
1.3 资料准备
        以太网开发是非常复杂的工作,在开始之前最好先大致浏览一些ENC28J60的使用手册,MICROCHIP可以下载,中文版本阅读非常方便。除此之外,需要认真阅读TCP IP相关知识,推荐一本图书《嵌入式Internet TCP/IP基础、实现和应用》。
        嵌入式开发总是一个反复借鉴的过程。该部分代码参考了AVRNET项目和奋斗开发板的相关范例。AVRNET项目网址链接http://www.avrportal.com/?page=avrnet
2 寄存器和寄存器操作
       ENC28J60的寄存器很多,操作这些寄存器需要一个良好的代码组织工作。在AVRNET项目中,把ENC28J60的驱动分解成ENC28J60.h文件和ENC28J60.c文件。H文件中主要描述ENC28J60寄存器的基本定义,而C文件主要实现了这些寄存器的操作。
2.1 寄存器定义
首先分析一下ENC28J60头文件。阅读数据手册之后,会发现ENC28J60寄存器数量较多,通过分析和整理,操作ENC28J60的寄存器需要注意以下3点。
(1)   共有三种不同形式的寄存器——控制寄存器以太网寄存器 PHY寄存器,不同的寄存器以不同的字母开头,以E MAMI加以区分。操作这三种不同的寄存器需要不同的组合命令。
(2)   寄存器被分布在4个不同的bank中,也就是说存在地址相同的寄存器,但是这些寄存器却位于不同的分区中,在操作寄存器之前必须选中正确的bank
(3)  虽然存在4bank,但是有5个寄存器在4bank的位置相同,它们是EIE EIRESTATECON1ECON2。不言而喻,这5个寄存器将会非常重要。
AVRNET项目中,寄存器被定义成8位长度,而这8位长度包含了三个部分,地址bit7(最高位)用以区分PHY和MAC寄存器地址bit6和bit5用以区分BANK,2位空间正好区分4个BANK;地址的最后5位才是寄存器的地址。通过这种方式就可以区分所有的寄存器了。列举了几行代码。由于头文件很长,所以不全部列出。
// bank0 寄存器
#define ERDPTL            (0x00|0x00)
#define ERDPTH            (0x01|0x00)
#define EWRPTL            (0x02|0x00)
// bank1 寄存器
#define EHT0              (0x00|0x20)
#define EHT1              (0x01|0x20)
#define EHT2              (0x02|0x20)
// bank2 寄存器
#define MACON1           (0x00|0x40|0x80)
#define MACON2           (0x01|0x40|0x80)
#define MACON3           (0x02|0x40|0x80)
//bank3 寄存器
#define MAADR1           (0x00|0x60|0x80)
#define MAADR0           (0x01|0x60|0x80)
#define MAADR3           (0x02|0x60|0x80)
       例如ERDPTH为位于BANK0的以太网寄存器,第一个数字0x01代表BANK0中的地址,该地址为0x01,第二个数字0x00代表BANK编号,该编号为0,意味第0BANKEHT1为位于BANK1中的控制寄存器,第二个0x20代表BANK地址为1,请注意由于BANK编号被保存在bit6bit5,所以此处为0x20,绝不是0x10MACON2为位于bank2的以太网寄存器,第一个数字0x01代表在该BANK中的寄存器地址,第二个数字0x40代表BANK编号,而第三个数字0x80代表该寄存器为以太网寄存器或是PHY寄存器,这些寄存器的操作和控制寄存器有区别。
       为了方便寄存器操作,h文件中还定义了寄存器地址操作的掩码,简单而言就是需要查看哪些位,不需要查看哪些位。
/* 寄存器地址掩码 */
#defineADDR_MASK        0x1F
/* 存储区域掩码 */
#defineBANK_MASK        0x60
/* MACMII寄存器掩码*/
#defineSPRD_MASK        0x80
       另外还有比较特殊的5个控制寄存器,EIEEIRESTATECON2ECON1
/* 关键寄存器 */
#defineEIE                     0x1B
#defineEIR                     0x1C
#defineESTAT                   0x1D
#defineECON2                  0x1E
#defineECON1                  0x1F
2.2 寄存器操作命令
       寄存器操作命令也可称为寄存器操作码。为了实现寄存器的操作,ENC28J60定义了6+1个寄存器操作命令(操作码)。操作相关寄存器至少有读寄存器命令,写寄存器命令;发送或接收以太网数据则必有写缓冲区命令或读缓冲区命令;为了加快操作,对于某些控制寄存器而言还可以有置位或者清零某位的命令;最后加上一个软件复位命令,锦上添花。
  1. <font size="3">/* 读控制寄存器 */
  2. #define ENC28J60_READ_CTRL_REG          0x00
  3. /* 读缓冲区 */
  4. #define ENC28J60_READ_BUF_MEM          0x3A
  5. /* 写控制寄存器 */
  6. #define ENC28J60_WRITE_CTRL_REG          0x40
  7. /* 写缓冲区 */
  8. #define ENC28J60_WRITE_BUF_MEM          0x7A
  9. /* 位域置位 */
  10. #define ENC28J60_BIT_FIELD_SET              0x80
  11. /* 位域清零 */
  12. #define ENC28J60_BIT_FIELD_CLR                     0xA0
  13. /* 系统复位 */
  14. #define ENC28J60_SOFT_RESET                        0xFF</font>
复制代码
2.3 接收和发送缓冲区分配
       以太网数据的接收和发送离不开驱动芯片内部的RAM,也可称之为硬件缓冲区。ENC28J60包括8K 的硬件缓冲区,该硬件缓冲区一部分被接收缓冲区使用,另一部分为发送缓冲区使用。操作ENC28J60的最终目的为操作该硬件缓冲区。执行以太网发送命令时,向发送缓冲区中填充数据,并触发相关寄存器发送以太网数据;执行以太网接收命令时,通过查询相关寄存器或者外部中断的方式获得以太网数据输入事件,接着从接收缓冲区中读取相关数据。
(1)   把缓冲区划分为两个部分。把8K的硬件缓冲区划分为两个部分至少需要四个参数,接收缓冲区需要一个起始地址和一个结束地址加以描述,发送缓冲区也需要一个起始地址和一个结束地址加以描述。最理想的方式,两个缓冲区完全占据了8K的硬件缓冲区,完美地利用这一空间。由于ENC28J60的寄存器长度为8位,而硬件缓冲区的大小为8K,所以前面提到的4个地址需要8个寄存器才可以完全描述,需要把单个地址分为高8位和低8位。在AVRNET项目中,接收缓冲区较大,而发送缓冲区较小。在以太网协议中,最大的报文长度为1518字节,而最小报文长度为60字节。发送缓冲区等于或略大于1518字节,剩余的部分全部分配给接收缓冲区。接收缓冲区较大也是考虑到AVR的处理能力有限,若某个时间点收到多个以太网报文,可以先把报文闲置与硬件缓冲区中,待空闲时再从缓冲区中取出。
/* 接收缓冲区起始地址 */
#define RXSTART_INIT                0x00
/* 接收缓冲区停止地址 */
#define RXSTOP_INIT                 (0x1FFF - 0x0600 - 1)
/* 发送缓冲区起始地址 发送缓冲区大小约1500字节*/
#define TXSTART_INIT                (0x1FFF - 0x0600)
/* 发送缓冲区停止地址 */
#define TXSTOP_INIT                 0x1FFF


硬件缓冲区结构
(2)   对于发送缓冲区而言,需要指定发送缓冲区写指针,使用写缓冲区命令操作该部分缓冲区,写指针的地址会不断增长,若遇到结束地址会重新返回起始地址。对于接收缓冲区而言就稍微复杂一点,每次读取之前必须明确该次操作时的读指针位置,根据前文的代码,缓冲区读指针的起始地址为0,在第一次读操作发生之后需要立即设置下次读操作的读指针地址。ENC28J60读缓冲区时,读取的数据并不全是以太网的数据,在以太网数据之前还有下一个数据包的地址指针占两个字节,接收状态向量占4个字节,接着才是以太网数据包,该数据包包括目标MAC地址,源MAC地址,数据包类型等等;最后为CRC校验和。在接收状态向量的起始2个字节为该以太网数据包的长度,该参数也是非常有用的参数。


接收数据包结构
对于发送缓冲区而言,需要指定发送缓冲区写指针,使用写缓冲区命令操作该部分缓冲区,写指针的地址会不断增长,若遇到结束地址会重新返回起始地址。对于接收缓冲区而言就稍微复杂一点,每次读取之前必须明确该次操作时的读指针位置,根据前文的代码,缓冲区读指针的起始地址为0,在第一次读操作发生之后需要立即设置下次读操作的读指针地址。ENC28J60读缓冲区时,读取的数据并不全是以太网的数据,在以太网数据之前还有下一个数据包的地址指针占两个字节,接收状态向量占4个字节,接着才是以太网数据包,该数据包包括目标MAC地址,源MAC地址,数据包类型等等;最后为CRC校验和。在接收状态向量的起始2个字节为该以太网数据包的长度,该参数也是非常有用的参数。
3 寄存器操作实现
      ENC28j60的寄存器操作分为2+2+2部分,分别为写寄存器和读寄存器部分,读缓冲区和写缓冲区部分,写PHY寄存器和读PHY寄存器部分。
3.1 读写寄存器
       读或写寄存器的函数如下
  1. <font size="3">unsigned char enc28j60Read(unsigned char address)
  2. {
  3.        /* 设定寄存器地址区域 */
  4.        enc28j60SetBank(address);
  5.        /* 读取寄存器值 发送读寄存器命令和地址 */
  6.        return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address);
  7. }
  8. void enc28j60Write(unsigned char address, unsigned char data)
  9. {
  10.        /* 设定寄存器地址区域 */
  11.        enc28j60SetBank(address);
  12.        /* 写寄存器值 发送写寄存器命令和地址 */
  13.        enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data);
  14. }</font>
复制代码
       读写寄存器的分为两步,第一步为选定寄存器的BANK编号,第二步为使用写命令或读命令,操作指定地址的寄存器。在ENC28J60中,由ECON1中的某两位保存BANK编号,ECON1是比较特殊的控制寄存器,在4BANK中具有该寄存器且该寄存器的地址相同。Enc28j60Bank为全局变量,用于保存当前的BANK编号,如果两次操作控制寄存器在同一个BANK时,该变量保持不变,若两次操作的控制寄存器位于不同的BANK,那么BANK的值会变为新的BANK编号。
  1. <font size="3">void enc28j60SetBank(unsigned char address)
  2. {
  3.        /* 计算本次寄存器地址在存取区域的位置 */
  4.        if((address & BANK_MASK) != Enc28j60Bank)
  5.        {
  6.     /* 清除ECON1的BSEL1 BSEL0 详见数据手册15页 */
  7.     enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0));
  8.     /* 请注意寄存器地址的宏定义,bit6 bit5代码寄存器存储区域位置 */
  9.     enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5);
  10.     /* 重新确定当前寄存器存储区域 */
  11.     Enc28j60Bank = (address & BANK_MASK);
  12.        }
  13. }</font>
复制代码
  1. <font size="3">unsigned char enc28j60ReadOp(unsigned char op, unsigned char address)
  2. {
  3.        unsigned char dat = 0;
  4.       
  5.        /* CS拉低 使能ENC28J60 */
  6.        ENC28J60_CSL();
  7.        /* 操作码和地址 */
  8.        dat = op | (address & ADDR_MASK);
  9.   /* 通过SPI写数据*/
  10.        spi_sendbyte(dat);
  11.   /* 通过SPI读出数据 */
  12.        dat = spi_sendbyte(0xFF);
  13.       
  14.   /* 如果是MAC和MII寄存器,第一个读取的字节无效,该信息包含在地址的最高位 */
  15.        if(address & 0x80)
  16.        {
  17.               /* 再次通过SPI读取数据 */
  18.     dat = spi_sendbyte(0xFF);
  19.        }
  20.       
  21.   /* CS拉高 禁止ENC28J60 */
  22.        ENC28J60_CSH();

  23.   /* 返回数据 */
  24.        return dat;
  25. }</font>
复制代码
读控制寄存器实际上就是严格遵守数据手册的操作要求,一次编写程序。在这里由于读MACMII寄存器时,第一个接收到的字节为无效字节,第二个字节才为有效字节。程序通过寄存器地址的最高位来判断是否为MACMII寄存器。写寄存器函数较为简单,第一次字节包括操作码和寄存器地址,第二个字节则为数据。在这两个函数中参数opENC28J60的指令,或称之为操作码,该指令占据了SPI第一个字节的前3位,参数address为寄存器地址,参数data为寄存器的具体值。
这两个函数和硬件发生某些关系,ENC28J60_CSL()和ENC28J60_CSH()为操作CS端口的操作宏,而spi_sendbyte()可通过SPI发送一个字节。修改这些函数即可在其他平台上使用ENC28J60。不过请特别注意,在使用其他开发板时由于SPI总线上可能挂载多个设备,单独使用ENC28J60时需要把其他设备的CS端口拉高,或安装一个上拉电阻。
  1. <font size="3">unsigned char enc28j60ReadOp(unsigned char op, unsigned char address)
  2. {
  3.        unsigned char dat = 0;
  4.       
  5.        /* CS拉低 使能ENC28J60 */
  6.        ENC28J60_CSL();
  7.        /* 操作码和地址 */
  8.        dat = op | (address & ADDR_MASK);
  9.   /* 通过SPI写数据*/
  10.        spi_sendbyte(dat);
  11.   /* 通过SPI读出数据 */
  12.        dat = spi_sendbyte(0xFF);
  13.       
  14.   /* 如果是MAC和MII寄存器,第一个读取的字节无效,该信息包含在地址的最高位 */
  15.        if(address & 0x80)
  16.        {
  17.               /* 再次通过SPI读取数据 */
  18.     dat = spi_sendbyte(0xFF);
  19.        }
  20.       
  21.   /* CS拉高 禁止ENC28J60 */
  22.        ENC28J60_CSH();

  23.   /* 返回数据 */
  24.        return dat;
  25. }
  26. void enc28j60WriteOp(unsigned char op, unsigned char address, unsigned char data)
  27. {
  28.        unsigned char dat = 0;
  29.   /* 使能ENC28J60 */                                                   
  30.        ENC28J60_CSL();        
  31.   /* 通过SPI发送 操作码和寄存器地址 */                        
  32.        dat = op | (address & ADDR_MASK);
  33.   /* 通过SPI1发送数据 */
  34.        spi_sendbyte(dat);
  35.   /* 准备寄存器数值 */                             
  36.        dat = data;
  37.   /* 通过SPI发送数据 */
  38.        spi_sendbyte(dat);
  39.   /* 禁止ENC28J60 */                     
  40.        ENC28J60_CSH();   
  41. }</font>
复制代码
3.2 读写缓冲区
       读写缓冲区的操作也是易于理解的。需要说明的是,两个函数具有相同的输入参数,参数len代表被操作数据的长度,pdata为被操作数据的指针。和寄存器读写函数相似,发送或接收数据之前需要发送特定的操作码。
  1. <font size="3">void enc28j60ReadBuffer(unsigned int len, unsigned char* pdata)
  2. {
  3.   /* 使能ENC28J60 */
  4.   ENC28J60_CSL();
  5.        /* 通过SPI发送读取缓冲区命令*/
  6.        spi_sendbyte(ENC28J60_READ_BUF_MEM);
  7.   /* 循环读取 */
  8.        while(len)
  9.        {
  10.     len--;
  11.     /* 读取数据 */
  12.     *pdata = (unsigned char)spi_sendbyte(0);
  13.     /* 地址指针累加 */
  14.     pdata++;
  15.        }
  16.   /* 增加字符串结尾 便于操作 */
  17.        *pdata='\0';
  18.   /* 禁止ENC28J60 */
  19.        ENC28J60_CSH();
  20. }
  21. void enc28j60WriteBuffer(unsigned int len, unsigned char* pdata)
  22. {
  23.   /* 使能ENC28J60 */
  24.   ENC28J60_CSL();
  25.        /* 通过SPI发送写取缓冲区命令*/
  26.        spi_sendbyte(ENC28J60_WRITE_BUF_MEM);
  27.       
  28.   /* 循环发送 */
  29.        while(len)
  30.        {
  31.               len--;
  32.     /* 发送数据 */
  33.               spi_sendbyte(*pdata);
  34.     /* 地址指针累加 */
  35.               pdata++;
  36.        }

  37.   /* 禁止ENC28J60 */
  38.        ENC28J60_CSH();
  39. }</font>
复制代码
3.3 读写PHY寄存器
       PHY寄存器和由ENC28J60控制的LED指示灯有关,控制这些寄存器可以控制这两个LED的驱动方式,和发生相应事件时LED的显示方式。一般情况下,一个LED指示灯常亮,显示接收和发送活动,另一个LED指示灯显示接收活动,有数据输入时产生一个点亮脉冲。PHY是比较特殊的寄存器,先要想一个控制寄存器写入PHY寄存器的地址,再向两个控制寄存器依次写入PHY寄存器的具体数据的高8位和低8位,最后等待PHY寄存器操作完成。
  1. <font size="3">void enc28j60PhyWrite(unsigned char address, unsigned int data)
  2. {
  3.        /* 向MIREGADR写入地址 详见数据手册19页*/
  4.        enc28j60Write(MIREGADR, address);
  5.        /* 写入低8位数据 */
  6.        enc28j60Write(MIWRL, data);
  7.   /* 写入高8位数据 */
  8.        enc28j60Write(MIWRH, data>>8);
  9.        /* 等待PHY寄存器写入完成 */
  10.        while(enc28j60Read(MISTAT) & MISTAT_BUSY);
  11. }</font>
复制代码
4 ENC28J60写操作
       ENC28J60的寄存器操作时ENC28J60初始化,发送以太网数据和接收以太网数据的基础。通过ENC28J60进行以太网发送数据操作,本质上为操作硬件缓冲区的发送缓冲区部分。每次发送时总是从发送缓冲区的起始地址开始填充数据,数据填充的结束地址和数据的输入长度有关。操作完发送缓冲区的大小之后可向发送缓冲区填充数据,即调用ENC28J60_WRITE_BUF_MEM操作码,接着置位ECON1中的 ECON1_TXRTS位启动发送,并使用等待法不断查询是否发送完毕。基本的思路还是和SPIUART发送数据相似,即填充数据,启动发送,查询发送完成。写操作的输入参数为数据包的长度len和数据包指针packet,该参数正好和uIP的网络层操作函数相对应。若是LwIP协议,输入参数将会是pBuf这种自定义数据结构,需要经过适当的修改才应用于lwIP协议栈。
  1. <font size="3">void enc28j60PacketSend(unsigned int len, unsigned char* packet)
  2. {
  3.        /* 查询发送逻辑复位位 */
  4.        while((enc28j60Read(ECON1) & ECON1_TXRTS)!= 0);

  5.   /* 设置发送缓冲区起始地址 */   
  6.        enc28j60Write(EWRPTL, TXSTART_INIT & 0xFF);
  7.        enc28j60Write(EWRPTH, TXSTART_INIT >> 8);

  8.        /* 设置发送缓冲区结束地址 该值对应发送数据包长度 */  
  9.        enc28j60Write(ETXNDL, (TXSTART_INIT + len) & 0xFF);
  10.        enc28j60Write(ETXNDH, (TXSTART_INIT + len) >>8);

  11.        /* 发送之前发送控制包格式字 */
  12.        enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);

  13.        /* 通过ENC28J60发送数据包 */
  14.        enc28j60WriteBuffer(len, packet);

  15.        /* 开始发送 */
  16.        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);

  17.   /* 复位发送逻辑的问题 */
  18.        if( (enc28j60Read(EIR) & EIR_TXERIF) )
  19.        {
  20.               enc28j60SetBank(ECON1);
  21.     enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS);
  22.   }
  23. }</font>
复制代码
5 ENC28J60读操作
       读操作要略比写操作复杂。写操作时每次总是从硬件发送缓冲区的起始地址开始操作,而读操作时需要不断修改接收缓冲区的读指针地址,该参数需要通过NextPacketPtr完成,该变量为长度为16的全局变量。读操作时,先通过寄存器查看是否存在以太网数据包,读EPKTCNT寄存器便可返回以太网数据包的个数;若存在以太网数据包则设定读指针的地址,执行读缓冲区操作,ENC28J60的以太网数据包中前两个字节为下一个以太网数据包的起始地址,立即保存该参数至NextPacketPtr全局变量中;以太网数据包中的后两个字节为该数据包的长度,该长度只从目标MAC地址开始的数据包的长度,进行处理时还应该舍弃最后的4字节CRC校验结果;最重要的事情便是通过读缓冲区操作码把len长度的以太网数据读出,读出的目标应为软件缓冲区,例如定义在程序中的rxtx_buf。最后根据NextPacketPtr移动读指针以便下次操作,并通过操作ECON2ECON2_PKTDEC位递减了以太网数据包。
  1. <font size="3">unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet)
  2. {
  3.        unsigned int rxstat;
  4.        unsigned int len;

  5.        /* 是否收到以太网数据包 */
  6.        if( enc28j60Read(EPKTCNT) == 0 )
  7.        {
  8.               return(0);
  9.     }

  10.        /* 设置接收缓冲器读指针 */
  11.        enc28j60Write(ERDPTL, (NextPacketPtr));
  12.        enc28j60Write(ERDPTH, (NextPacketPtr)>>8);

  13.   /* 接收数据包结构示例 数据手册43页 */

  14.        /* 读下一个包的指针 */
  15.        NextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
  16.        NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;

  17.        /* 读包的长度 */
  18.        len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
  19.        len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;

  20.    /* 去除CRC校验部分 */
  21.    len-= 4;
  22.                   
  23.        /* 读取接收状态 */
  24.        rxstat  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
  25.        rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;

  26.        /* 限制检索的长度  */
  27.   if (len > maxlen-1)
  28.        {
  29.     len = maxlen-1;
  30.   }
  31.   /* 检查CRC和符号错误 */
  32.   /* ERXFCON.CRCEN是默认设置。通常我们不需要检查 */
  33.   if ((rxstat & 0x80)==0)
  34.        {
  35.           //无效的
  36.           len = 0;
  37.        }
  38.        else
  39.        {
  40.     /* 从接收缓冲器中复制数据包 */
  41.     enc28j60ReadBuffer(len, packet);
  42.   }

  43.   /* 移动接收缓冲区 读指针*/
  44.        enc28j60Write(ERXRDPTL, (NextPacketPtr));
  45.        enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8);

  46.        /* 数据包递减 */
  47.        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);

  48.   /* 返回长度 */
  49.        return(len);
  50. }</font>
复制代码
6 ENC28J60初始化操作
       ENC28J60的操作比较琐碎。第一,进行CS端口的相关配置,即把该端口设置为输出状态,该部分代码可以出现在任何硬件初始化代码中,例如可以把所有的IO操作放入gpio_config中;第二,进行软件复位,并通过查询ESTATESTAT_CLKRDY标志位确定是否复位完成;第二,初始化NextPacketPtr变量,该变量的初值为发送缓冲区的起始地址;第三,配置发送和接收缓冲区的区间;第四,若干参数配置,请看代码注释部分,ENC28J60具有自动填充0 的功能,即发送报文长度低于以太网最小报文长度时可以填充0至最小长度;第五,写入MAC地址,由于ENC28J60内部没有全球唯一的MAC地址,所以该地址需要软件填写。但是这种软件填写方式存在缺陷,实际应用中可以含有全球唯一的MAC地址的EEPROM,从EERPOM读取MAC地址并用该地址初始化ENC28J60;第六,初始化中断,并使能接收,ENC28J60含有多个中断,最重要的有全局中断和数据包带接收中断。
  1. <font size="3">void enc28j60Init(unsigned char* macaddr)
  2. {
  3.   /* CS端口为输出 */
  4.   DDRB |= (1<<4);

  5.   /* 禁止ENC28J60 */
  6.   ENC28J60_CSH();
  7.        /* ENC28J60软件复位 该函数可以改进*/
  8.        enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
  9.   /*查询ESTAT.CLKRDY位*/
  10.        while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY));
  11.    
  12.        /* 设置接收缓冲区起始地址 该变量用于每次读取缓冲区时保留下一个包的首地址 */
  13.        NextPacketPtr = RXSTART_INIT;

  14.   /* 设置接收缓冲区 起始指针*/
  15.        enc28j60Write(ERXSTL, RXSTART_INIT & 0xFF);
  16.        enc28j60Write(ERXSTH, RXSTART_INIT >> 8);

  17.   /* 设置接收缓冲区 读指针*/
  18.        enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF);
  19.        enc28j60Write(ERXRDPTH, RXSTART_INIT>>8);

  20.   /* 设置接收缓冲区 结束指针 */
  21.        enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF);
  22.        enc28j60Write(ERXNDH, RXSTOP_INIT>>8);

  23.        /* 设置发送缓冲区 起始指针 */
  24.        enc28j60Write(ETXSTL, TXSTART_INIT&0xFF);
  25.        enc28j60Write(ETXSTH, TXSTART_INIT>>8);
  26.        /* 设置发送缓冲区 结束指针 */
  27.        enc28j60Write(ETXNDL, TXSTOP_INIT&0xFF);
  28.        enc28j60Write(ETXNDH, TXSTOP_INIT>>8);

  29.   /* 使能单播过滤 使能CRC校验 使能 格式匹配自动过滤*/
  30.        enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN);
  31.        enc28j60Write(EPMM0, 0x3f);
  32.        enc28j60Write(EPMM1, 0x30);
  33.        enc28j60Write(EPMCSL, 0xf9);
  34.        enc28j60Write(EPMCSH, 0xf7);

  35.   /* 使能MAC接收 允许MAC发送暂停控制帧 当接收到暂停控制帧时停止发送*/
  36.   /* 数据手册34页 */
  37.        enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);

  38.   /* 退出复位状态 */
  39.        enc28j60Write(MACON2, 0x00);

  40.   /* 用0填充所有短帧至60字节长 并追加一个CRC 发送CRC使能 帧长度校验使能 MAC全双工使能*/
  41.        /* 提示 由于ENC28J60不支持802.3的自动协商机制, 所以对端的网络卡需要强制设置为全双工 */
  42.        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN|MACON3_FULDPX);

  43.   /* 填入默认值 */
  44.        enc28j60Write(MAIPGL, 0x12);
  45.   /* 填入默认值 */
  46.        enc28j60Write(MAIPGH, 0x0C);
  47.   /* 填入默认值 */
  48.        enc28j60Write(MABBIPG, 0x15);

  49.   /* 最大帧长度 */
  50.        enc28j60Write(MAMXFLL, MAX_FRAMELEN & 0xFF);
  51.        enc28j60Write(MAMXFLH, MAX_FRAMELEN >> 8);

  52.   /* 写入MAC地址 */
  53.        enc28j60Write(MAADR5, macaddr[0]);     
  54.        enc28j60Write(MAADR4, macaddr[1]);
  55.        enc28j60Write(MAADR3, macaddr[2]);
  56.        enc28j60Write(MAADR2, macaddr[3]);
  57.        enc28j60Write(MAADR1, macaddr[4]);
  58.        enc28j60Write(MAADR0, macaddr[5]);

  59.        /* 配置PHY为全双工  LEDB为拉电流 */
  60.        enc28j60PhyWrite(PHCON1, PHCON1_PDPXMD);

  61.   /* LED状态 */
  62.   enc28j60PhyWrite(PHLCON,0x0476);   

  63.   /* 半双工回环禁止 */
  64.        enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS);

  65.   /* 返回BANK0 */     
  66.        enc28j60SetBank(ECON1);

  67.   /* 使能中断 全局中断 接收中断 接收错误中断 */
  68.        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE|EIE_RXERIE);

  69.   /* 接收使能位 */
  70.        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
  71. }</font>
复制代码

7 总结
ENC28J60的驱动编写算是比较复杂的。但是回过头来看看,其他的以太网驱动芯片的操作和ENC28J60的操作类似,其操作的核心即时数KB的硬件缓冲区。本例不能给出合适的运行范例,因为以太网驱动芯片要配合以太网协议栈来实现,而以太网协议栈内容很多,即使通过uIP或是lwIP也必须面对繁多的基础知识。ENC28J60的驱动是以太网协议栈实现的基础,通过ENC28J60还将会分析uIP协议栈,lwIP协议栈的应用。在实现TCP通信之后,还将会结合AVRNET或uIP,lwIP协议栈实现web服务器,通过网页交换数据。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2013-2-10 21:35:37 | 显示全部楼层
年前刚写了一个基于这款芯片的网络模块,MCU用的是nuc120,用的是UDP,效率一直调不满意,只有30KB每秒。。。回头认真学习一下这篇文章,看看有没有可以进一步优化的地方。

出0入0汤圆

发表于 2013-2-10 22:32:29 | 显示全部楼层
兄弟写了一篇好文章 正需要

出0入0汤圆

发表于 2013-2-10 23:14:37 | 显示全部楼层
内容很给力嘛~~

出0入0汤圆

发表于 2013-2-10 23:19:46 | 显示全部楼层
正准备入ENC28J60

出0入0汤圆

 楼主| 发表于 2013-2-10 23:26:32 | 显示全部楼层
zhuhuning 发表于 2013-2-10 22:32
兄弟写了一篇好文章 正需要

接着几天还有uIP或是lwIP,http网页控制相关内容!
请关注,有问题请提出,共同交流!

出0入0汤圆

发表于 2013-2-10 23:28:37 | 显示全部楼层
我这几天也在写这个应用 发现提供的源码要想运行起来 还是需要好好研究一下

出0入0汤圆

发表于 2013-2-11 07:01:29 | 显示全部楼层
注释的比较详细!

出0入0汤圆

发表于 2013-2-11 07:34:47 | 显示全部楼层
謝謝你,註解滿詳細的.

出0入0汤圆

发表于 2013-2-11 07:48:48 来自手机 | 显示全部楼层
好文章,收下好好研究

出0入0汤圆

发表于 2013-2-12 08:45:25 | 显示全部楼层
此帖必火,顶起来,慢慢学习。

出0入0汤圆

发表于 2013-2-12 09:32:22 | 显示全部楼层
学习了,谢谢楼主

出0入0汤圆

发表于 2013-2-15 16:28:10 | 显示全部楼层
哎!在家看他的数据手册了,还是没感觉。看到这帖我真的太高兴了,看来没白跑网吧啊

出0入0汤圆

发表于 2013-2-15 20:53:34 来自手机 | 显示全部楼层
写得不错!

出0入0汤圆

发表于 2013-2-15 22:01:28 | 显示全部楼层

看来没白跑网吧啊

出0入0汤圆

发表于 2013-3-2 22:46:12 | 显示全部楼层
学习了,期待接下来的内容。

出0入0汤圆

发表于 2013-3-6 00:42:44 | 显示全部楼层
不错,收藏

出0入0汤圆

发表于 2013-3-7 12:38:17 | 显示全部楼层
谢谢分享~~~~~~~~~~~~~

出0入0汤圆

发表于 2013-3-7 13:28:03 | 显示全部楼层
这个片子适合低速小型应用,比如门禁监控类;

现在 ENC424J600 出来了,速度飞快,配合有总线的处理器还是不错,建议楼主玩玩!

出0入0汤圆

 楼主| 发表于 2013-3-7 16:53:09 | 显示全部楼层
Wxy8030 发表于 2013-3-7 13:28
这个片子适合低速小型应用,比如门禁监控类;

现在 ENC424J600 出来了,速度飞快,配合有总线的处理器还是 ...

有时间玩玩,现在这个是10M的,那个应该是10/100M。

出0入0汤圆

发表于 2013-3-9 09:42:33 | 显示全部楼层
mark! ENC424J600未玩过,ENC28J60玩过很久了。支持一下。

出0入0汤圆

发表于 2013-3-9 13:31:41 | 显示全部楼层
刚买了模块,还在摸索怎么用
感谢分享心得,对新人很有帮助......

出0入0汤圆

发表于 2013-3-9 22:42:19 | 显示全部楼层
标记  学习中

出0入0汤圆

发表于 2013-3-12 14:32:54 | 显示全部楼层
楼主大好人,学习了!

出0入0汤圆

发表于 2013-3-14 14:43:07 | 显示全部楼层
受益匪浅,正在学习中

出5入42汤圆

发表于 2013-3-18 14:59:58 | 显示全部楼层
记号留名 要做ENC424J600 参考一下

出0入0汤圆

发表于 2013-3-18 15:57:29 | 显示全部楼层
正需要,学习了

出0入0汤圆

发表于 2013-3-18 16:01:56 | 显示全部楼层
這個比較強悍

出0入0汤圆

发表于 2013-3-21 14:54:02 | 显示全部楼层
写的很好,但我有一个疑问,文章中说"对于发送缓冲区而言,需要指定发送缓冲区写指针,使用写缓冲区命令操作该部分缓冲区,写指针的地址会不断增长,若遇到结束地址会重新返回起始地址。"难道发送缓冲区就不能存储2个及以上的数据包(一个数据包正在发送,另外又有一个数据包正在往缓冲区里写,是否可行?!)

出0入0汤圆

发表于 2013-3-21 15:39:57 | 显示全部楼层
xukai871105 发表于 2013-2-10 23:26
接着几天还有uIP或是lwIP,http网页控制相关内容!
请关注,有问题请提出,共同交流! ...

好文学习了
很期待下文

出0入0汤圆

发表于 2013-3-21 16:18:06 来自手机 | 显示全部楼层
收藏了!!!

出0入0汤圆

发表于 2013-3-21 16:33:54 | 显示全部楼层
xukai871105 发表于 2013-2-10 23:26
接着几天还有uIP或是lwIP,http网页控制相关内容!
请关注,有问题请提出,共同交流! ...

这个要顶的,必须顶~

出0入0汤圆

发表于 2013-3-22 14:51:46 | 显示全部楼层
只能顶下了

出0入0汤圆

 楼主| 发表于 2013-3-23 14:14:38 | 显示全部楼层
newywx 发表于 2013-3-21 16:33
这个要顶的,必须顶~

一直忙于硕士论文答辩和实习工作交接的事宜,一直没有时间写!
不好意思,但是说到做到,不含糊!

出0入0汤圆

发表于 2013-3-23 19:20:07 | 显示全部楼层
学习了,继续期待楼主的web服务器、http网页控制相关内容部分。

出0入0汤圆

发表于 2013-5-30 19:33:23 | 显示全部楼层
学习 收藏

出0入0汤圆

发表于 2013-5-30 19:42:33 | 显示全部楼层
xukai871105 发表于 2013-3-23 14:14
一直忙于硕士论文答辩和实习工作交接的事宜,一直没有时间写!
不好意思,但是说到做到,不含糊! ...

楼主的代码编辑软件是啥?

出0入0汤圆

 楼主| 发表于 2013-5-31 15:50:01 | 显示全部楼层
Hunter 发表于 2013-5-30 19:42
楼主的代码编辑软件是啥?

先使用了AVR Studio 6
然后用了 STM32 IAR 5.5

出0入0汤圆

发表于 2013-6-6 06:40:23 | 显示全部楼层
enc28j60在数据量过大时有死机现象 怎么处理

出0入0汤圆

 楼主| 发表于 2013-6-6 20:15:53 | 显示全部楼层
fengkehy 发表于 2013-6-6 06:40
enc28j60在数据量过大时有死机现象 怎么处理

比较抱歉,我还没有使用ENC28J60传输大数据的情况!
是否可以把你的测试环境描述的清楚一点,我有时间一定会测试的!

出0入0汤圆

发表于 2013-6-6 20:22:10 | 显示全部楼层
非常好 值得学习

出0入0汤圆

发表于 2013-6-7 12:54:18 | 显示全部楼层
楼主给力!!!!!!!!

出0入0汤圆

发表于 2013-6-14 14:55:16 | 显示全部楼层
MARK
                        

出0入0汤圆

发表于 2013-6-20 10:09:35 | 显示全部楼层
不知楼主的ENC28J60有没出现接收溢出后一段时间就不能再接收的问题.

出0入0汤圆

 楼主| 发表于 2013-6-20 11:20:36 | 显示全部楼层
linjunxiong 发表于 2013-6-20 10:09
不知楼主的ENC28J60有没出现接收溢出后一段时间就不能再接收的问题.

接收溢出我还没有仔细测试过!
能否相似说明出现问题的 场景,我好复现你的场景!

出0入0汤圆

发表于 2013-6-20 13:30:28 | 显示全部楼层
支持楼主,期待楼主的大作!

出0入0汤圆

发表于 2013-6-20 15:29:46 | 显示全部楼层
好文章,值得学习

出0入0汤圆

发表于 2013-6-20 15:43:55 | 显示全部楼层
内容很给力嘛~~

出0入0汤圆

发表于 2013-6-20 19:23:04 | 显示全部楼层
xukai871105 发表于 2013-6-20 11:20
接收溢出我还没有仔细测试过!
能否相似说明出现问题的 场景,我好复现你的场景! ...

电脑直接用UDP不断发数据,速度快,溢出,但是还是能接收,溢出一段时间后就再也收不到数据了。

出0入0汤圆

发表于 2013-7-3 12:15:24 | 显示全部楼层
lwip那段特别期待,楼主加油!

出0入0汤圆

发表于 2013-7-3 12:32:26 | 显示全部楼层
这个一定要顶下楼主

出0入0汤圆

 楼主| 发表于 2013-7-3 12:54:09 | 显示全部楼层
robin45853258 发表于 2013-7-3 12:15
lwip那段特别期待,楼主加油!

呵呵 有时间动手学习一下估计便可实现!
资料看了很长时间了!

出0入0汤圆

发表于 2013-7-4 19:25:01 | 显示全部楼层
xukai871105 发表于 2013-7-3 12:54
呵呵 有时间动手学习一下估计便可实现!
资料看了很长时间了!

enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
请问这个操作在每次发送数据包之前有什么用?另外,从你的讲解和代码中可以知道几种不同的OP操作,为什么在官方的数据手册里没有找到说明的地方?还是我找的不仔细?

出0入0汤圆

 楼主| 发表于 2013-7-5 08:50:44 | 显示全部楼层
robin45853258 发表于 2013-7-4 19:25
enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
请问这个操作在每次发送数据包之前有什么用?另外, ...

#define ENC28J60_READ_CTRL_REG          0x00
/* 读缓冲区 */
#define ENC28J60_READ_BUF_MEM          0x3A
/* 写控制寄存器 */
#define ENC28J60_WRITE_CTRL_REG          0x40
/* 写缓冲区 */
#define ENC28J60_WRITE_BUF_MEM          0x7A
/* 位域置位 */
#define ENC28J60_BIT_FIELD_SET              0x80
/* 位域清零 */
#define ENC28J60_BIT_FIELD_CLR               0xA0
/* 系统复位 */
#define ENC28J60_SOFT_RESET                  0xFF

不知道这个是否是你要的!

出0入0汤圆

发表于 2013-7-5 10:07:14 | 显示全部楼层
xukai871105 发表于 2013-7-5 08:50
#define ENC28J60_READ_CTRL_REG          0x00
/* 读缓冲区 */
#define ENC28J60_READ_BUF_MEM          ...

你之前发的那个命令列表我看到了,我是没有理解下面这个函数:
// write per-packet control byte (0x00 means use macon3 settings)
enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);

其中,enc28j60WriteOp()的原型是:
void enc28j60WriteOp(u8 op, u8 address, u8 data)
{
   u8 dat = 0;

   ENC28J60_CSL();
   // issue write command
   dat = op | (address & ADDR_MASK);
   spi_readwrite(dat);
   // write data
   dat = data;
   spi_readwrite(dat);
   ENC28J60_CSH();
}

很明显,上面这个函数通过SPI接口先发送了0x7A,后发送了0x00。
其中,第一个数据0x7A是op | (address & ADDR_MASK)的结果,而事实上address取任何地址都无法影响到bit1、bin3、bit4,因为前面op是01111010b,如果第一个0x7A是在告诉ENC28J60我要控制的寄存器地址的话,我不理解0x7A选择的是哪一个寄存器?不知道我哪里理解的不对。请帮忙解答,谢谢。

出0入0汤圆

发表于 2013-7-5 10:54:42 | 显示全部楼层
我找到地方了,看来确实是文档看的不仔细,在SPI那章有解释,多谢楼主了。

出0入0汤圆

 楼主| 发表于 2013-7-5 12:43:06 | 显示全部楼层
robin45853258 发表于 2013-7-5 10:54
我找到地方了,看来确实是文档看的不仔细,在SPI那章有解释,多谢楼主了。 ...

诶,我当时是每一个寄存器的操作我都仔细查看的!

呵呵,找到问题就好,欢迎你进入嵌入式互联网世界!

出0入0汤圆

发表于 2013-7-5 13:25:58 来自手机 | 显示全部楼层
好文章,楼主继续吧

出0入14汤圆

发表于 2013-7-5 14:26:41 | 显示全部楼层
学习了!THANKS!等待楼主后面文章!

出0入0汤圆

发表于 2013-7-5 14:51:05 | 显示全部楼层
很详细!太感谢了

出0入0汤圆

发表于 2013-7-5 15:03:11 | 显示全部楼层
有用 留着

出0入0汤圆

 楼主| 发表于 2013-7-5 21:08:06 | 显示全部楼层
trent5145 发表于 2013-7-5 13:25
好文章,楼主继续吧

后面的文章陆陆续续在今天(2013年)过年的时候都陆陆续续发到论坛上了,你可以搜索以下!
或者点我的头像!

出0入0汤圆

发表于 2013-7-6 17:26:49 | 显示全部楼层
晚上再看.不错

出0入0汤圆

发表于 2013-7-6 17:51:21 | 显示全部楼层
mark,近期准备玩这颗芯片。

出0入0汤圆

发表于 2013-7-6 18:08:35 | 显示全部楼层
写得很详细,非常不错,楼主辛苦了
39楼的情况我也遇到过,数据包长点的,传输量大就很容易发生死机的情况,这个也许跟芯片的硬件流控这些有关系,一直也没有深入研究,毕竟这个东东一般也都是低速应用

出0入0汤圆

发表于 2013-7-6 18:11:54 | 显示全部楼层
markmarkmark

出0入0汤圆

发表于 2013-7-7 12:38:01 | 显示全部楼层
xukai871105 发表于 2013-7-5 21:08
后面的文章陆陆续续在今天(2013年)过年的时候都陆陆续续发到论坛上了,你可以搜索以下!
或者点我的头像 ...

点击头像不能看到你发过的帖子啊?
另外,我有一个问题,为了进行测试,我会将发送到ENC28J60“发送缓存区”里的数据马上通过SPI接口读回来看数据对不对(这个时候我还没有给发送使能),能这样测试吗?我移植了lwip+enc28j60,正在想办法测试,有没有什么好方法,或者好软件?

出0入0汤圆

 楼主| 发表于 2013-7-7 16:57:26 | 显示全部楼层
robin45853258 发表于 2013-7-7 12:38
点击头像不能看到你发过的帖子啊?
另外,我有一个问题,为了进行测试,我会将发送到ENC28J60“发送缓存 ...

1.你可以点击自己的头像看看,可以看到您以前发过的帖子。

2.我猜想你正在进行TDD测试
如果真的是需要这样做的话,在填充发送缓冲区的时(使用写缓冲命令)需要记录起始地址,在填充动作完成之后,再使用读缓冲区命令从刚才记录的起始地址读取一定长度的数据。
3.lwip和enc28j60
我觉得调试网络还是有点运气成分的,我一般一直好驱动之后采用ping命令,尝试能否是否可以达到目标板。如果不介意的话,可以参考别人的例子以节约时间。
建议可以尝试RT-Thread简单有效方便快捷。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-7-7 17:01:26 | 显示全部楼层
wqhzhy5858 发表于 2013-7-6 18:08
写得很详细,非常不错,楼主辛苦了
39楼的情况我也遇到过,数据包长点的,传输量大就很容易发生死 ...

如果是高速的情况下的话,也不会使用STM32这样的芯片。不如使用linux+ARM9以上的方案!

出0入0汤圆

发表于 2013-7-7 22:38:53 | 显示全部楼层
xukai871105 发表于 2013-7-7 16:57
1.你可以点击自己的头像看看,可以看到您以前发过的帖子。

2.我猜想你正在进行TDD测试

恩,一直没注意可以点击图标看主题,现在正在看你之前的帖子。我仿照STM32F103挂载ENC28J60的代码,在STM32F407上做移植,老板的意思要支持几种常见的以太网模块,先支持SPI接口的ENC28J60,感觉SPI以太网调试不像SPI调试NANDFLASH那样,知道FLASH里存储了啥,读出来写回去没有错就OK,网络我还不太熟,也使用过一些网络监控软件,感觉目前都不是我想要的那种测试ENC28J60的工具。

出0入0汤圆

 楼主| 发表于 2013-7-8 11:17:55 | 显示全部楼层
robin45853258 发表于 2013-7-7 22:38
恩,一直没注意可以点击图标看主题,现在正在看你之前的帖子。我仿照STM32F103挂载ENC28J60的代码,在STM ...

我曾经也想过这个问题
例如ENC28J60发送 01 02 03 04 PC机可以收到 01 02 03 04,放过来再来一次便可证明发送和接收都没有问题。

不过这个好像要使用winpcap捕获以太网数据,暂时还不会弄,所以搁置了!

出0入0汤圆

发表于 2013-7-9 10:26:43 | 显示全部楼层
不知enc28j60的中断如何使用?

出0入0汤圆

发表于 2013-7-9 16:57:37 | 显示全部楼层

出0入0汤圆

 楼主| 发表于 2013-7-9 20:14:25 | 显示全部楼层
robin45853258 发表于 2013-7-9 10:26
不知enc28j60的中断如何使用?

如果没有操作系统的话,在中断中改变标志位
如果有操作系统的话,可以发送消息邮箱

出0入0汤圆

发表于 2013-7-9 21:48:03 | 显示全部楼层
持续关注,以后的项目需要用到

出0入4汤圆

发表于 2013-7-11 15:01:38 | 显示全部楼层
xukai871105 发表于 2013-7-5 08:50
#define ENC28J60_READ_CTRL_REG          0x00
/* 读缓冲区 */
#define ENC28J60_READ_BUF_MEM          ...

enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);

请问,这里的0,和0x00两个参数怎么理解?

文档里说发送数据包之前应该发个包控制字节,然后给出了包控制字节的格式,但是没弄明白enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);  怎么就发包控制字节了?

出0入0汤圆

发表于 2013-7-11 19:36:13 | 显示全部楼层
学习了,感谢

出0入0汤圆

 楼主| 发表于 2013-7-12 08:22:23 | 显示全部楼层
dadian 发表于 2013-7-11 15:01
enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);

请问,这里的0,和0x00两个参数怎么理解?

这个我需要查看一下驱动代码,有些细节时间久了逐渐淡忘了!

出0入4汤圆

发表于 2013-7-12 08:41:44 | 显示全部楼层
xukai871105 发表于 2013-7-12 08:22
这个我需要查看一下驱动代码,有些细节时间久了逐渐淡忘了!

谢谢楼主!!!

出0入0汤圆

 楼主| 发表于 2013-7-12 22:09:19 | 显示全部楼层
本帖最后由 xukai871105 于 2013-7-12 22:11 编辑
dadian 发表于 2013-7-12 08:41
谢谢楼主!!!


非常感谢你的细致,虽然我暂时无法解释清楚问题的本质原因,但是基本可以判断问题出在缓冲区的地址分配上。
1.首先我查看了自己的驱动程序,尝试去除该行,但是若去除该行便无法ping目标板
2.查看了rt-threa的的enc28j60的驱动代码,同样保留了该行。

3.查阅了microchip的tcp ip协议栈(enc28j60驱动的出处,我个人也很喜欢使用该公司的芯片),该协议栈我个人并不熟悉,所以还需要花点时间研究。在相似的数据包发送代码中,并没有这句奇怪的语句。
但是在填写发送缓冲区之前,缓冲区的起始地址被增加了1。

顺着这个思路,我进行了以下修改,修正了这个奇怪的问题


我当时这里也存在疑问,但是由于时间和精力的关系最后忽略了这一个细节问题。
感谢你的耐心和细致使我能够发现问题,并及时修正。

回想论坛中很多人问这个稳定不稳定,那个稳定不稳定,其实最靠不住的是自己!当然我自己也靠不住,需要时时刻刻反思。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入4汤圆

发表于 2013-7-12 23:38:41 | 显示全部楼层
谢谢lz这么晚还回帖,手册我也看了,意思是空出一个字节来干别的用,所以起始地址加1是和手册意思相符 。参数0是不是代表寄存器0?具体我也晕了,说不清楚,呵呵

出0入0汤圆

 楼主| 发表于 2013-7-13 11:33:45 | 显示全部楼层
dadian 发表于 2013-7-12 23:38
谢谢lz这么晚还回帖,手册我也看了,意思是空出一个字节来干别的用,所以起始地址加1是和手册意思相符 。参 ...

我再次认真看了一遍,在发送过程中第一个字节并不是数据,而是控制字节,该控制字节可决定自动填充和CRC校验等参数,但是如果该控制字节的最后一位为0的话,选择macon3的参数设置,所有才有了发送一个0x00的故事

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-7-21 15:05:29 | 显示全部楼层
我的ENC28J60温度高呢

出0入0汤圆

发表于 2013-7-21 15:07:01 | 显示全部楼层
rejoice818 发表于 2013-3-9 09:42
mark! ENC424J600未玩过,ENC28J60玩过很久了。支持一下。

能否给点资料,我的ENC28J60温度高呢

出0入0汤圆

 楼主| 发表于 2013-7-21 16:17:55 | 显示全部楼层
liu100149 发表于 2013-7-21 15:05
我的ENC28J60温度高呢

温度高?我刚才还用手摸了一下ENC28J60,基本没有感觉,拿来的温度高!

出0入0汤圆

发表于 2013-7-21 16:34:15 | 显示全部楼层
写得不错!标记备用。

出0入0汤圆

发表于 2013-7-22 12:07:01 | 显示全部楼层
xukai871105 发表于 2013-7-21 16:17
温度高?我刚才还用手摸了一下ENC28J60,基本没有感觉,拿来的温度高!

那有可能是什么原因呢

出0入0汤圆

发表于 2013-7-22 13:07:01 | 显示全部楼层
但还是能工作

出0入0汤圆

发表于 2013-7-22 13:07:33 | 显示全部楼层
就是不知久会出什么情况

出0入0汤圆

 楼主| 发表于 2013-7-22 14:15:52 | 显示全部楼层
liu100149 发表于 2013-7-22 13:07
就是不知久会出什么情况

不常做硬件,还是等待高手回答你吧!

出0入0汤圆

发表于 2013-7-22 15:18:46 | 显示全部楼层
xukai871105 发表于 2013-7-22 14:15
不常做硬件,还是等待高手回答你吧!


你能否把驱动例程发我学习一下

出0入0汤圆

 楼主| 发表于 2013-7-22 20:25:13 | 显示全部楼层
liu100149 发表于 2013-7-22 15:18
你能否把驱动例程发我学习一下

http://www.amobbs.com/thread-5519452-1-1.html
这个帖子的附件里面有ENC28J60的驱动。

更何况,这个帖子就是讲ENC28J60如何使用的????

出0入0汤圆

发表于 2013-7-24 13:11:16 | 显示全部楼层
顶顶

出0入0汤圆

发表于 2013-7-26 22:16:16 | 显示全部楼层
xukai871105 发表于 2013-6-6 20:15
比较抱歉,我还没有使用ENC28J60传输大数据的情况!
是否可以把你的测试环境描述的清楚一点,我有时间一 ...

你好,
我向你请教个问题

出0入0汤圆

发表于 2013-7-26 22:20:08 | 显示全部楼层
xukai871105 发表于 2013-6-6 20:15
比较抱歉,我还没有使用ENC28J60传输大数据的情况!
是否可以把你的测试环境描述的清楚一点,我有时间一 ...

01.<font size="3">unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet)

02.{

03.       unsigned int rxstat;

04.       unsigned int len;

05.

06.       /* 是否收到以太网数据包 */

07.       if( enc28j60Read(EPKTCNT) == 0 )

08.       {

09.              return(0);

10.    }

11.

12.       /* 设置接收缓冲器读指针 */

13.       enc28j60Write(ERDPTL, (NextPacketPtr));

14.       enc28j60Write(ERDPTH, (NextPacketPtr)>>8);

15.

16.  /* 接收数据包结构示例 数据手册43页 */

17.

18.       /* 读下一个包的指针 */

19.       NextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);

20.       NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;

21.

22.       /* 读包的长度 */

23.       len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);

24.       len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;

25.

26.   /* 去除CRC校验部分 */

27.   len-= 4;

28.                  

29.       /* 读取接收状态 */

30.       rxstat  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);

31.       rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;

32.

33.       /* 限制检索的长度  */

34.  if (len > maxlen-1)

35.       {

36.    len = maxlen-1;

37.  }

38.  /* 检查CRC和符号错误 */

39.  /* ERXFCON.CRCEN是默认设置。通常我们不需要检查 */

40.  if ((rxstat & 0x80)==0)

41.       {

42.          //无效的

43.          len = 0;

44.       }

45.       else

46.       {

47.    /* 从接收缓冲器中复制数据包 */

48.    enc28j60ReadBuffer(len, packet);

49.  }

50.

51.  /* 移动接收缓冲区 读指针*/

52.       enc28j60Write(ERXRDPTL, (NextPacketPtr));

53.       enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8);

54.

55.       /* 数据包递减 */

56.       enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);

57.

58.  /* 返回长度 */

59.       return(len);

60.}</font>


下面的我有点看不明白了
enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);这个出现2次我有点看不明白请指点一下


13.       enc28j60Write(ERDPTL, (NextPacketPtr));

14.       enc28j60Write(ERDPTH, (NextPacketPtr)>>8);

15.

16.  /* 接收数据包结构示例 数据手册43页 */

17.

18.       /* 读下一个包的指针 */

19.       NextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);

20.       NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;

21.

22.       /* 读包的长度 */

23.       len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);

24.       len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;

25.














出0入0汤圆

发表于 2013-7-27 09:08:02 | 显示全部楼层
如果单片机集成了mac  在外加一个phy芯片  那么我们还需要配置这些东西吗

出0入0汤圆

发表于 2013-7-27 09:31:29 | 显示全部楼层
对这个我也不太熟呢
现在也正在学呢

出0入0汤圆

 楼主| 发表于 2013-7-27 18:49:26 | 显示全部楼层
liu100149 发表于 2013-7-26 22:20
01.unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet)

02.{

下面的我有点看不明白了
enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);这个出现2次我有点看不明白请指点一下

请查看本帖82楼!

出0入0汤圆

 楼主| 发表于 2013-7-27 18:51:02 | 显示全部楼层
糖糖love丽 发表于 2013-7-27 09:08
如果单片机集成了mac  在外加一个phy芯片  那么我们还需要配置这些东西吗

呵呵!这些配置的内容虽然复杂,但是耐性的对比代码和数据手册也就不那么复杂了!

例如STM32F107,对比数据手册和实例代码,如果刚入手的话也会感觉很复杂的!不过看段时间就好了!

出0入0汤圆

发表于 2013-7-27 20:14:20 | 显示全部楼层
学习, mark
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-24 20:32

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表