STM32NET學(xué)習(xí)筆記 ARP和Ethernet部分
(2013年初整理筆記,2013底發(fā)布至CSDN博客中)
嵌入式以太網(wǎng)開發(fā)是一個(gè)很有挑戰(zhàn)性的工作。通過幾個(gè)月的學(xué)習(xí),個(gè)人覺得大致有兩條途徑。第一條途徑,通過高級語言熟悉socket編程,例如C#或C++,熟悉bind,listen,connect,accept等函數(shù),在嵌入式系統(tǒng)中應(yīng)用 lwIP協(xié)議棧。第二種途徑,通過分析嵌入式以太網(wǎng)代碼,結(jié)合TCPIP協(xié)議棧規(guī)范逐步實(shí)踐協(xié)議棧代碼。第一種途徑效率高,開發(fā)周期短,編寫出來的代碼性能穩(wěn)定,第二種途徑花的時(shí)間長,開發(fā)出來的代碼功能不完善,但是由于緊緊結(jié)合TCPIP規(guī)范,可以了解的內(nèi)容較多,適合學(xué)習(xí)。本文通過分析和修改AVRNET源碼并移植到STM32平臺(tái),逐步實(shí)現(xiàn)TCPIP協(xié)議棧的各個(gè)子部分,包括ETHERNET部分,ARP部分,IP部分,ICMP部分,UDP部分,TCP部分和HTTP部分。
【STM32NET學(xué)習(xí)筆記——索引】【代碼倉庫】
本文先實(shí)現(xiàn)ethernet部分和ARP部分。
1.2 其他說明【硬件平臺(tái)】 STM32+ENC28J60
【編譯平臺(tái)】 IAR 6.5
【IP地址】在實(shí)踐之前,需要通過ipconfig命令查看PC機(jī)的IP地址和MAC地址,AVR的IP地址設(shè)定必須和PC機(jī)在同一個(gè)網(wǎng)段中。例如 :
PC機(jī)IP:192.168.1.102
AVR IP: 192.168.1.115
【局域網(wǎng)訪問 】
如果有STM32開發(fā)板或者其他CPU的開發(fā)板的話,可以把開發(fā)板的以太網(wǎng)端口連接到路由器LAN端口,只要保證開發(fā)板的IP地址和PC機(jī)的IP地址在同一個(gè)網(wǎng)段。
【廣域網(wǎng)訪問 】
如果有固定的電信網(wǎng)IP地址的話,可以在路由器中設(shè)置靜態(tài)端口映射,把某個(gè)端口映射成局域網(wǎng)內(nèi)的IP地址和端口號。若沒有固定IP地址的話,可使用花生殼軟件虛擬一個(gè)域名。
【代碼倉庫】——CSDN Code代碼倉庫。
以太網(wǎng)協(xié)議棧的實(shí)現(xiàn)離不開以太網(wǎng)驅(qū)動(dòng)芯片。以太網(wǎng)驅(qū)動(dòng)如何實(shí)現(xiàn)請參考——ENC28J60學(xué)習(xí)筆記。TCPIP的實(shí)現(xiàn)離不開兩個(gè)基本地址,IP地址和MAC地址。在本例中通過以下代碼定義和實(shí)現(xiàn)。
struct.h頭文件中 相關(guān)定義:
//MAC地址結(jié)構(gòu)體
#pragmapack(1)
typedefstruct_MAC_ADDR
{
BYTEbyte[6];
}MAC_ADDR;
//IP地址結(jié)構(gòu)體
#pragmapack(1)
typedefstruct_IP_ADDR
{
BYTEbyte[4];
}IP_ADDR;
main.c函數(shù)中的初始化代碼:
//初始化MAC地址
stm32_mac.byte[0]='S';
stm32_mac.byte[1]='T';
stm32_mac.byte[2]='M';
stm32_mac.byte[3]='N';
stm32_mac.byte[4]='E';
stm32_mac.byte[5]='T';
//初始化IP地址,固定IP地址
stm32_ip.byte[0]=192;
stm32_ip.byte[1]=168;
stm32_ip.byte[2]=1;
stm32_ip.byte[3]=115;
MAC地址和IP地址均為自定義的結(jié)構(gòu)體,結(jié)構(gòu)體中為一個(gè)字節(jié)數(shù)組。嚴(yán)格來說,MAC地址不能胡亂定義,應(yīng)嚴(yán)格遵守相關(guān)規(guī)范,如果條件允許的話可以使用帶有全球唯一的MAC地址的EEPROM芯片。
3.實(shí)現(xiàn)ETHERNETTCPIP是一系列協(xié)議的組合,其中最有名的為TCP協(xié)議和IP協(xié)議。但是千萬不要忽視最底層的協(xié)議結(jié)構(gòu)——ETHERNET。ETHERNET包括14個(gè)字節(jié),稱之為以太網(wǎng)首部,其中前六個(gè)字節(jié)為目標(biāo)MAC地址,緊著的6個(gè)字節(jié)為源MAC地址,最后的兩個(gè)字節(jié)為協(xié)議類型。以太網(wǎng)的實(shí)現(xiàn)通信時(shí)必須要知道雙方的MAC地址,發(fā)送方不明確接收方的地址便通過ARP協(xié)議尋找目標(biāo)MAC地址,如果依然沒有結(jié)果則可只能把該報(bào)文轉(zhuǎn)發(fā)給路由器,讓路由器處理該報(bào)文。協(xié)議類型只需關(guān)心兩種,0800的IP協(xié)議和0806的ARP協(xié)議。
ethernet.h中相關(guān)宏定義
//協(xié)議類型ARP報(bào)文
#defineETH_TYPE_ARP_V0x0806
#defineETH_TYPE_ARP_H_V0x08
#defineETH_TYPE_ARP_L_V0x06
//協(xié)議類型以太網(wǎng)報(bào)文
#defineETH_TYPE_IP_V0x0800
#defineETH_TYPE_IP_H_V0x08
#defineETH_TYPE_IP_L_V0x00
//以太網(wǎng)報(bào)文頭部長度14
#defineETH_HEADER_LEN14
//目標(biāo)MAC地址
#defineETH_DST_MAC_P0
//源MAC地址
#defineETH_SRC_MAC_P6
//協(xié)議類型
#defineETH_TYPE_H_P12
#defineETH_TYPE_L_P13
ethernet.c中相關(guān)函數(shù)
[cpp]view plaincopy
voideth_generate_header(BYTE*rxtx_buffer,WORD_BYTEStype,BYTE*dest_mac)
{
BYTEi;
//配置以太網(wǎng)報(bào)文目標(biāo)MAC地址和源MAC地址
for(i=0;i
{
rxtx_buffer[ETH_DST_MAC_P+i]=dest_mac[i];
//avr_mac為全局變量
rxtx_buffer[ETH_SRC_MAC_P+i]=stm32_mac.byte[i];
}
//配置協(xié)議類型IP報(bào)文或ARP報(bào)文
rxtx_buffer[ETH_TYPE_H_P]=type.byte.high;
rxtx_buffer[ETH_TYPE_L_P]=type.byte.low;
}
eth_generate_header函數(shù)實(shí)現(xiàn)了填充以太網(wǎng)首部的功能,第一個(gè)輸入?yún)?shù)為發(fā)送接收緩沖區(qū)。第二個(gè)參數(shù)為IP類型,在AVRNET項(xiàng)目中傳入的參數(shù)不是0800的IP協(xié)議類型就是0806的ARP協(xié)議類型。第三個(gè)參數(shù)為目標(biāo)MAC地址,由于本機(jī)MAC地址作為了全局變量,可以在函數(shù)內(nèi)部填充到緩沖區(qū)中。
4.實(shí)現(xiàn)ARP為了使用最少的代碼實(shí)現(xiàn)TCPIP功能,假設(shè)通過IP發(fā)送報(bào)文時(shí)已經(jīng)確認(rèn)了目標(biāo)的IP地址,設(shè)備總是先被動(dòng)的通過ARP先讓PC機(jī)知道其MAC地址,這樣當(dāng)PC機(jī)發(fā)送UDP或者TCP報(bào)文時(shí),在報(bào)文中已經(jīng)包含了PC機(jī)的IP地址,設(shè)備僅需從rxtx_buffer中取出PC機(jī)IP地址。ARP協(xié)議是一個(gè)找鄰居的過程,是一個(gè)廣播找MAC的過程。發(fā)出者通過廣播報(bào)文確認(rèn)某個(gè)IP的MAC地址。ARP首部包括,2字節(jié)硬件類型,2字節(jié)協(xié)議類型,1字節(jié)硬件長度,1字節(jié)協(xié)議長度,2字節(jié)操作碼,6字節(jié)發(fā)送者硬件地址,4字節(jié)發(fā)送者IP地址,6字節(jié)目標(biāo)硬件地址和4字節(jié)目標(biāo)IP地址。
在使用ARP協(xié)議時(shí)需要注意三點(diǎn):
第一,操作碼分為兩種——ARP請求和ARP響應(yīng),ARP請求的編碼為1,ARP響應(yīng)的編碼為2,先有請求后有響應(yīng)。第二,發(fā)送ARP協(xié)議請求時(shí)請求方明確對方IP地址,但是不明確對方MAC地址,所以在請求報(bào)文中MAC地址全部以0替代。第三,由于不知道對方的MAC地址,所以只能通過廣播幀發(fā)送以太網(wǎng)數(shù)據(jù),所以以太網(wǎng)首部的前6個(gè)字節(jié)被FF填充。
為了便于ARP功能的實(shí)現(xiàn),在arp.h文件中定義了以下宏定義
#defineARP_PACKET_LEN28
//ARP請求
#defineARP_OPCODE_REQUEST_V0x0001
#defineARP_OPCODE_REQUEST_H_V0x00
#defineARP_OPCODE_REQUEST_L_V0x01
//ARP響應(yīng)
#defineARP_OPCODE_REPLY_V0x0002
#defineARP_OPCODE_REPLY_H_V0x00
#defineARP_OPCODE_REPLY_L_V0x02
//硬件類型10M以太網(wǎng)
#defineARP_HARDWARE_TYPE_H_V0x00
#defineARP_HARDWARE_TYPE_L_V0x01
//協(xié)議類型IPV4
#defineARP_PROTOCOL_H_V0x08
#defineARP_PROTOCOL_L_V0x00
//硬件地址長度
#defineARP_HARDWARE_SIZE_V0x06
//協(xié)議地址長度
#defineARP_PROTOCOL_SIZE_V0x04
//硬件類型2字節(jié)
#defineARP_HARDWARE_TYPE_H_P0x0E
#defineARP_HARDWARE_TYPE_L_P0x0F
//協(xié)議類型2字節(jié)
#defineARP_PROTOCOL_H_P0x10
#defineARP_PROTOCOL_L_P0x11
//硬件地址1字節(jié)
#defineARP_HARDWARE_SIZE_P0x12
//協(xié)議地址長度1字節(jié)
#defineARP_PROTOCOL_SIZE_P0x13
//操作碼2字節(jié)
#defineARP_OPCODE_H_P0x14
#defineARP_OPCODE_L_P0x15
//發(fā)送者硬件地址6字節(jié)
#defineARP_SRC_MAC_P0x16
//發(fā)送者IP地址4字節(jié)
#defineARP_SRC_IP_P0x1C
//目標(biāo)硬件地址6字節(jié)
#defineARP_DST_MAC_P0x20
//目標(biāo)IP地址6字節(jié)
#defineARP_DST_IP_P0x26
在沒有操作系統(tǒng)的支持下,一般通過一個(gè)無限循環(huán)實(shí)現(xiàn)子功能的實(shí)現(xiàn)。項(xiàng)目中通過某個(gè)process不斷查詢是否存在網(wǎng)卡數(shù)據(jù),如果有網(wǎng)卡數(shù)據(jù)則立刻保存源MAC地址。因?yàn)轫?xiàng)目中沒有維護(hù)ARP表,所