0.相关资料
3 寄存器操作实现
ENC28j60的寄存器操作分为2+2+2部分,分别为写寄存器和读寄存器部分,读缓冲区和写缓冲区部分,写PHY寄存器和读PHY寄存器部分。3.1 读写寄存器
读或写寄存器的函数如下: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); }读写寄存器的分为两步,第一步为选定寄存器的BANK编号,第二步使用写命令或读命令,操作指定地址的寄存器。在ENC28J60中,由ECON1中的低两位(BIT1-BIT1)保存BANK编号,ECON1是比较特殊的控制寄存器, 4个BANK均具有该寄存器且该寄存器的地址相同。Enc28j60Bank为全局变量,用于保存当前的BANK编号,如果两次操作控制寄存器在同一个BANK时,该变量保持不变,若两次操作的控制寄存器位于不同的BANK,那么BANK的值会变为新的BANK编号。
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); } } 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; }读控制寄存器实际上就是严格遵守数据手册的操作要求。由于读MAC和MII寄存器时,第一个接收到的字节为无效字节,第二个字节才为有效字节。程序通过寄存器地址的最高位来判断是否为MAC或MII寄存器。写寄存器函数较为简单,第一次字节包括操作码和寄存器地址,第二个字节为数据。在这两个函数中参数op为ENC28J60的指令,或称之为操作码,该指令占据SPI首字节的前3位,参数address为寄存器地址,参数data为寄存器的具体值。
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(); }
3.2 读写缓冲区
读写缓冲区的操作也易于理解的。需要说明的是,两个函数具有相同的输入参数,参数len代表被操作数据的长度,pdata为被操作数据的指针。和寄存器读写函数相似,发送或接收数据之前需要发送特定的操作码。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++; } /* 禁止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(); }
3.3 读写PHY寄存器
PHY寄存器和被ENC28J60控制的LED指示灯有关,控制该寄存器可以控制LED驱动方式和发生相应事件时LED显示方式。一般情况下,一个LED指示灯用于指示网络状态(常亮可理解为网络接通),另一个LED指示灯显示接收活动,有数据输入时产生一个点亮脉冲。PHY是比较特殊的寄存器,先要想一个控制寄存器写入PHY寄存器的地址,再向两个控制寄存器依次写入PHY寄存器的具体数据的高8位和低8位,最后等待PHY寄存器操作完成。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); }