轉載:
本介紹可分為三塊內容:
1.以太網數據幀結構
符合IEEE802.3標准的以太網幀的長度是介於64-1516字節之間。主要由目標MAC地址、源MAC地址、類型/長度字段、數據有效負載、可選填充字段和循環冗余校驗組成,另外在通過以太網介質發送數據包時,一個7字節的前導字段和一字節的幀起始定界符被附加到以太網數據包的開頭。以太網數據包的結構如圖1所示。
圖1以太網數據幀結構圖
ENC28J60在發送或接收數據包時由以下幾點值得關注:
首先,ENC28J60具有一個接收過濾器可以丟棄或接收具有組播、廣播或單播目標地址的數據包。
其次,在數據字段處:
以太網數據字段的長度可以在0-1500字節之間變換,超過這一范圍的數據包是違反以太網標准的,這些包將會被大多數以太網節點丟棄。若設置ENC28J60的巨大幀使能位為1,可以發送和接收超大規格數據包。
在數據域中的填充字段是在數據字段小於46字節時起填充作用。ENC28J60在發送數據包時,會自動填充0。ENC28J60在接收時自動拒絕小於18字節的數據包。數據填充亦可由主控芯片來配置。
最后,在CRC處:
ENC28J60在接收數據包時將檢查每個傳入數據包的CRC,通過檢測ERXFCON.CRCEN位來判斷輸入數據包的CRC是否正確。ENC28J60在發送數據包時,將自動生成一個有效的CRC並發送它。發送數據包的CRC亦可由主控芯片來提供。
2.驅動程序介紹
(1)ENC28J60的寄存器讀寫規則
由於ENC28J60芯片采用的是SPI串行接口模式,其對內部寄存器讀寫的規則是先發操作碼<前3bit>+寄存器地址<后5bit>,再發送欲操作數據。通過不同操作碼來判別操作時讀寄存器(緩存區)還是寫寄存器(緩沖區)或是其它。
(2)ENC28J60芯片初始化程序
ENC28J60發送和接收數據包前必須對內進行初始化設置,通常在復位后完成,不需再更改。
void enc28j60_init(void)
{
//*****Bank1區相關寄存器配置 SPI操作塊 數據塊
//初始化程序一開始先進行軟件復位,111<操作碼>+11111<參數>, N/A
// ENC28J60_SOFT_RESET=0xFF
enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
delay_ms(5);
//初始化接收緩沖區,設置接收起始地址
NextPacketPtr = RXSTART_INIT; //讀下一數據包指針
enc28j60Write(ERXSTL, RXSTART_INIT&0xFF);
enc28j60Write(ERXSTH, RXSTART_INIT>>8);
//設置接收讀指針指向地址
enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF);
enc28j60Write(ERXRDPTH, RXSTART_INIT>>8);
//設置接收緩沖區的末尾地址
// ERXND寄存器默認指向整個緩沖區的最后一個單元
enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF);
enc28j60Write(ERXNDH, RXSTOP_INIT>>8);
//設置發送緩沖區的起始地址
//ETXST寄存器默認地址是整個緩沖區的第一個單元
enc28j60Write(ETXSTL, TXSTART_INIT&0xFF);
enc28j60Write(ETXSTH, TXSTART_INIT>>8);
//*****Bank2區相關寄存器配置
//MAC初始化配置
//MAC接收使能,下行程序段表示使能MAC接收,使能IEEE流量控制
enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
//MACON2清零,讓MAC退出復位狀態
enc28j60Write(MACON2, 0x00);
//下行程序段表示使能自動填充和自動CRC添加
enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,MACON3,
MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
//enc28j60Write(MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
//配置非背對背包之間的間隔
enc28j60Write(MAIPGL, 0x12);
enc28j60Write(MAIPGH, 0x0C);
//配置背對背包之間的間隔
enc28j60Write(MABBIPG, 0x12);
//設置允許接收或發送的最大幀長度編程
enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF);
enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8);
//*****Bank3區相關寄存器配置
// 將MAC地址寫入MAADR0-MAADR5寄存器中
// NOTE: MAC address in ENC28J60 is byte-backward
enc28j60Write(MAADR5, UIP_ETHADDR0);
enc28j60Write(MAADR4, UIP_ETHADDR1);
enc28j60Write(MAADR3, UIP_ETHADDR2);
enc28j60Write(MAADR2, UIP_ETHADDR3);
enc28j60Write(MAADR1, UIP_ETHADDR4);
enc28j60Write(MAADR0, UIP_ETHADDR5);
//阻止發送回路的自動環回
enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS);
//*****Bank0區相關寄存器配置
enc28j60SetBank(ECON1);//設置寄存器區
//中斷使能
enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);
//包接收使能
enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
}
說明:enc28j60Write函數內部包含了SetBank<設置寄存器區>子程序,而enc28j60WriteOp直接根據spi操作碼<前3bit>+寄存器地址<后5bit>進行操作的。
(3)ENC28J60發送數據包程序
ENC28J60內的MAC在發送數據包時會自動生成前導符合幀起始定界符。此外,也會根據用戶配置以及數據具體情況自動生成數據填充和CRC字段。主控器必須把所有其它要發送的幀數據寫入ENC28J60緩沖存儲器中。另外在待發送數據包前要添加一個包控制字節。包控制字節包括內容有:包超大幀使能位(PHUGEEN)、包填充使能位(PPADEN)、包CRC使能位(PCRCEN)和包改寫位(POVERRIDE)四個內容。如圖2所示。
void enc28j60PacketSend(u16_t len, u8_t* packet)
{
//配置發送緩沖區寫指針起始地址
enc28j60Write(EWRPTL, TXSTART_INIT);
enc28j60Write(EWRPTH, TXSTART_INIT>>8);
// 根據給定數據域的大小配置發送緩沖區的末尾地址
enc28j60Write(ETXNDL, (TXSTART_INIT+len));
enc28j60Write(ETXNDH, (TXSTART_INIT+len)>>8);
//給每個數據包的包控制字節預留一個單元
enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
// TO DO, fix this up
if( uip_len <= TOTAL_HEADER_LENGTH )
{
//將數據包復制到緩沖區中
enc28j60WriteBuffer(len, packet);
}
else
{
len -= TOTAL_HEADER_LENGTH;
enc28j60WriteBuffer(TOTAL_HEADER_LENGTH, packet);
enc28j60WriteBuffer(len, (unsigned char *)uip_appdata);
}
//將以太網控制寄存器ECON1所有位 置1,以發送緩沖區數據
enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
}
圖2 發送數據包結構
(4)ENC28J60接收數據包程序
u16_t enc28j60PacketReceive(u16_t maxlen, u8_t* packet)
{
u16_t rxstat;
u16_t len;
//檢測緩沖區是否收到一個數據包
if( !(enc28j60Read(EIR) & EIR_PKTIF) ) //檢測EIR.PKTIF是否為1
{
// 通過查看EPKTCNT寄存器再次檢查是否收到包
if (enc28j60Read(EPKTCNT) == 0)//EPKTCNT為0表示沒有包接收/或包已被處理
return 0;
}
// 配置接收緩沖器讀指針指向地址
enc28j60Write(ERDPTL, (NextPacketPtr));
enc28j60Write(ERDPTH, (NextPacketPtr)>>8);
//下一個數據包的讀指針<詳情可查看接收數據包結構圖圖3>
NextPacketPtr = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
//讀數據包字節長度<詳情可查看接收數據包結構圖圖3,status[15..0]>
len = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
//讀接收數據包的狀態<status[31..16]>
rxstat = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
//計算實際數據長度
//移除CRC字段的長度來減少MAC所報告長度
len = MIN(len, maxlen);
//從緩沖區中將數據包復制到packet中
enc28j60ReadBuffer(len, packet);
//ERXRDPT讀緩沖器指針
//ENC28J60將一直寫到該指針之前的一單元為止
u16_t rs,re;
rs = enc28j60Read(ERXSTH);//ERXST接收緩沖區的起始地址
rs <<= 8;
rs |= enc28j60Read(ERXSTL);
re = enc28j60Read(ERXNDH);//ERXND接收緩沖區的末尾地址
re <<= 8;
re |= enc28j60Read(ERXNDL);
if (NextPacketPtr - 1 < rs || NextPacketPtr - 1 > re)
{
enc28j60Write(ERXRDPTL, (re));//ERXRDPT接收讀地址
enc28j60Write(ERXRDPTH, (re)>>8);
}
else
{
enc28j60Write(ERXRDPTL, (NextPacketPtr-1));
enc28j60Write(ERXRDPTH, (NextPacketPtr-1)>>8);
}
// 數據包個數遞減位EPKTCNT減1
enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
return len;
}
圖3 接收數據包結構