ENC28J60学习笔记2

4.ENC28J60写操作

通过ENC28J60发送以太网数据包,操作ENC28J60硬件缓冲区的发送部分即可。每次发送时总是从发送缓冲区的起始地址开始填充数据,数据填充的结束地址和数据包长度有关。设置发送缓冲区大小之后可向发送缓冲区填充数据,即调用ENC28J60_WRITE_BUF_MEM操作命令,接着置位ECON1中的 ECON1_TXRTS位启动发送,并使用等待法不断查询是否发送完毕。基本的思路还是和SPI或UART发送数据相似,即填充数据,启动发送,查询发送完成。写操作的输入参数为数据包的长度len和数据包指针packet,该参数正好和uIP的网络层操作函数相对应。 
[cpp]  view plain copy
  1. void enc28j60PacketSend(unsigned int len, unsigned char* packet)  
  2. {  
  3.     /* 查询发送逻辑复位位 */  
  4.     while((enc28j60Read(ECON1) & ECON1_TXRTS)!= 0);  
  5.     
  6.   /* 设置发送缓冲区起始地址 */      
  7.     enc28j60Write(EWRPTL, TXSTART_INIT & 0xFF);  
  8.     enc28j60Write(EWRPTH, TXSTART_INIT >> 8);  
  9.   
  10.   
  11.     /* 设置发送缓冲区结束地址 该值对应发送数据包长度 */     
  12.     enc28j60Write(ETXNDL, (TXSTART_INIT + len) & 0xFF);  
  13.     enc28j60Write(ETXNDH, (TXSTART_INIT + len) >>8);  
  14.   
  15.   
  16.     /* 发送之前发送控制包格式字 */  
  17.     enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);  
  18.   
  19.   
  20.     /* 通过ENC28J60发送数据包 */  
  21.     enc28j60WriteBuffer(len, packet);  
  22.   
  23.   
  24.     /* 开始发送 */  
  25.     enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);  
  26.   
  27.   
  28.   /* 复位发送逻辑的问题 */  
  29.     if( (enc28j60Read(EIR) & EIR_TXERIF) )  
  30.     {  
  31.         enc28j60SetBank(ECON1);  
  32.     enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS);  
  33.   }  
  34. }  

5 ENC28J60读操作

读操作要比写操作复杂一些。写操作时每次总是从硬件发送缓冲区的起始地址开始操作,而读操作时需要不断修改接收缓冲区的读指针地址,该参数需要通过NextPacketPtr完成,该变量为uint16_t类型的全局变量。读操作时,先通过寄存器查看是否存在以太网数据包,读EPKTCNT寄存器便可返回以太网数据包的个数;若存在以太网数据包则设定读指针的地址,执行读缓冲区操作,ENC28J60的以太网接收数据包中前两个字节为下一个以太网数据包的起始地址,立即保存该参数至NextPacketPtr全局变量;以太网数据包中的后两个字节为该数据包的长度,该长度指从目标MAC地址开始的数据包的长度,进行处理时还需要舍弃最后的4字节CRC校验结果;通过读缓冲区操作码把长度为Len的以太网接收数据包保存至RAM中的某个位置,例如rxtx_buf全局数组。最后根据NextPacketPtr移动读指针以便下次操作,并通过操作ECON2的ECON2_PKTDEC位递减了以太网数据包个数。
[cpp]  view plain copy
  1. unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet)  
  2. {  
  3.     unsigned int rxstat;  
  4.     unsigned int len;  
  5.   
  6.   
  7.     /* 是否收到以太网数据包 */  
  8.     if( enc28j60Read(EPKTCNT) == 0 )  
  9.     {  
  10.         return(0);  
  11.     }  
  12.   
  13.   
  14.     /* 设置接收缓冲器读指针 */  
  15.     enc28j60Write(ERDPTL, (NextPacketPtr));  
  16.     enc28j60Write(ERDPTH, (NextPacketPtr)>>8);  
  17.   
  18.   
  19.   /* 接收数据包结构示例 数据手册43页 */  
  20.   
  21.   
  22.     /* 读下一个包的指针 */  
  23.     NextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);  
  24.     NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;  
  25.   
  26.   
  27.     /* 读包的长度 */  
  28.     len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);  
  29.     len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;  
  30.   
  31.   
  32.    /* 去除CRC校验部分 */  
  33.    len-= 4;       
  34.               
  35.     /* 读取接收状态 */  
  36.     rxstat  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);  
  37.     rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;  
  38.   
  39.   
  40.     /* 限制检索的长度  */    
  41.   if (len > maxlen-1)  
  42.     {  
  43.     len = maxlen-1;  
  44.   }  
  45.   /* 检查CRC和符号错误 */  
  46.   /* ERXFCON.CRCEN是默认设置。通常我们不需要检查 */  
  47.   if ((rxstat & 0x80)==0)  
  48.     {  
  49.        //无效的  
  50.        len = 0;  
  51.     }  
  52.     else  
  53.     {  
  54.     /* 从接收缓冲器中复制数据包 */  
  55.     enc28j60ReadBuffer(len, packet);  
  56.   }  
  57.   
  58.   
  59.   /* 移动接收缓冲区 读指针*/  
  60.     enc28j60Write(ERXRDPTL, (NextPacketPtr));  
  61.     enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8);  
  62.   
  63.   
  64.     /* 数据包递减 */  
  65.     enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);  
  66.   
  67.   
  68.   /* 返回长度 */  
  69.     return(len);  
  70. }  

6 ENC28J60初始化操作

ENC28J60初始化操作内容较多。
第一,进行CS端口的相关配置,即把该端口设置为输出状态,该部分代码可以出现在任何硬件初始化代码中,例如可以把所有的IO操作放入gpio_config中;

第二,进行软件复位,并通过查询ESTAT的ESTAT_CLKRDY标志位确定是否复位完成,初始化NextPacketPtr变量,该变量的初值为发送缓冲区的起始地址;

第三,配置发送和接收缓冲区的区间第四,若干参数配置,特别说明ENC28J60具有自动填充0 的功能,即发送报文长度低于以太网最小报文长度时可以填充0至最小长度;第五,写入MAC地址,由于ENC28J60内部没有全球唯一的MAC地址,所以该地址需要软件填写。但是这种软件填写方式存在缺陷,实际应用中可以含有全球唯一的MAC地址的EEPROM,从EERPOM读取MAC地址并用该地址初始化ENC28J60;第六,初始化中断,并使能接收,ENC28J60含有多个中断,本例只打开全局中断和数据包接收中断。

[cpp]  view plain copy
  1. void enc28j60Init(unsigned char* macaddr)  
  2. {  
  3.   /* CS端口为输出 */  
  4.   DDRB |= (1<<4);  
  5.     
  6.   /* 禁止ENC28J60 */  
  7.   ENC28J60_CSH();  
  8.     /* ENC28J60软件复位 该函数可以改进*/  
  9.     enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);   
  10.   /*查询ESTAT.CLKRDY位*/  
  11.     while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY));  
  12.        
  13.     /* 设置接收缓冲区起始地址 该变量用于每次读取缓冲区时保留下一个包的首地址 */  
  14.     NextPacketPtr = RXSTART_INIT;  
  15.     
  16.   /* 设置接收缓冲区 起始指针*/  
  17.     enc28j60Write(ERXSTL, RXSTART_INIT & 0xFF);  
  18.     enc28j60Write(ERXSTH, RXSTART_INIT >> 8);  
  19.   
  20.   
  21.   /* 设置接收缓冲区 读指针*/   
  22.     enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF);  
  23.     enc28j60Write(ERXRDPTH, RXSTART_INIT>>8);  
  24.   
  25.   
  26.   /* 设置接收缓冲区 结束指针 */  
  27.     enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF);  
  28.     enc28j60Write(ERXNDH, RXSTOP_INIT>>8);  
  29.   
  30.   
  31.     /* 设置发送缓冲区 起始指针 */  
  32.     enc28j60Write(ETXSTL, TXSTART_INIT&0xFF);  
  33.     enc28j60Write(ETXSTH, TXSTART_INIT>>8);  
  34.     /* 设置发送缓冲区 结束指针 */  
  35.     enc28j60Write(ETXNDL, TXSTOP_INIT&0xFF);  
  36.     enc28j60Write(ETXNDH, TXSTOP_INIT>>8);  
  37.   
  38.   
  39.   /* 使能单播过滤 使能CRC校验 使能 格式匹配自动过滤*/  
  40.     enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN);  
  41.     enc28j60Write(EPMM0, 0x3f);  
  42.     enc28j60Write(EPMM1, 0x30);  
  43.     enc28j60Write(EPMCSL, 0xf9);  
  44.     enc28j60Write(EPMCSH, 0xf7);  
  45.   
  46.   
  47.   /* 使能MAC接收 允许MAC发送暂停控制帧 当接收到暂停控制帧时停止发送*/  
  48.   /* 数据手册34页 */  
  49.     enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);  
  50.   
  51.   
  52.   /* 退出复位状态 */  
  53.     enc28j60Write(MACON2, 0x00);  
  54.   
  55.   
  56.   /* 用0填充所有短帧至60字节长 并追加一个CRC 发送CRC使能 帧长度校验使能 MAC全双工使能*/  
  57.     /* 提示 由于ENC28J60不支持802.3的自动协商机制, 所以对端的网络卡需要强制设置为全双工 */  
  58.     enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN|MACON3_FULDPX);  
  59.   
  60.   
  61.   /* 填入默认值 */  
  62.     enc28j60Write(MAIPGL, 0x12);  
  63.   /* 填入默认值 */  
  64.     enc28j60Write(MAIPGH, 0x0C);  
  65.   /* 填入默认值 */  
  66.     enc28j60Write(MABBIPG, 0x15);  
  67.   
  68.   
  69.   /* 最大帧长度 */  
  70.     enc28j60Write(MAMXFLL, MAX_FRAMELEN & 0xFF);      
  71.     enc28j60Write(MAMXFLH, MAX_FRAMELEN >> 8);  
  72.   
  73.   
  74.   /* 写入MAC地址 */  
  75.     enc28j60Write(MAADR5, macaddr[0]);    
  76.     enc28j60Write(MAADR4, macaddr[1]);  
  77.     enc28j60Write(MAADR3, macaddr[2]);  
  78.     enc28j60Write(MAADR2, macaddr[3]);  
  79.     enc28j60Write(MAADR1, macaddr[4]);  
  80.     enc28j60Write(MAADR0, macaddr[5]);  
  81.     
  82.     /* 配置PHY为全双工  LEDB为拉电流 */  
  83.     enc28j60PhyWrite(PHCON1, PHCON1_PDPXMD);  
  84.     
  85.   /* LED状态 */  
  86.   enc28j60PhyWrite(PHLCON,0x0476);    
  87.     
  88.   /* 半双工回环禁止 */  
  89.     enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS);  
  90.     
  91.   /* 返回BANK0 */   
  92.     enc28j60SetBank(ECON1);  
  93.     
  94.   /* 使能中断 全局中断 接收中断 接收错误中断 */  
  95.     enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE|EIE_RXERIE);  
  96.   
  97.   
  98.   /* 接收使能位 */  
  99.     enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);  
  100. }  

7 总结

ENC28J60的驱动编写算是比较复杂的。但是回过头来看看,其他的以太网驱动芯片的操作和ENC28J60的操作类似,其操作的核心便是4KB的硬件缓冲区。本例不能给出合适的运行范例,因为以太网驱动芯片要配合以太网协议栈来实现,而以太网协议栈内容很多涉及非常多的基础知识。ENC28J60的驱动是以太网协议栈实现的基础,通过ENC28J60还将会分析uIP协议栈,lwIP协议栈的应用等。

你可能感兴趣的:(ENC28J60学习笔记2)