/*************笔记****************
1、ENC28J60_中文手册.pdf
2、控制寄存器的地址定义是一个组合地址:
0-4bit 寄存器地址
5-6bit 块区编号
7bit MAC/PHY indicator
3、
4、
5、
***********************************/
#include "main.h"
#include "enc28j60.h"
#include "cmsis_os.h"

static uint8_t Enc28j60Bank;
static uint32_t NextPacketPtr;

#define ENC28J60_CS(N) HAL_GPIO_WritePin(ENC28J60_CS_GPIO_Port,ENC28J60_CS_Pin,(N)?GPIO_PIN_SET:GPIO_PIN_RESET)
/*********Start*************************底层命令*******************************Start*****************/
/*********************************************
函数名:SPI_ReadWrite
功 能:SPI读写1byte数据
形 参:pTxData--要发送的字节
返回值:收到的字节
备 注:CubeMX启动SPI配置,分频根据系统推荐。
!!!如果移植,需要在此填写SPI读写函数!!!
**********************************************/
extern SPI_HandleTypeDef hspi1;
static uint8_t SPI_ReadWrite(uint8_t pTxData)
{
uint8_t pRxData;
HAL_SPI_TransmitReceive(&hspi1, &pTxData, &pRxData, sizeof(pTxData), 10);
return pRxData;
}
/*********************************************
函数名:ENC28J60_ReadOp
功 能:从器件寄存器读取状态
形 参:op--操作码 address--控制寄存器地址
返回值:寄存器状态值
备 注:命令时序图--29页
**********************************************/
uint8_t ENC28J60_ReadOp(uint8_t op, uint8_t address)
{
uint8_t dat = 0;

ENC28J60_CS(0);

dat = op | (address & ADDR_MASK); //重构操作码和地址,参见手册第28页。
SPI_ReadWrite(dat);

dat = SPI_ReadWrite(0xFF);
//如果需要,进行虚拟读取(对于MAC和MII,请参见手册第29页)
if(address & 0x80)
{
dat = SPI_ReadWrite(0xFF);
}
// release CS
ENC28J60_CS(1);
return dat;
}

/*********************************************
函数名:ENC28J60_WriteOp
功 能:向器件寄存器写入1byte
形 参:op--操作码 address--控制寄存器地址 data--数据字节
返回值:
备 注:
**********************************************/
void ENC28J60_WriteOp(uint8_t op, uint8_t address, uint8_t data)
{
uint8_t dat = 0;

ENC28J60_CS(0);
dat = op | (address & ADDR_MASK);//重构操作码和地址,参见手册第28页。
SPI_ReadWrite(dat);

SPI_ReadWrite(data);//写入数据
ENC28J60_CS(1);
}

/*********************************************
函数名:ENC28J60_ReadBuffer
功 能:读取缓冲存储器的数据
形 参:len--长度 data--存放数据的指针
返回值:
备 注:
**********************************************/
void ENC28J60_ReadBuffer(uint32_t len, uint8_t* data)
{
ENC28J60_CS(0);
SPI_ReadWrite(ENC28J60_READ_BUF_MEM);
while(len)
{
len--;
*data = (uint8_t)SPI_ReadWrite(0);
data++;
}
*data = '\0';
ENC28J60_CS(1);
}
/*********************************************
函数名:ENC28J60_WriteBuffer
功 能:向缓冲存储器写入数据
形 参:len--字节长度 data--数据内容的指针
返回值:
备 注:
**********************************************/
void ENC28J60_WriteBuffer(uint32_t len, uint8_t* data)
{
ENC28J60_CS(0);
SPI_ReadWrite(ENC28J60_WRITE_BUF_MEM);
while(len)
{
len--;
SPI_ReadWrite(*data);
data++;
}
ENC28J60_CS(1);
}
/*********************************************
函数名:ENC28J60_SetBank
功 能:设置块区
形 参:
返回值:
备 注:块区编号在寄存器地址的bit6 bit5
**********************************************/
void ENC28J60_SetBank(uint8_t address)
{
/*计算本次寄存器地址在存取区域的位置*/
if((address & BANK_MASK) != Enc28j60Bank)
{
/*清除ECON1的BSEL1 BSEL0 详见数据手册15页*/
ENC28J60_WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1 | ECON1_BSEL0));
/*请注意寄存器地址的宏定义,bit6 bit5为寄存器存储区域位置*/
ENC28J60_WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK) >> 5);
/*重新确定当前寄存器存储区域*/
Enc28j60Bank = (address & BANK_MASK);
}
}
/*********END*************************底层命令*******************************END*****************/




/*********Start*************************中层命令*******************************Start*****************/
/*********************************************
函数名:ENC28J60_Read
功 能:向指定块区的寄存器地址读取状态值
形 参:address--寄存器地址
返回值:
备 注:
**********************************************/
uint8_t ENC28J60_Read(uint8_t address)
{
// 设定寄存器地址区域
ENC28J60_SetBank(address);
// 读取寄存器值--发送读寄存器命令和地址
return ENC28J60_ReadOp(ENC28J60_READ_CTRL_REG, address);
}

/*********************************************
函数名:ENC28J60_Write
功 能:向指定块区的寄存器地址写入数据
形 参:address--寄存器地址
返回值:
备 注:
**********************************************/
void ENC28J60_Write(uint8_t address, uint8_t data)
{
// 设定寄存器地址区域
ENC28J60_SetBank(address);
// 写寄存器值--发送写寄存器命令和地址
ENC28J60_WriteOp(ENC28J60_WRITE_CTRL_REG, address, data);
}
/*********END*************************中层命令*******************************END*****************/


/*********************************************
函数名:ENC28J60_PhyWrite
功 能:向PHY寄存器地址写入2byte
形 参:address--寄存器地址 data--数据字节
返回值:
备 注:详见手册21页
**********************************************/
void ENC28J60_PhyWrite(uint8_t address, uint16_t data)
{
uint16_t retry = 0;
//向MIREGADR写入地址
ENC28J60_Write(MIREGADR, address);
//写入低8位数据
ENC28J60_Write(MIWRL, data);
//写入高8位数据
ENC28J60_Write(MIWRH, data >> 8);
//等待PHY寄存器写入完成
while(ENC28J60_Read(MISTAT) & MISTAT_BUSY && retry < 0XFFF)
{
retry++;
}
}

/*********************************************
函数名:ENC28J60_PhyWrite
功 能:从PHY寄存器地址读取1byte
形 参:address--寄存器地址 data--数据字节
返回值:
备 注:详见手册21页
**********************************************/
uint16_t ENC28J60_PhyRead(uint8_t address)
{
uint16_t retry = 0;
uint16_t data;
//向MIREGADR写入地址
ENC28J60_Write(MIREGADR, address);
ENC28J60_Write(MICMD, MICMD_MIIRD);//MII 读使能位
//等待PHY寄存器操作完毕
while((ENC28J60_Read(MISTAT) & MISTAT_BUSY) && retry < 0XFFF)
{
retry++;
}
ENC28J60_Write(MICMD, 0x00);//复位
data = ENC28J60_Read(MIRDH) << 8;
data |= ENC28J60_Read(MIRDL);
return data;
}

/*********************************************
函数名:ENC28J60_clkout
功 能:将enc28j60第三引脚的时钟输出改为:
形 参:
返回值:
备 注:(本例程该引脚NC,没用到) enc28j60clkout(2);//from 6.25MHz to 12.5MHz
**********************************************/
void ENC28J60_clkout(uint8_t clk)
{
//setup clkout: 2 is 12.5MHz:
ENC28J60_Write(ECOCON, clk & 0x7);
}


/*********************************************
函数名:ENC28J60_getrev
功 能:读取器件版本号
形 参:
返回值:版本号
备 注:在EREVID内也存储了版本信息。
EREVID 是一个只读控制寄存器。
其中包含一个5位标识符,用来标识器件特定硅片的版本号
**********************************************/
uint8_t ENC28J60_getrev(void)
{
return ENC28J60_Read(EREVID);
}



/*********************************************
函数名:ENC28J60_Init
功 能:初始化
形 参:
返回值:
备 注:
**********************************************/
int ENC28J60_Init(uint8_t* macaddr)
{
uint16_t retry = 0;
ENC28J60_WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); //软件复位
while(!(ENC28J60_Read(ESTAT)&ESTAT_CLKRDY) && retry < 500) //等待时钟稳定
{
retry++;
osDelay(1);
};
if(retry >= 500)
return 1; //ENC28J60初始化失败

// 设置接收缓冲区起始地址
NextPacketPtr = RXSTART_INIT; //数据包起始位赋值
// 设置接收缓冲区 起始指针
ENC28J60_Write(ERXSTL, RXSTART_INIT & 0xFF);
ENC28J60_Write(ERXSTH, RXSTART_INIT >> 8);
// 设置接收缓冲区 读指针
ENC28J60_Write(ERXRDPTL, RXSTART_INIT & 0xFF);
ENC28J60_Write(ERXRDPTH, RXSTART_INIT >> 8);
// 设置接收缓冲区 结束指针
ENC28J60_Write(ERXNDL, RXSTOP_INIT & 0xFF);
ENC28J60_Write(ERXNDH, RXSTOP_INIT >> 8);
/* 设置发送缓冲区 起始指针 */
ENC28J60_Write(ETXSTL, TXSTART_INIT & 0xFF);
ENC28J60_Write(ETXSTH, TXSTART_INIT >> 8);
/* 设置发送缓冲区 结束指针 */
ENC28J60_Write(ETXNDL, TXSTOP_INIT & 0xFF);
ENC28J60_Write(ETXNDH, TXSTOP_INIT >> 8);
// do bank 1 stuff, packet filter:
// For broadcast packets we allow only ARP packtets
// All other packets should be unicast only for our mac (MAADR)
//
// The pattern to match on is therefore
// Type ETH.DST
// ARP BROADCAST
// 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9
// in binary these poitions are:11 0000 0011 1111
// This is hex 303F->EPMM0=0x3f,EPMM1=0x30
/* 使能单播过滤 使能CRC校验 使能 格式匹配自动过滤*/
ENC28J60_Write(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN);
ENC28J60_Write(EPMM0, 0x3f);
ENC28J60_Write(EPMM1, 0x30);
ENC28J60_Write(EPMCSL, 0xf9);
ENC28J60_Write(EPMCSH, 0xf7);
/* 使能MAC接收 允许MAC发送暂停控制帧 当接收到暂停控制帧时停止发送*/
ENC28J60_Write(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
// 退出复位状态
ENC28J60_Write(MACON2, 0x00);

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

// 设置帧间间隙 (non-back-to-back)
ENC28J60_Write(MAIPGL, 0x12);
ENC28J60_Write(MAIPGH, 0x0C);

// 设置帧间间隙 (back-to-back)
ENC28J60_Write(MABBIPG, 0x15);

// 设置控制器将接受的最大数据包大小
// 不要发送超过max_framelen的数据包:
ENC28J60_Write(MAMXFLL, MAX_FRAMELEN & 0xFF);
ENC28J60_Write(MAMXFLH, MAX_FRAMELEN >> 8);

// 写入MAC地址
// NOTE: ENC28J60中的MAC地址是向后字节
ENC28J60_Write(MAADR5, macaddr[0]);
ENC28J60_Write(MAADR4, macaddr[1]);
ENC28J60_Write(MAADR3, macaddr[2]);
ENC28J60_Write(MAADR2, macaddr[3]);
ENC28J60_Write(MAADR1, macaddr[4]);
ENC28J60_Write(MAADR0, macaddr[5]);

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

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

// 切换到 bank 0
ENC28J60_SetBank(ECON1);

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

// 使能数据包接收位
ENC28J60_WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);


if(ENC28J60_Read(MAADR5) == macaddr[0])
return 0; //初始化成功
else
return 1;

}



/*********Start********************************以太网层*******************************Start*****************/
/*********************************************
函数名:ENC28J60_PacketSend
功 能:发送一次数据包
形 参:len--字节长度 packet--数据包指针
返回值:
备 注:参见手册42页
**********************************************/
void ENC28J60_PacketSend(uint32_t len, uint8_t* packet)
{
// 设置发送缓冲区起始地址
ENC28J60_Write(EWRPTL, TXSTART_INIT & 0xFF);
ENC28J60_Write(EWRPTH, TXSTART_INIT >> 8);

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

// 发送之前发送控制包格式字 (0x00 表示使用Macon3设置)
ENC28J60_WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);

// 将数据包发送到ENC28J60传输缓冲区
ENC28J60_WriteBuffer(len, packet);

// 将传输缓冲区的内容发送到网络层
ENC28J60_WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);

// 重置传输逻辑问题. See Rev. B4 Silicon Errata point 12.
if( (ENC28J60_Read(EIR) & EIR_TXERIF) )
{
ENC28J60_WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS);
}
}

// Gets a packet from the network receive buffer, if one is available.
// The packet will by headed by an ethernet header.
/*********************************************
函数名:ENC28J60_PacketReceive
功 能:读取一次数据包
形 参:maxlen--检索到的数据包的最大可接受长度 packet--存储数据包数据的指针。
返回值:如果检索到数据包,则以字节为单位的数据包长度,否则为零。
备 注:接收数据包结构示例 器件手册45页
**********************************************/
uint32_t ENC28J60_PacketReceive(uint32_t maxlen, uint8_t* packet)
{
uint32_t rxstat;
uint32_t len;

// 检查是否已接收和缓冲数据包
//if( !(ENC28J60_Read(EIR) & EIR_PKTIF) ){
// 以上不起作用. See Rev. B4 Silicon Errata point 6.
if( ENC28J60_Read(EPKTCNT) == 0 ) //收到的以太网数据包长度
{
return(0);
}

// 将读取指针设置为接收数据包的起始位置 缓冲器读指针
ENC28J60_Write(ERDPTL, (NextPacketPtr));
ENC28J60_Write(ERDPTH, (NextPacketPtr) >> 8);

// 读取下一个数据包指针
NextPacketPtr = ENC28J60_ReadOp(ENC28J60_READ_BUF_MEM, 0);
NextPacketPtr |= ENC28J60_ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;

/************[接收状态向量-->0-15bit]****************/
// 读取数据包长度 (see datasheet page 46)
len = ENC28J60_ReadOp(ENC28J60_READ_BUF_MEM, 0);
len |= ENC28J60_ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;
len -= 4; //删除CRC部分

/************[接收状态向量-->16-31bit]****************/
// 读取接收状态 (see datasheet page 45)
rxstat = ENC28J60_ReadOp(ENC28J60_READ_BUF_MEM, 0);
rxstat |= ENC28J60_ReadOp(ENC28J60_READ_BUF_MEM, 0) << 8;
// 限制检索长度
if (len > maxlen - 1)
{
len = maxlen - 1;
}

// 检查CRC是否错误 (see datasheet page 46, table 7-3):
// The ERXFCON.CRCEN is set by default. 通常我们不需要检查这个
if ((rxstat & 0x80) == 0) //表示数据包具有有效的 CRC,无符号错误。
{
len = 0;
}
else //
{

ENC28J60_ReadBuffer(len, packet);// 从接收缓冲区复制数据包
}

// 将RX读取指针移动到下一个接收数据包的开始处
// 这会释放我们刚刚读出的内存
ENC28J60_Write(ERXRDPTL, (NextPacketPtr));
ENC28J60_Write(ERXRDPTH, (NextPacketPtr) >> 8);

// 减少数据包计数器表明我们已完成此数据包的处理
ENC28J60_WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
return(len);
}
/*********END*************************以太网层*******************************END*****************/
#ifndef __ENC28J60_H
#define __ENC28J60_H

#include "stm32f1xx_hal.h"

// ENC28J60控制寄存器
// 控制寄存器的地址定义是一个组合地址,
// 存器地址、块区编号、[Ethernet/MAC/PHY] 分别在指定bit中,如下
// - 寄存器地址 (bits 0-4)
// - 块区编号 (bits 5-6)
// - MAC/PHY indicator (bit 7)

#define ADDR_MASK 0x1F //寄存器地址掩码
#define BANK_MASK 0x60 //存储区域掩码
#define SPRD_MASK 0x80 //MAC和MII寄存器掩码

/*************笔记****************
1、所有块区公用的寄存器地址
2、关键寄存器
***********************************/
#define EIE 0x1B
#define EIR 0x1C
#define ESTAT 0x1D
#define ECON2 0x1E
#define ECON1 0x1F
// Bank 0 registers
#define ERDPTL (0x00|0x00)
#define ERDPTH (0x01|0x00)
#define EWRPTL (0x02|0x00)
#define EWRPTH (0x03|0x00)
#define ETXSTL (0x04|0x00)
#define ETXSTH (0x05|0x00)
#define ETXNDL (0x06|0x00)
#define ETXNDH (0x07|0x00)
#define ERXSTL (0x08|0x00)
#define ERXSTH (0x09|0x00)
#define ERXNDL (0x0A|0x00)
#define ERXNDH (0x0B|0x00)
//ERXWRPTH:ERXWRPTL 寄存器定义硬件向FIFO 中
//的哪个位置写入其接收到的字节。 指针是只读的,在成
//功接收到一个数据包后,硬件会自动更新指针。 指针可
//用于判断FIFO 内剩余空间的大小。
#define ERXRDPTL (0x0C|0x00)
#define ERXRDPTH (0x0D|0x00)
#define ERXWRPTL (0x0E|0x00)
#define ERXWRPTH (0x0F|0x00)
#define EDMASTL (0x10|0x00)
#define EDMASTH (0x11|0x00)
#define EDMANDL (0x12|0x00)
#define EDMANDH (0x13|0x00)
#define EDMADSTL (0x14|0x00)
#define EDMADSTH (0x15|0x00)
#define EDMACSL (0x16|0x00)
#define EDMACSH (0x17|0x00)
// Bank 1 registers
#define EHT0 (0x00|0x20)
#define EHT1 (0x01|0x20)
#define EHT2 (0x02|0x20)
#define EHT3 (0x03|0x20)
#define EHT4 (0x04|0x20)
#define EHT5 (0x05|0x20)
#define EHT6 (0x06|0x20)
#define EHT7 (0x07|0x20)
#define EPMM0 (0x08|0x20)
#define EPMM1 (0x09|0x20)
#define EPMM2 (0x0A|0x20)
#define EPMM3 (0x0B|0x20)
#define EPMM4 (0x0C|0x20)
#define EPMM5 (0x0D|0x20)
#define EPMM6 (0x0E|0x20)
#define EPMM7 (0x0F|0x20)
#define EPMCSL (0x10|0x20)
#define EPMCSH (0x11|0x20)
#define EPMOL (0x14|0x20)
#define EPMOH (0x15|0x20)
#define EWOLIE (0x16|0x20)
#define EWOLIR (0x17|0x20)
#define ERXFCON (0x18|0x20)
#define EPKTCNT (0x19|0x20)
// Bank 2 registers
#define MACON1 (0x00|0x40|0x80)
#define MACON2 (0x01|0x40|0x80)
#define MACON3 (0x02|0x40|0x80)
#define MACON4 (0x03|0x40|0x80)
#define MABBIPG (0x04|0x40|0x80)
#define MAIPGL (0x06|0x40|0x80)
#define MAIPGH (0x07|0x40|0x80)
#define MACLCON1 (0x08|0x40|0x80)
#define MACLCON2 (0x09|0x40|0x80)
#define MAMXFLL (0x0A|0x40|0x80)
#define MAMXFLH (0x0B|0x40|0x80)
#define MAPHSUP (0x0D|0x40|0x80)
#define MICON (0x11|0x40|0x80)
#define MICMD (0x12|0x40|0x80)
#define MIREGADR (0x14|0x40|0x80)
#define MIWRL (0x16|0x40|0x80)
#define MIWRH (0x17|0x40|0x80)
#define MIRDL (0x18|0x40|0x80)
#define MIRDH (0x19|0x40|0x80)
// Bank 3 registers
#define MAADR1 (0x00|0x60|0x80)
#define MAADR0 (0x01|0x60|0x80)
#define MAADR3 (0x02|0x60|0x80)
#define MAADR2 (0x03|0x60|0x80)
#define MAADR5 (0x04|0x60|0x80)
#define MAADR4 (0x05|0x60|0x80)
#define EBSTSD (0x06|0x60)
#define EBSTCON (0x07|0x60)
#define EBSTCSL (0x08|0x60)
#define EBSTCSH (0x09|0x60)
#define MISTAT (0x0A|0x60|0x80)
#define EREVID (0x12|0x60)
#define ECOCON (0x15|0x60)
#define EFLOCON (0x17|0x60)
#define EPAUSL (0x18|0x60)
#define EPAUSH (0x19|0x60)
// PHY registers
#define PHCON1 0x00
#define PHSTAT1 0x01
#define PHHID1 0x02
#define PHHID2 0x03
#define PHCON2 0x10
#define PHSTAT2 0x11
#define PHIE 0x12
#define PHIR 0x13
#define PHLCON 0x14

// ENC28J60 ERXFCON Register Bit Definitions
#define ERXFCON_UCEN 0x80
#define ERXFCON_ANDOR 0x40
#define ERXFCON_CRCEN 0x20
#define ERXFCON_PMEN 0x10
#define ERXFCON_MPEN 0x08
#define ERXFCON_HTEN 0x04
#define ERXFCON_MCEN 0x02
#define ERXFCON_BCEN 0x01
// ENC28J60 EIE Register Bit Definitions
#define EIE_INTIE 0x80
#define EIE_PKTIE 0x40
#define EIE_DMAIE 0x20
#define EIE_LINKIE 0x10
#define EIE_TXIE 0x08
#define EIE_WOLIE 0x04
#define EIE_TXERIE 0x02
#define EIE_RXERIE 0x01
// ENC28J60 EIR Register Bit Definitions
#define EIR_PKTIF 0x40
#define EIR_DMAIF 0x20
#define EIR_LINKIF 0x10
#define EIR_TXIF 0x08
#define EIR_WOLIF 0x04
#define EIR_TXERIF 0x02
#define EIR_RXERIF 0x01
// ENC28J60 ESTAT Register Bit Definitions
#define ESTAT_INT 0x80
#define ESTAT_LATECOL 0x10
#define ESTAT_RXBUSY 0x04
#define ESTAT_TXABRT 0x02
#define ESTAT_CLKRDY 0x01
// ENC28J60 ECON2 Register Bit Definitions
#define ECON2_AUTOINC 0x80
#define ECON2_PKTDEC 0x40
#define ECON2_PWRSV 0x20
#define ECON2_VRPS 0x08
// ENC28J60 ECON1 Register Bit Definitions
#define ECON1_TXRST 0x80
#define ECON1_RXRST 0x40
#define ECON1_DMAST 0x20
#define ECON1_CSUMEN 0x10
#define ECON1_TXRTS 0x08
#define ECON1_RXEN 0x04
#define ECON1_BSEL1 0x02
#define ECON1_BSEL0 0x01
// ENC28J60 MACON1 Register Bit Definitions
#define MACON1_LOOPBK 0x10
#define MACON1_TXPAUS 0x08
#define MACON1_RXPAUS 0x04
#define MACON1_PASSALL 0x02
#define MACON1_MARXEN 0x01
// ENC28J60 MACON2 Register Bit Definitions
#define MACON2_MARST 0x80
#define MACON2_RNDRST 0x40
#define MACON2_MARXRST 0x08
#define MACON2_RFUNRST 0x04
#define MACON2_MATXRST 0x02
#define MACON2_TFUNRST 0x01
// ENC28J60 MACON3 Register Bit Definitions
#define MACON3_PADCFG2 0x80
#define MACON3_PADCFG1 0x40
#define MACON3_PADCFG0 0x20
#define MACON3_TXCRCEN 0x10
#define MACON3_PHDRLEN 0x08
#define MACON3_HFRMLEN 0x04
#define MACON3_FRMLNEN 0x02
#define MACON3_FULDPX 0x01
// ENC28J60 MICMD Register Bit Definitions
#define MICMD_MIISCAN 0x02
#define MICMD_MIIRD 0x01
// ENC28J60 MISTAT Register Bit Definitions
#define MISTAT_NVALID 0x04
#define MISTAT_SCAN 0x02
#define MISTAT_BUSY 0x01
// ENC28J60 PHY PHCON1 Register Bit Definitions
#define PHCON1_PRST 0x8000
#define PHCON1_PLOOPBK 0x4000
#define PHCON1_PPWRSV 0x0800
#define PHCON1_PDPXMD 0x0100
// ENC28J60 PHY PHSTAT1 Register Bit Definitions
#define PHSTAT1_PFDPX 0x1000
#define PHSTAT1_PHDPX 0x0800
#define PHSTAT1_LLSTAT 0x0004
#define PHSTAT1_JBSTAT 0x0002
// ENC28J60 PHY PHCON2 Register Bit Definitions
#define PHCON2_FRCLINK 0x4000
#define PHCON2_TXDIS 0x2000
#define PHCON2_JABBER 0x0400
#define PHCON2_HDLDIS 0x0100

// ENC28J60 Packet Control Byte Bit Definitions
#define PKTCTRL_PHUGEEN 0x08
#define PKTCTRL_PPADEN 0x04
#define PKTCTRL_PCRCEN 0x02
#define PKTCTRL_POVERRIDE 0x01

// SPI操作代码--ENC28J60_中文手册.pdf--28页
#define ENC28J60_READ_CTRL_REG 0x00 //读控制寄存器 (RCR)
#define ENC28J60_READ_BUF_MEM 0x3A //读缓冲器 (RBM)
#define ENC28J60_WRITE_CTRL_REG 0x40 //写控制寄存器 (WCR)
#define ENC28J60_WRITE_BUF_MEM 0x7A //写缓冲器 (WBM)
#define ENC28J60_BIT_FIELD_SET 0x80 //位域置 1 (BFS)
#define ENC28J60_BIT_FIELD_CLR 0xA0 //位域清零 (BFC)
#define ENC28J60_SOFT_RESET 0xFF //系统命令(软件复位)(SC)

// The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata
// buffer boundaries applied to internal 8K ram
// the entire available packet buffer space is allocated
//
// 接收缓冲区起始地址/
#define RXSTART_INIT 0x0
// 接收缓冲区停止地址
#define RXSTOP_INIT (0x1FFF-0x0600-1)
// 发送缓冲区起始地址 发送缓冲区大小约1500字节
#define TXSTART_INIT (0x1FFF-0x0600)
// 发送缓冲区停止地址
#define TXSTOP_INIT 0x1FFF
//
// max frame length which the conroller will accept:
#define MAX_FRAMELEN 1500 // (note: maximum ethernet frame length would be 1518)
//#define MAX_FRAMELEN 600





uint8_t ENC28J60_ReadOp(uint8_t op, uint8_t address);
void ENC28J60_WriteOp(uint8_t op, uint8_t address, uint8_t data);
void ENC28J60_ReadBuffer(uint32_t len, uint8_t* data);
void ENC28J60_WriteBuffer(uint32_t len, uint8_t* data);
void ENC28J60_SetBank(uint8_t address);
uint8_t ENC28J60_Read(uint8_t address);
void ENC28J60_Write(uint8_t address, uint8_t data);
void ENC28J60_PhyWrite(uint8_t address, uint16_t data);
uint16_t ENC28J60_PhyRead(uint8_t address);
void ENC28J60_clkout(uint8_t clk);
int ENC28J60_Init(uint8_t* macaddr);
uint8_t ENC28J60_getrev(void);
void ENC28J60_PacketSend(uint32_t len, uint8_t* packet);
uint32_t ENC28J60_PacketReceive(uint32_t maxlen, uint8_t* packet);

#endif /* __ENC28J60_H */

​ENC28J60中文手册.pdf​