xukai871105 发表于 2013-2-10 19:32:55

ENC28J60学习笔记——AVRNET项目

本帖最后由 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、 MA和MI加以区分。操作这三种不同的寄存器需要不同的组合命令。(2)   寄存器被分布在4个不同的bank中,也就是说存在地址相同的寄存器,但是这些寄存器却位于不同的分区中,在操作寄存器之前必须选中正确的bank。(3)虽然存在4个bank,但是有5个寄存器在4个bank的位置相同,它们是EIE、 EIR、ESTAT、ECON1、ECON2。不言而喻,这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,意味第0个BANK;EHT1为位于BANK1中的控制寄存器,第二个0x20代表BANK地址为1,请注意由于BANK编号被保存在bit6和bit5,所以此处为0x20,绝不是0x10;MACON2为位于bank2的以太网寄存器,第一个数字0x01代表在该BANK中的寄存器地址,第二个数字0x40代表BANK编号,而第三个数字0x80代表该寄存器为以太网寄存器或是PHY寄存器,这些寄存器的操作和控制寄存器有区别。       为了方便寄存器操作,h文件中还定义了寄存器地址操作的掩码,简单而言就是需要查看哪些位,不需要查看哪些位。/* 寄存器地址掩码 */#defineADDR_MASK      0x1F/* 存储区域掩码 */#defineBANK_MASK      0x60/* MAC和MII寄存器掩码*/#defineSPRD_MASK      0x80       另外还有比较特殊的5个控制寄存器,EIE,EIR,ESTAT,ECON2和ECON1/* 关键寄存器 */#defineEIE                     0x1B#defineEIR                     0x1C#defineESTAT                   0x1D#defineECON2                  0x1E#defineECON1                  0x1F2.2 寄存器操作命令       寄存器操作命令也可称为寄存器操作码。为了实现寄存器的操作,ENC28J60定义了6+1个寄存器操作命令(操作码)。操作相关寄存器至少有读寄存器命令,写寄存器命令;发送或接收以太网数据则必有写缓冲区命令或读缓冲区命令;为了加快操作,对于某些控制寄存器而言还可以有置位或者清零某位的命令;最后加上一个软件复位命令,锦上添花。<font size="3">/* 读控制寄存器 */
#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</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 读写寄存器       读或写寄存器的函数如下<font size="3">unsigned char enc28j60Read(unsigned char address)
{
       /* 设定寄存器地址区域 */
       enc28j60SetBank(address);
       /* 读取寄存器值 发送读寄存器命令和地址 */
       return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address);
}
void enc28j60Write(unsigned char address, unsigned char data)
{
       /* 设定寄存器地址区域 */
       enc28j60SetBank(address);
       /* 写寄存器值 发送写寄存器命令和地址 */
       enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data);
}</font>       读写寄存器的分为两步,第一步为选定寄存器的BANK编号,第二步为使用写命令或读命令,操作指定地址的寄存器。在ENC28J60中,由ECON1中的某两位保存BANK编号,ECON1是比较特殊的控制寄存器,在4个BANK中具有该寄存器且该寄存器的地址相同。Enc28j60Bank为全局变量,用于保存当前的BANK编号,如果两次操作控制寄存器在同一个BANK时,该变量保持不变,若两次操作的控制寄存器位于不同的BANK,那么BANK的值会变为新的BANK编号。<font size="3">void enc28j60SetBank(unsigned char address)
{
       /* 计算本次寄存器地址在存取区域的位置 */
       if((address & BANK_MASK) != Enc28j60Bank)
       {
    /* 清除ECON1的BSEL1 BSEL0 详见数据手册15页 */
    enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0));
    /* 请注意寄存器地址的宏定义,bit6 bit5代码寄存器存储区域位置 */
    enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5);
    /* 重新确定当前寄存器存储区域 */
    Enc28j60Bank = (address & BANK_MASK);
       }
}</font><font size="3">unsigned char enc28j60ReadOp(unsigned char op, unsigned char address)
{
       unsigned char dat = 0;
      
       /* CS拉低 使能ENC28J60 */
       ENC28J60_CSL();
       /* 操作码和地址 */
       dat = op | (address & ADDR_MASK);
/* 通过SPI写数据*/
       spi_sendbyte(dat);
/* 通过SPI读出数据 */
       dat = spi_sendbyte(0xFF);
      
/* 如果是MAC和MII寄存器,第一个读取的字节无效,该信息包含在地址的最高位 */
       if(address & 0x80)
       {
            /* 再次通过SPI读取数据 */
    dat = spi_sendbyte(0xFF);
       }
      
/* CS拉高 禁止ENC28J60 */
       ENC28J60_CSH();

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

/* 返回数据 */
       return dat;
}
void enc28j60WriteOp(unsigned char op, unsigned char address, unsigned char data)
{
       unsigned char dat = 0;
/* 使能ENC28J60 */                                                   
       ENC28J60_CSL();      
/* 通过SPI发送 操作码和寄存器地址 */                        
       dat = op | (address & ADDR_MASK);
/* 通过SPI1发送数据 */
       spi_sendbyte(dat);
/* 准备寄存器数值 */                           
       dat = data;
/* 通过SPI发送数据 */
       spi_sendbyte(dat);
/* 禁止ENC28J60 */                     
       ENC28J60_CSH();   
}</font>3.2 读写缓冲区       读写缓冲区的操作也是易于理解的。需要说明的是,两个函数具有相同的输入参数,参数len代表被操作数据的长度,pdata为被操作数据的指针。和寄存器读写函数相似,发送或接收数据之前需要发送特定的操作码。<font size="3">void enc28j60ReadBuffer(unsigned int len, unsigned char* pdata)
{
/* 使能ENC28J60 */
ENC28J60_CSL();
       /* 通过SPI发送读取缓冲区命令*/
       spi_sendbyte(ENC28J60_READ_BUF_MEM);
/* 循环读取 */
       while(len)
       {
    len--;
    /* 读取数据 */
    *pdata = (unsigned char)spi_sendbyte(0);
    /* 地址指针累加 */
    pdata++;
       }
/* 增加字符串结尾 便于操作 */
       *pdata='\0';
/* 禁止ENC28J60 */
       ENC28J60_CSH();
}
void enc28j60WriteBuffer(unsigned int len, unsigned char* pdata)
{
/* 使能ENC28J60 */
ENC28J60_CSL();
       /* 通过SPI发送写取缓冲区命令*/
       spi_sendbyte(ENC28J60_WRITE_BUF_MEM);
      
/* 循环发送 */
       while(len)
       {
            len--;
    /* 发送数据 */
            spi_sendbyte(*pdata);
    /* 地址指针累加 */
            pdata++;
       }

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

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

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

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

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

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

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

       /* 是否收到以太网数据包 */
       if( enc28j60Read(EPKTCNT) == 0 )
       {
            return(0);
    }

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

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

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

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

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

       /* 限制检索的长度*/
if (len > maxlen-1)
       {
    len = maxlen-1;
}
/* 检查CRC和符号错误 */
/* ERXFCON.CRCEN是默认设置。通常我们不需要检查 */
if ((rxstat & 0x80)==0)
       {
          //无效的
          len = 0;
       }
       else
       {
    /* 从接收缓冲器中复制数据包 */
    enc28j60ReadBuffer(len, packet);
}

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

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

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

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

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

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

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

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

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

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

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

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

/* 填入默认值 */
       enc28j60Write(MAIPGL, 0x12);
/* 填入默认值 */
       enc28j60Write(MAIPGH, 0x0C);
/* 填入默认值 */
       enc28j60Write(MABBIPG, 0x15);

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

/* 写入MAC地址 */
       enc28j60Write(MAADR5, macaddr);   
       enc28j60Write(MAADR4, macaddr);
       enc28j60Write(MAADR3, macaddr);
       enc28j60Write(MAADR2, macaddr);
       enc28j60Write(MAADR1, macaddr);
       enc28j60Write(MAADR0, macaddr);

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

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

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

/* 返回BANK0 */   
       enc28j60SetBank(ECON1);

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

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

liangtao 发表于 2013-2-10 21:35:37

年前刚写了一个基于这款芯片的网络模块,MCU用的是nuc120,用的是UDP,效率一直调不满意,只有30KB每秒。。。回头认真学习一下这篇文章,看看有没有可以进一步优化的地方。

zhuhuning 发表于 2013-2-10 22:32:29

兄弟写了一篇好文章 正需要

highmao 发表于 2013-2-10 23:14:37

内容很给力嘛~~

xwkm 发表于 2013-2-10 23:19:46

正准备入ENC28J60

xukai871105 发表于 2013-2-10 23:26:32

zhuhuning 发表于 2013-2-10 22:32 static/image/common/back.gif
兄弟写了一篇好文章 正需要

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

zhuhuning 发表于 2013-2-10 23:28:37

我这几天也在写这个应用 发现提供的源码要想运行起来 还是需要好好研究一下

aworker 发表于 2013-2-11 07:01:29

注释的比较详细!

et009tw 发表于 2013-2-11 07:34:47

謝謝你,註解滿詳細的.

狂奔的蜗牛Y 发表于 2013-2-11 07:48:48

好文章,收下好好研究

xslff 发表于 2013-2-12 08:45:25

此帖必火,顶起来,慢慢学习。

jordonwu 发表于 2013-2-12 09:32:22

学习了,谢谢楼主

1066950103 发表于 2013-2-15 16:28:10

哎!在家看他的数据手册了,还是没感觉。看到这帖我真的太高兴了,看来没白跑网吧啊

yongxiangu 发表于 2013-2-15 20:53:34

写得不错!

mdj-fish 发表于 2013-2-15 22:01:28


看来没白跑网吧啊

shuiyunzhijian 发表于 2013-3-2 22:46:12

学习了,期待接下来的内容。

abcfanyuan 发表于 2013-3-6 00:42:44

不错,收藏

jacktau 发表于 2013-3-7 12:38:17

谢谢分享~~~~~~~~~~~~~

Wxy8030 发表于 2013-3-7 13:28:03

这个片子适合低速小型应用,比如门禁监控类;

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

xukai871105 发表于 2013-3-7 16:53:09

Wxy8030 发表于 2013-3-7 13:28 static/image/common/back.gif
这个片子适合低速小型应用,比如门禁监控类;

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

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

rejoice818 发表于 2013-3-9 09:42:33

mark! ENC424J600未玩过,ENC28J60玩过很久了。支持一下。

ray911 发表于 2013-3-9 13:31:41

刚买了模块,还在摸索怎么用
感谢分享心得,对新人很有帮助......

lhuan 发表于 2013-3-9 22:42:19

标记学习中

犯戒和尚 发表于 2013-3-12 14:32:54

楼主大好人,学习了!

孤独_求败 发表于 2013-3-14 14:43:07

受益匪浅,正在学习中

kevin_me 发表于 2013-3-18 14:59:58

记号留名 要做ENC424J600 参考一下

鼎昇科技 发表于 2013-3-18 15:57:29

正需要,学习了

jackboy 发表于 2013-3-18 16:01:56

這個比較強悍

ganjinming 发表于 2013-3-21 14:54:02

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

sync765 发表于 2013-3-21 15:39:57

xukai871105 发表于 2013-2-10 23:26 static/image/common/back.gif
接着几天还有uIP或是lwIP,http网页控制相关内容!
请关注,有问题请提出,共同交流! ...

好文学习了
很期待下文

cc1989summer 发表于 2013-3-21 16:18:06

收藏了!!!

newywx 发表于 2013-3-21 16:33:54

xukai871105 发表于 2013-2-10 23:26 static/image/common/back.gif
接着几天还有uIP或是lwIP,http网页控制相关内容!
请关注,有问题请提出,共同交流! ...

这个要顶的,必须顶~

49547754 发表于 2013-3-22 14:51:46

只能顶下了

xukai871105 发表于 2013-3-23 14:14:38

newywx 发表于 2013-3-21 16:33 static/image/common/back.gif
这个要顶的,必须顶~

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

cbwdm 发表于 2013-3-23 19:20:07

学习了,继续期待楼主的web服务器、http网页控制相关内容部分。

wwwrt891 发表于 2013-5-30 19:33:23

学习 收藏

Hunter 发表于 2013-5-30 19:42:33

xukai871105 发表于 2013-3-23 14:14 static/image/common/back.gif
一直忙于硕士论文答辩和实习工作交接的事宜,一直没有时间写!
不好意思,但是说到做到,不含糊! ...

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

xukai871105 发表于 2013-5-31 15:50:01

Hunter 发表于 2013-5-30 19:42 static/image/common/back.gif
楼主的代码编辑软件是啥?

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

fengkehy 发表于 2013-6-6 06:40:23

enc28j60在数据量过大时有死机现象 怎么处理

xukai871105 发表于 2013-6-6 20:15:53

fengkehy 发表于 2013-6-6 06:40 static/image/common/back.gif
enc28j60在数据量过大时有死机现象 怎么处理

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

leicai05 发表于 2013-6-6 20:22:10

非常好 值得学习

zyin123 发表于 2013-6-7 12:54:18

楼主给力!!!!!!!!

87hedong87 发表于 2013-6-14 14:55:16

MARK
                        

linjunxiong 发表于 2013-6-20 10:09:35

不知楼主的ENC28J60有没出现接收溢出后一段时间就不能再接收的问题.

xukai871105 发表于 2013-6-20 11:20:36

linjunxiong 发表于 2013-6-20 10:09 static/image/common/back.gif
不知楼主的ENC28J60有没出现接收溢出后一段时间就不能再接收的问题.

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

bsz84 发表于 2013-6-20 13:30:28

支持楼主,期待楼主的大作!

wsxiaoping 发表于 2013-6-20 15:29:46

好文章,值得学习

qhshilin 发表于 2013-6-20 15:43:55

内容很给力嘛~~

linjunxiong 发表于 2013-6-20 19:23:04

xukai871105 发表于 2013-6-20 11:20 static/image/common/back.gif
接收溢出我还没有仔细测试过!
能否相似说明出现问题的 场景,我好复现你的场景! ...

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

robin45853258 发表于 2013-7-3 12:15:24

lwip那段特别期待,楼主加油!

3050311118 发表于 2013-7-3 12:32:26

这个一定要顶下楼主

xukai871105 发表于 2013-7-3 12:54:09

robin45853258 发表于 2013-7-3 12:15 static/image/common/back.gif
lwip那段特别期待,楼主加油!

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

robin45853258 发表于 2013-7-4 19:25:01

xukai871105 发表于 2013-7-3 12:54 static/image/common/back.gif
呵呵 有时间动手学习一下估计便可实现!
资料看了很长时间了!

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

xukai871105 发表于 2013-7-5 08:50:44

robin45853258 发表于 2013-7-4 19:25 static/image/common/back.gif
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

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

robin45853258 发表于 2013-7-5 10:07:14

xukai871105 发表于 2013-7-5 08:50 static/image/common/back.gif
#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选择的是哪一个寄存器?不知道我哪里理解的不对。请帮忙解答,谢谢。

robin45853258 发表于 2013-7-5 10:54:42

我找到地方了,看来确实是文档看的不仔细,在SPI那章有解释,多谢楼主了。{:smile:}

xukai871105 发表于 2013-7-5 12:43:06

robin45853258 发表于 2013-7-5 10:54 static/image/common/back.gif
我找到地方了,看来确实是文档看的不仔细,在SPI那章有解释,多谢楼主了。 ...

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

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

trent5145 发表于 2013-7-5 13:25:58

好文章,楼主继续吧

Pjm2008 发表于 2013-7-5 14:26:41

学习了!THANKS!等待楼主后面文章!

ele-boy 发表于 2013-7-5 14:51:05

很详细!太感谢了

_Kevin_ 发表于 2013-7-5 15:03:11

有用 留着

xukai871105 发表于 2013-7-5 21:08:06

trent5145 发表于 2013-7-5 13:25 static/image/common/back.gif
好文章,楼主继续吧

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

czg94215 发表于 2013-7-6 17:26:49

晚上再看.不错

ilovemysel 发表于 2013-7-6 17:51:21

mark,近期准备玩这颗芯片。

wqhzhy5858 发表于 2013-7-6 18:08:35

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

michael.yang 发表于 2013-7-6 18:11:54

markmarkmark

robin45853258 发表于 2013-7-7 12:38:01

xukai871105 发表于 2013-7-5 21:08 static/image/common/back.gif
后面的文章陆陆续续在今天(2013年)过年的时候都陆陆续续发到论坛上了,你可以搜索以下!
或者点我的头像 ...

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

xukai871105 发表于 2013-7-7 16:57:26

robin45853258 发表于 2013-7-7 12:38 static/image/common/back.gif
点击头像不能看到你发过的帖子啊?
另外,我有一个问题,为了进行测试,我会将发送到ENC28J60“发送缓存 ...

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

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

xukai871105 发表于 2013-7-7 17:01:26

wqhzhy5858 发表于 2013-7-6 18:08 static/image/common/back.gif
写得很详细,非常不错,楼主辛苦了
39楼的情况我也遇到过,数据包长点的,传输量大就很容易发生死 ...

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

robin45853258 发表于 2013-7-7 22:38:53

xukai871105 发表于 2013-7-7 16:57 static/image/common/back.gif
1.你可以点击自己的头像看看,可以看到您以前发过的帖子。

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


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

xukai871105 发表于 2013-7-8 11:17:55

robin45853258 发表于 2013-7-7 22:38 static/image/common/back.gif
恩,一直没注意可以点击图标看主题,现在正在看你之前的帖子。我仿照STM32F103挂载ENC28J60的代码,在STM ...

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

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

robin45853258 发表于 2013-7-9 10:26:43

不知enc28j60的中断如何使用?

yfgww 发表于 2013-7-9 16:57:37

{:smile:}{:smile:}{:smile:}{:smile:}{:smile:}{:smile:}{:smile:}

xukai871105 发表于 2013-7-9 20:14:25

robin45853258 发表于 2013-7-9 10:26 static/image/common/back.gif
不知enc28j60的中断如何使用?

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

jerry840922 发表于 2013-7-9 21:48:03

持续关注,以后的项目需要用到

dadian 发表于 2013-7-11 15:01:38

xukai871105 发表于 2013-7-5 08:50 static/image/common/back.gif
#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);怎么就发包控制字节了?

ZYBing 发表于 2013-7-11 19:36:13

学习了,感谢

xukai871105 发表于 2013-7-12 08:22:23

dadian 发表于 2013-7-11 15:01 static/image/common/back.gif
enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);

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


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

dadian 发表于 2013-7-12 08:41:44

xukai871105 发表于 2013-7-12 08:22 static/image/common/back.gif
这个我需要查看一下驱动代码,有些细节时间久了逐渐淡忘了!

谢谢楼主!!!

xukai871105 发表于 2013-7-12 22:09:19

本帖最后由 xukai871105 于 2013-7-12 22:11 编辑

dadian 发表于 2013-7-12 08:41 static/image/common/back.gif
谢谢楼主!!!

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

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

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


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

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


dadian 发表于 2013-7-12 23:38:41

谢谢lz这么晚还回帖,手册我也看了,意思是空出一个字节来干别的用,所以起始地址加1是和手册意思相符 。参数0是不是代表寄存器0?具体我也晕了,说不清楚,呵呵

xukai871105 发表于 2013-7-13 11:33:45

dadian 发表于 2013-7-12 23:38 static/image/common/back.gif
谢谢lz这么晚还回帖,手册我也看了,意思是空出一个字节来干别的用,所以起始地址加1是和手册意思相符 。参 ...

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

liu100149 发表于 2013-7-21 15:05:29

我的ENC28J60温度高呢

liu100149 发表于 2013-7-21 15:07:01

rejoice818 发表于 2013-3-9 09:42 static/image/common/back.gif
mark! ENC424J600未玩过,ENC28J60玩过很久了。支持一下。

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

xukai871105 发表于 2013-7-21 16:17:55

liu100149 发表于 2013-7-21 15:05 static/image/common/back.gif
我的ENC28J60温度高呢

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

bygreencn 发表于 2013-7-21 16:34:15

写得不错!标记备用。

liu100149 发表于 2013-7-22 12:07:01

xukai871105 发表于 2013-7-21 16:17 static/image/common/back.gif
温度高?我刚才还用手摸了一下ENC28J60,基本没有感觉,拿来的温度高!

那有可能是什么原因呢

liu100149 发表于 2013-7-22 13:07:01

但还是能工作

liu100149 发表于 2013-7-22 13:07:33

就是不知久会出什么情况

xukai871105 发表于 2013-7-22 14:15:52

liu100149 发表于 2013-7-22 13:07 static/image/common/back.gif
就是不知久会出什么情况

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

liu100149 发表于 2013-7-22 15:18:46

xukai871105 发表于 2013-7-22 14:15 static/image/common/back.gif
不常做硬件,还是等待高手回答你吧!

{:smile:}
你能否把驱动例程发我学习一下

xukai871105 发表于 2013-7-22 20:25:13

liu100149 发表于 2013-7-22 15:18 static/image/common/back.gif
你能否把驱动例程发我学习一下

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

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

lsy5110 发表于 2013-7-24 13:11:16

顶顶{:smile:}

liu100149 发表于 2013-7-26 22:16:16

xukai871105 发表于 2013-6-6 20:15 static/image/common/back.gif
比较抱歉,我还没有使用ENC28J60传输大数据的情况!
是否可以把你的测试环境描述的清楚一点,我有时间一 ...

你好,
我向你请教个问题

liu100149 发表于 2013-7-26 22:20:08

xukai871105 发表于 2013-6-6 20:15 static/image/common/back.gif
比较抱歉,我还没有使用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.














糖糖love丽 发表于 2013-7-27 09:08:02

如果单片机集成了mac在外加一个phy芯片那么我们还需要配置这些东西吗

liu100149 发表于 2013-7-27 09:31:29

对这个我也不太熟呢
现在也正在学呢

xukai871105 发表于 2013-7-27 18:49:26

liu100149 发表于 2013-7-26 22:20 static/image/common/back.gif
01.unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet)

02.{


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

请查看本帖82楼!

xukai871105 发表于 2013-7-27 18:51:02

糖糖love丽 发表于 2013-7-27 09:08 static/image/common/back.gif
如果单片机集成了mac在外加一个phy芯片那么我们还需要配置这些东西吗

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

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

wcm_e 发表于 2013-7-27 20:14:20

学习, mark
页: [1] 2 3
查看完整版本: ENC28J60学习笔记——AVRNET项目