0.相關資料 相關資料中包括其他精彩博文和代碼倉庫 【嵌入式 TCP IP 資料匯總】 【ENC28J60學習筆記——索引】 【第1部分】【第2部分】【第3部分】【第4部分】 【STM32NET學習筆記——索引】 【uIP學習筆記】 【LwIP學習筆記——STM32 ENC28J60移植與入門】
1前言
嵌入式乙太網路開發,可以分為兩個部分,一個是乙太網路收發晶片的使用,一個是嵌入式乙太網路協議棧的實現。乙太網路收發晶片的使用要比串口收發晶片的使用複雜的多,市面上流通比較廣泛的乙太網路收發晶片種類還不少,有SPI介面的ENC28J60,也有並口形式的RTL8019S,CS8900A等。嵌入式乙太網路協議棧有著名的uIP協議棧,Lwip協議棧,還有其他嵌入式高手開發的協議棧。無論是硬體還是軟體,都無法分出高低,適合項目需求的才是最好的。 1.1 寫作理由
再說明一下我寫作的理由。以前從淘寶上購買過ENC28J60,店家信誓旦旦地說能提供51AVR LPC STM32等多個平台的代碼,可以實現一個網頁控制LED。頭腦一熱買了回來,買回來才發現,店家提供的資料零零散散,不易弄懂。幾經周轉,發現原來這些ENC28J60的代碼都出自一個地方——AVRNET,源自老外的一個開源項目。把最原始的代碼拿來細細品味,乙太網路協議就不那麼神秘了。在這裡說一下ENC28J60的使用,熟悉了ENC28J60的驅動可以分幾步走。第一步,通過ENC28J60移植uIP或者lwIP協議棧,實現TCP或是UDP通訊,第二,順著AVRNET項目走,實現一個簡單的web伺服器,運行靜態或者動態網頁。嵌入式乙太網路和電腦乙太網路開發不同,對於TCP通訊而言沒有socket通訊端,對於網頁編程而言也沒有IIS或PHP,所示實現起來會相對麻煩,但是也非常有樂趣。 1.2 資料準備
嵌入式乙太網路開發是非常複雜的工作,在開始之前最好先大致瀏覽ENC28J60的使用手冊。除此之外,需要認真閱讀TCP IP相關知識,推薦一本圖書《嵌入式Internet TCP/IP基礎、實現和應用》。嵌入式開發是一個反覆借鑒的過程,該部分代碼參考了AVRNET項目和奮鬥開發板的相關範例。【AVRNET項目網址連結】
雖然AVRNET項目所使用的MCU為ATmega32,但是認真閱讀原始碼之後也可以方便的移植到其他的MCU平台,例如STM8、STM32和MSP430等。 2 寄存器和寄存器操作
ENC28J60的寄存器很多,操作這些寄存器需要一個良好的程式碼群組織工作。在AVRNET項目中,把ENC28J60的驅動分解成ENC28J60.h檔案和ENC28J60.c檔案。H檔案中主要描述ENC28J60寄存器的基本定義,而C檔案主要實現了這些寄存器的操作。 2.1 寄存器定義
首先分析一下ENC28J60.h這個標頭檔。閱讀資料手冊之後,會發現ENC28J60寄存器數量較多,通過分析和整理,操作ENC28J60的寄存器需要注意以下3點。
【1】共有三種不同形式的寄存器——控制寄存器,乙太網路寄存器 和PHY寄存器,不同的寄存器以不同的字母開頭,以E、 MA和MI加以區分。操作這三種不同的寄存器需要不同的組合命令。
【2】 寄存器被分布在4個不同的bank中,也就是說存在地址相同的寄存器,但是這些寄存器卻位於不同的分區中,在操作寄存器之前必須選中正確的bank。
【3】 雖然存在4個bank,但是有5個寄存器在4個bank的位置相同,它們是EIE、 EIR、ESTAT、ECON1、ECON2。
AVRNET項目中,寄存器被定義成8位長度,而這8位長度包含了三個部分,地址bit7(最高位)用以區分PHY和MAC寄存器,PHY寄存器的操作最為特殊;地址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代表BANKx中的具體地址,該地址為0x01,第二個數字0x00代表BANK編號,該BANK地址為0;EHT1為位於BANK1中的控制寄存器,第一個0x01代表BANKx中的具體地址,該地址為0x01,第二個0x20代表BANK編號,此處BANK編號為1。請注意由於BANK編號被儲存在BIT6和BIT5,所以此處為0x20而不是0x10;MACON2為位於BANK2的乙太網路寄存器,第一個數字0x01代表在該BANKx中的寄存器地址,第二個數字0x40代表BANK編號,此處BANK編號為2,而第三個數字0x80代表該寄存器為乙太網路寄存器或是PHY寄存器,該寄存器的操作比較特殊。
為了方便寄存器操作,標頭檔中還定義了寄存器地址操作的掩碼,簡單而言就是需要查看哪些位,不需要查看哪些位。
/* 寄存器位址遮罩 */#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 0x1F
2.2 寄存器操作命令
寄存器操作命令也可稱為寄存器作業碼。為了實現寄存器的操作,ENC28J60定義了6+1個寄存器操作命令(作業碼)。操作相關寄存器至少有讀寄存器命令,寫寄存器命令;發送或接收乙太網路資料則必有寫緩衝區命令或讀緩衝區命令;為了加快操作,對於某些控制寄存器而言還可以有置位或者清零某位的命令;最後加上一個軟體複位命令,錦上添花。
/* 讀控制寄存器 */#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
2.3 接收和發送緩衝區分配
乙太網路資料的接收和發送離不開驅動晶片內部的RAM,也可稱之為硬體緩衝區。ENC28J60包括8K 的硬體緩衝區,該硬體緩衝區一部分被接收緩衝區使用,另一部分為發送緩衝區使用。控制ENC28J60的最終目的為操作該硬體緩衝區。執行乙太網路發送命令時,向發送緩衝區中填充資料,並觸發相關寄存器發送乙太網路資料;執行乙太網路接收命令時,通過查詢相關寄存器或者外部中斷的方式獲得乙太網路資料輸入事件,接著從接收緩衝區中讀取相關資料。
(1) 把緩衝區劃分為兩個部分。把8K的硬體緩衝區劃分為兩個部分至少需要四個參數,接收緩衝區需要一個起始地址和一個結束位址加以描述,發送緩衝區也需要一個起始地址和一個結束位址加以描述。最理想的方式,兩個緩衝區完全佔據了8K的硬體緩衝區,完美地利用這一空間。由於ENC28J60的寄存器長度為8位,而硬體緩衝區的大小為8K,所以前面提到的4個地址需要8個寄存器才可以完全描述,需要把單個地址分為高8位和低8位。在AVRNET項目中,接收緩衝區較大,而發送緩衝區較小。在乙太網路協議中,最大的報文長度為1518位元組,而最小報文長度為60位元組。發送緩衝區等於或略大於1518位元組,剩餘的部分全部分配給接收緩衝區。接收緩衝區較大也是考慮到AVR的處理能力有限,若某個時間點收到多個乙太網路報文,可以先把報文閑置與硬體緩衝區中,待MCU空閑時再從緩衝區中取出。
/* 接收緩衝區起始地址 */#define RXSTART_INIT 0x00/* 接收緩衝區停止地址 */#define RXSTOP_INIT (0x1FFF - 0x0600 - 1)/* 發送緩衝區起始地址 發送緩衝區大小約1500位元組*/#define TXSTART_INIT (0x1FFF - 0x0600)/* 發送緩衝區停止地址 */#define TXSTOP_INIT 0x1FFF
圖2.1 硬體緩衝區結構
(2) 對於發送緩衝區而言,需要指定發送緩衝區寫指標,使用寫緩衝區命令操作該部分緩衝區,寫指標的地址會不斷增長,若遇到結束位址會重新返回起始地址。對於接收緩衝區而言就稍微複雜一點,每次讀取之前必須明確該次操作時的讀指標位置,根據前文的代碼,緩衝區讀指標的起始地址為0,在第一次讀操作發生之後需要立即計算下次讀操作的讀指標地址。ENC28J60讀緩衝區時,被讀取的內容並不全是乙太網路負載,在乙太網路負載之前還有下一個資料包的地址指標(佔兩個位元組),接收狀態向量(佔4個位元組),之後才是“真實”乙太網路負載,該負載包括目標MAC地址,源MAC地址,資料包類型等等;最後為CRC校正位元組。
圖2.2 接收資料包結構