?
一、環(huán)境介紹
代碼開發(fā)工具: Keil5
TCP/IP協(xié)議棧: LWIP
網(wǎng)卡: DM9000
本篇文章主要講解如何在STM32F103工程里添加移植LWIP協(xié)議,最終完成TCP服務(wù)器、TCP客戶端的通信測(cè)試。 網(wǎng)卡采用的是DM9000,工程代碼中,采用STM32的FSMC接口來驅(qū)動(dòng)DM900網(wǎng)卡,DM9000是并口網(wǎng)卡,引腳多,但是速度快,也可以采用其他網(wǎng)卡,SPI協(xié)議的、UART協(xié)議的等。 比如:ENC28J60。 因?yàn)橹饕侵vLWIP協(xié)議棧的移植,所以網(wǎng)卡相關(guān)的代碼就沒有細(xì)說(需要準(zhǔn)備一個(gè)網(wǎng)卡可以正常通信的工程,再移植)。
下面進(jìn)行工程的移植代碼比較多,需要下載對(duì)應(yīng)版本的LWIP源碼,STM32本身的DM9000網(wǎng)卡代碼也比較多,文章里會(huì)講解移植的詳細(xì)的過程,代碼貼出了核心部分。
為了方便大家下載體驗(yàn),下面給了個(gè)下載鏈接,里面包含了本次文章移植的工程、LWIP源碼,移植文檔等,方便直接體驗(yàn)。地址在這里:https://download.csdn.net/download/xiaolong1126626497/19907087
資料包里的內(nèi)容如下:
?
?
?
二、D9000網(wǎng)卡
2.1 DM9000簡(jiǎn)介
DM9000 是一款完全集成的、性價(jià)比高、引腳數(shù)少、帶有通用處理器接口的單芯片快速以太網(wǎng)控制器。 自帶一個(gè) 10/100M PHY 和 4K 雙字的 SRAM ,DM9000A 為適應(yīng)各種處理器提供了8位、16 位數(shù)據(jù)接口訪問內(nèi)部存儲(chǔ)器,DM9000擁有自動(dòng)協(xié)商功能,DM9000特性如下:
1、集成自適應(yīng)10/100M收發(fā)器。
2、內(nèi)置16k字節(jié)的SRAM。
3、支持硬件幀校驗(yàn)。
4、兼容3.3V和5.0V輸入輸出電壓。
DM9000 有多種型號(hào),有 100 引腳和 48 引腳的, 開發(fā)板選擇的是 48 引腳的 DM9000,型號(hào)為 DM9000CEP。
2.2 DM9000 中斷引腳電平設(shè)置
DM9000的34(INT)引腳為中斷輸出引腳,默認(rèn)情況下該引腳高電平有效??梢酝ㄟ^設(shè)置DM9000 的 20(EECK)引腳來改變 INT 的有效電平,當(dāng) EECK 拉高以后, INT 低電平有效,否則的話 INT 是高電平有效的。開發(fā)板上 R66 電阻為 EECK 的上拉電阻,因此開發(fā)板上 DM9000 的 INT 引腳是低電平有效的。
?
2.3 DM9000 數(shù)據(jù)位寬設(shè)置
前面我們提了一下 DM9000 支持 8 位和 16 位兩種數(shù)據(jù)位寬,可以通過 DM9000 的 21(EECS)引腳設(shè)置其數(shù)據(jù)位寬,當(dāng) EECS 上拉的時(shí)候 DM9000 選擇 8 位數(shù)據(jù)位寬,否則的話選擇 16 位數(shù)據(jù)位寬。開發(fā)板上的 R65 電阻為 EECS 的上拉電阻,但是此電阻并未焊接! DM9000 芯片的數(shù)據(jù)位寬為 16 位。
?
2.4 DM9000寄存器表
寄存器 |
描述 |
寄存器地址 |
默認(rèn)值 |
|
NCR |
網(wǎng)絡(luò)控制寄存器。 |
00H |
00H |
|
NSR |
網(wǎng)絡(luò)狀態(tài)寄存器。 |
01H |
00H |
|
TCR |
發(fā)送控制寄存器。 |
02H |
00H |
|
TSR I |
發(fā)送狀態(tài)寄存器 I。 |
03H |
00H |
|
TSR II |
發(fā)送狀態(tài)寄存器 II。 |
04H |
00H |
|
RCR |
接收控制寄存器。 |
05H |
00H |
|
RSR |
接收狀態(tài)寄存器。 |
06H |
00H |
|
ROCR |
接收溢出計(jì)數(shù)寄存器。 |
07H |
00H |
|
BPTR |
背壓門限寄存器。 |
08H |
37H |
|
FCTR |
溢出控制門限寄存器。 |
09H |
38H |
|
FCR |
TX/RX 流量控制寄存器。 |
0AH |
00H |
|
EPCR |
EEPROM/PHY 控制寄存器。 |
0BH |
00H |
|
EPAR |
EEPROM/PHY 地址寄存器。 |
0CH |
40H |
|
EPDRL |
EEPROM/PHY 數(shù)據(jù)寄存器低位。 |
0DH |
XXH |
|
EPDRH |
EEPROM/PHY 數(shù)據(jù)寄存器高位。 |
0EH |
XXH |
|
WCR |
喚醒控制寄存器。 |
0FH |
00H |
|
PAR |
物理地址寄存器。 |
10H~15H |
由 EEPROM 決定 |
|
MAR |
廣播地址寄存器。 |
16H~1DH |
XXH |
|
GPCR |
通用目的控制寄存器(8bit 模式)。 |
1EH |
01H |
|
GPR |
通用目的寄存器。 |
1FH |
XXH |
|
TRPAL |
TX SRAM 讀指針地址低字節(jié)。 |
22H |
00H |
|
TRPAH |
TX SRAM 讀指針地址高字節(jié)。 |
23H |
00H |
|
RWPAL |
RX SRAM 寫指針地址低字節(jié)。 |
24H |
00H |
|
RWRAH |
RX SRAM 寫指針地址高字節(jié)。 |
25H |
0CH |
|
VID |
廠家 ID。 |
28H~29H |
0A46H |
|
PID |
產(chǎn)品 ID。 |
2AH~2BH |
9000H |
|
CHIPR |
芯片版本。 |
2CH |
18H |
|
TCR2 |
發(fā)送控制寄存器 2。 |
2DH |
00H |
|
OCR |
操作控制寄存器。 |
2EH |
00H |
|
SMCR |
特殊模式控制寄存器。 |
2FH |
00H |
|
ETXCSR |
即將發(fā)送控制/狀態(tài)寄存器。 |
30H |
00H |
|
TCSCR |
發(fā)送校驗(yàn)和控制寄存器。 |
31H |
00H |
|
RCSCSR |
接收校驗(yàn)和控制狀態(tài)寄存器。 |
32H |
00H |
|
MRCMDX |
內(nèi)存數(shù)據(jù)預(yù)取讀命令寄存器(地址不加 1)。 |
F0H |
XXH |
|
MRCMDX1 |
內(nèi)存數(shù)據(jù)讀命令寄存器(地址不加 1)。 |
F1H |
XXH |
|
MRCMD |
內(nèi)存數(shù)據(jù)讀命令寄存器(地址加 1)。 |
F2H |
XXH |
|
MRRL |
內(nèi)存數(shù)據(jù)讀地址寄存器低字節(jié)。 |
F4H |
00H |
|
MRRH |
內(nèi)存數(shù)據(jù)讀地址寄存器高字節(jié)。 |
F5H |
00H |
|
MWCMDX |
內(nèi)存數(shù)據(jù)寫命令寄存器(地址不加 1) |
F6H |
XXH |
|
MWCMD |
內(nèi)存數(shù)據(jù)寫命令寄存器(地址加 1)。 |
F8H |
XXH |
|
MWRL |
內(nèi)存數(shù)據(jù)寫地址寄存器低字節(jié)。 |
FAH |
00H |
|
MWRH |
內(nèi)存數(shù)據(jù)寫地址寄存器高字節(jié)。 |
FBH |
00H |
|
TXPLL |
TX 數(shù)據(jù)包長度低字節(jié)寄存器。 |
FCH |
XXH |
|
TXPLH |
TX 數(shù)據(jù)包長度高字節(jié)寄存器。 |
FDH |
XXH |
|
ISR |
中斷狀態(tài)寄存器。 |
FEH |
00H |
|
IMR |
中斷屏蔽寄存器。 |
FFH |
00H |
|
2.5 DM9000常用寄存器介紹
NCR、 NSR、 TCR、 RCR、 FCTR、 BPTR、 TCR2、 ISR、 IMR。
NCR(網(wǎng)絡(luò)控制寄存器)寄存器
BIT |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
名稱 |
RESERVED |
WAKEEN |
RESERVED |
FCOL |
FDX |
LBK |
RST |
FCOL:強(qiáng)制沖突模式,用于檢測(cè)。
FDX:內(nèi)部 PHY 全雙工模式。
LBK:回環(huán)模式(LoopBack)
00 正常;
01 MAC 內(nèi)部回環(huán);
10 內(nèi)部 PHY100M 模式數(shù)字回環(huán);
11 保留;
RST:置 1 軟件復(fù)位, 10us 后自動(dòng)清零。
NSR 寄存器(網(wǎng)絡(luò)狀態(tài)寄存器)
BIT |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
名 稱 |
SPEED |
LINKST |
WAKEST |
RESERVED |
TX2END |
TX1END |
RXOV |
RESERVED |
SPEED:網(wǎng)絡(luò)速度,在使用內(nèi)部 PHY 情況下,0 表示 100Mbps,1 表示 100Mbps,當(dāng) LINKST=0時(shí),此位無意義。
LINKST:連接狀態(tài), 0 為連接失敗, 1 位已連接。
TX2END: TX(發(fā)送)數(shù)據(jù)包 2 完成標(biāo)志,讀取或?qū)?1 將清零該位。
TX1END: TX(發(fā)送)數(shù)據(jù)包 1 完成標(biāo)志,讀取或?qū)?1 將清零該位。
RXOV: RX(接收)FIFO 溢出標(biāo)志。
TCR 寄存器(發(fā)送控制寄存器)
BIT |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
名 稱 |
RESERVED |
TJDIS |
EXCECM |
PAD_DIS2 |
CRC_DIS2 |
PAD_DIS1 |
CRC_DIS1 |
TXREQ |
TJDIS: Jabber 傳輸禁止。
1,禁止 Jabber 傳輸定時(shí)器(2048 字節(jié))。
0,使能。
EXCECM:嚴(yán)重沖突模式控制
0,當(dāng)沖突計(jì)數(shù)多于 15 則終止本次數(shù)據(jù)包。
1,始終嘗試發(fā)送本次數(shù)據(jù)包。
PAD_DIS2:禁止為數(shù)據(jù)包 II 添加填充。
CRC_DIS2:禁止為數(shù)據(jù)包 II 添加 CRC 校驗(yàn)。
PAD_DIS1:禁止為數(shù)據(jù)包 I 添加填充。
CRC_DIS1:禁止為數(shù)據(jù)包 I 添加 CRC 校驗(yàn)。
TXREQ: TX(發(fā)送)請(qǐng)求,發(fā)送完成后自動(dòng)清零該位
RCR 寄存器(發(fā)送控制寄存器)
BIT |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
名稱 |
RESERVED |
WTDIS |
DIS_LONG |
DIS_CRC |
ALL |
RUNT |
PRMSC |
RXEN |
WTDIS:看門狗定時(shí)器(2048 字節(jié))禁止。
1,進(jìn)制
0,使能
DIS_LONG:丟棄長數(shù)據(jù)包, 1,丟棄數(shù)據(jù)包長度超過 1522 字節(jié)的數(shù)據(jù)包。
DIS_CRC:丟棄 CRC 校驗(yàn)錯(cuò)誤數(shù)據(jù)包。
ALL:允許廣播。
RUNT:允許小于最小長度的數(shù)據(jù)包。
PRMSC:各種模式。
RXEN:接收使能。
FCTR 寄存器(流控制閾值寄存器)
BIT |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
名稱 |
HWOT |
HWOT |
HWOT |
HWOT |
LWOT |
LWOT |
LWOT |
LWOT |
HWOT:RX FIFO 緩存高位溢出門限
當(dāng) RX SRAM 空閑空間小于該門限值時(shí)則發(fā)送一個(gè)暫停時(shí)間為 FFFFH 的暫停包,若該值為 0,則無接收控件。 1=1k 字節(jié),默認(rèn)值為 3H,即 3K 字節(jié)空閑空間,不要超過 S RAM 大小。
LWOT:RX FIFO 緩存低位溢出門限當(dāng) RX SRAM 空閑空間大于該門限值時(shí)則發(fā)送一個(gè)暫停時(shí)間為 0000H 的暫停包。
當(dāng)溢出門限最高值的暫停包發(fā)送之后,溢出門限最低值的暫停包才有效,默認(rèn)值為 8K,不要超過 SRAM 大小。
BPTR 寄存器(背壓閾值寄存器)
BIT |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
名稱 |
BPHW |
JPT |
BPHW:背壓閾值最高值當(dāng)接收 SRAM 空閑空間低于該閾值,則 MAC 將產(chǎn)生一個(gè)擁擠狀態(tài), 1=1k 字節(jié)。默認(rèn)值為 3H,即 3K 字節(jié)空閑空間,不要超過 SRAM 大小。
JPT:擁擠狀態(tài)時(shí)間,模式為 200us, JPT 值與其對(duì)應(yīng)的擁擠狀態(tài)時(shí)間表
JPT 值 |
擁擠狀態(tài)時(shí)間(us) |
JPT 值 |
擁擠狀態(tài)時(shí)間(us) |
0000 |
5 |
1000 |
250 |
0001 |
10 |
1001 |
300 |
0010 |
15 |
1010 |
350 |
0011 |
25 |
1011 |
400 |
0100 |
50 |
1100 |
450 |
0101 |
100 |
1101 |
500 |
0110 |
150 |
1110 |
550 |
0111 |
200 |
1111 |
600 |
TCR2 寄存器(發(fā)送控制寄存器 2)
BIT |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
名稱 |
RLCP |
DTU |
ONEPM |
IFGS |
LED: LED 模式
1,設(shè)置 LED 引腳為模式 1
0,設(shè)置 LED 引腳為模式 0 或根據(jù) EEPROM 的設(shè)定。
RLCP:重試沖突延時(shí)數(shù)據(jù)包, 1 重新發(fā)送有沖突延遲的數(shù)據(jù)包。
DTU: 1 禁止重新發(fā)送“underruned”數(shù)據(jù)包。
ONEPM:?jiǎn)伟J健?/span>
1,發(fā)送完成前發(fā)送一個(gè)數(shù)據(jù)包的命令能被執(zhí)行。
0,發(fā)送完成前發(fā)送最多兩個(gè)數(shù)據(jù)包的命令能被執(zhí)行。
IFGS:幀間間隔設(shè)置。
0XXX 為 96bit, 1000 為 64bit, 1001 為 72bit
1010 為 80bit, 1011 為 88bit, 1100 為 96bit
1101 為 104bit, 1110 位 112bit, 1111 為 120bit
ISR 寄存器(中斷狀態(tài)寄存器)
BIT |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
名稱 |
IOMODE |
RESERVED |
LNKCHG |
UDRUN |
ROO |
PT |
PR |
IOMODE: 0,16 位模式; 1,8 位模式。
LNKCHG:連接狀態(tài)改變。
UDRUN:發(fā)送“Underrun”
ROO:接收溢出計(jì)數(shù)器溢出
ROS:接收溢出。
PT:數(shù)據(jù)包發(fā)送。
PR:數(shù)據(jù)包接收。
IMR 寄存器(中斷狀態(tài)寄存器)
BIT |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
名稱 |
PAR |
RESERVED |
LNKCHGI |
UDRUNI |
ROOI |
ROSI |
PTI |
PRI |
PAR:使能 SRAM 的讀/寫指針在指針地址超過 SRAM 的大小時(shí)自動(dòng)跳回起始位置。需要驅(qū)動(dòng)程序設(shè)置該位,若設(shè)置該位, REG_F5 將自動(dòng)置為 0XH。
LNKCHGI:使能連接狀態(tài)改變中斷。
UDRUNI:使能發(fā)送“Underrun”中斷。
ROOI:使能接收溢出計(jì)數(shù)器溢出中斷。
ROI:使能接收溢出中斷。
PTI:使能數(shù)據(jù)包發(fā)送中斷。
PRI:使能數(shù)據(jù)包接收中斷。
2.6 DM9000 直接內(nèi)存訪問控制(DMAC)
DM9000 直接內(nèi)存訪問控制(DMAC)
DM9000 支持 DMA 方式簡(jiǎn)化對(duì)內(nèi)部存儲(chǔ)器的訪問。在我們編程寫好內(nèi)部存儲(chǔ)器地址后,就可以用一個(gè)讀/寫命令偽指令把當(dāng)前數(shù)據(jù)加載到內(nèi)部數(shù)據(jù)緩沖區(qū),這樣,內(nèi)部存儲(chǔ)器指定位置就可以被讀/寫命令寄存器訪問。存儲(chǔ)器地址將會(huì)自動(dòng)增加,增加的大小與當(dāng)前總線操作模式相同(比如:8-bit、 16-bit 或 32-bit),接著下一個(gè)地址數(shù)據(jù)將會(huì)自動(dòng)加載到內(nèi)部數(shù)據(jù)緩沖區(qū)。要注意的是在連續(xù)突發(fā)式第一次訪問的數(shù)據(jù)應(yīng)該被忽略,因?yàn)?,這個(gè)數(shù)據(jù)是最后一次讀寫命令的內(nèi)容。內(nèi)部存儲(chǔ)器空間大小 16K 字節(jié)。前 3K 字節(jié)單元用作發(fā)送包的緩沖區(qū),其他 13K 字節(jié)用作接收包的緩沖區(qū)。所以在寫存儲(chǔ)器操作時(shí),如果地址越界(即超出 3K 空間),在 IMR 寄存器 bit7 置位的情況下,地址指針將會(huì)返回到存儲(chǔ)器 0 地址處。同樣,在讀存儲(chǔ)器操作時(shí),如果地址越界(即超出 16K 空間),在 IMR 寄存器 bit7 置位的情況下,地址指針將會(huì)返回到存儲(chǔ)器 0x0C00 地址處。
DM9000 數(shù)據(jù)包發(fā)送
DM9000 有兩個(gè)發(fā)送數(shù)據(jù)包: index1 和 index2,同時(shí)存儲(chǔ)在 TX SRAM 中。發(fā)送控制寄存器(02h)控制循環(huán)冗余校驗(yàn)碼(CRC)和填充(pads)的插入,其狀態(tài)分別記錄在發(fā)送狀態(tài)寄存器I(03H)和發(fā)送狀態(tài)寄存器 II(04H)中。發(fā)送器的起始地址為 0x00H,在軟件或硬件復(fù)位后,默認(rèn)的數(shù)據(jù)發(fā)送包為 index1。首先,使用 DMA 端口將數(shù)據(jù)寫 TX SRAM 中,然后,在發(fā)送數(shù)據(jù)包長度寄存器中把數(shù)據(jù)字節(jié)數(shù)寫入字節(jié)計(jì)數(shù)寄存器。置位發(fā)送控制寄存器的 bit0 位,則 DM9000 開始發(fā)送 index1 數(shù)據(jù)包。在 index1數(shù)據(jù)包發(fā)送結(jié)束之前,數(shù)據(jù)發(fā)送包 index2 被移入 TX SRAM 中。在 index1 數(shù)據(jù)包發(fā)送結(jié)束后,將 index2 數(shù)據(jù)字節(jié)數(shù)寫入字節(jié)計(jì)數(shù)寄存器中,然后,置位發(fā)送控制寄存器的 bit0 位,則 index2數(shù)據(jù)包開始發(fā)送。以此類推,后面的數(shù)據(jù)包都以此方式進(jìn)行發(fā)送。
DM9000 數(shù)據(jù)包接收
RX SRAM 是一個(gè)環(huán)形數(shù)據(jù)結(jié)構(gòu)。在軟件或硬件復(fù)位后, RX SRAM 的起始地址為 0X0C00。每個(gè)接收數(shù)據(jù)包都包含有 CRC 校驗(yàn)域,數(shù)據(jù)域,以及緊跟其后的 4 字節(jié)包頭域。 4 字節(jié)包頭格式為: 01h、狀態(tài)、 BYTE_COUNT 低、 BYTE_COUNT 高。請(qǐng)注意:每個(gè)接收包的起始地址處在適當(dāng)?shù)牡刂愤吔?,這取決于當(dāng)前總線操作模式(8bit 或者 16bit)
2.7 DM9000原理圖介紹
?
各信號(hào)線描述如下:
PWRST: DM9000 復(fù)位信號(hào)。
CS: DM9000 的片選信號(hào)。
WR(IOW): 處理器寫命令。
RD(IOR): 處理器讀命令。
CMD: 命令/數(shù)據(jù)標(biāo)志, 0,讀寫命令; 1,讀寫數(shù)據(jù)。
SD0~SD15: 16 位雙向數(shù)據(jù)線。
信號(hào)線對(duì)應(yīng)的GPIO口對(duì)應(yīng)關(guān)系
引腳名稱 |
GPIO口 |
功能說明 |
PWRST-->DM9000_RST |
PD7 |
復(fù)位信號(hào) |
CS-->FSMC_NE2 |
PG9 |
片選信號(hào) |
WR(IOW)-->FSMC_NWE |
PD5 |
處理器寫命令 |
RD(IOR) --->FSMC_NOE |
PD4 |
處理器讀命令 |
CMD—>FSMC-A7 |
PF13 |
命令/數(shù)據(jù)標(biāo)志, 0,讀寫命令; 1,讀寫數(shù)據(jù) |
INT--->DM9000_INT |
PG6 |
中斷引腳 |
FSMC_D0 |
PD14 |
數(shù)據(jù)線0 |
FSMC_D1 |
PD15 |
數(shù)據(jù)線1 |
FSMC_D2 |
PD0 |
數(shù)據(jù)線2 |
FSMC_D3 |
PD1 |
數(shù)據(jù)線3 |
FSMC_D4 |
PE7 |
數(shù)據(jù)線4 |
FSMC_D5 |
PE8 |
數(shù)據(jù)線5 |
FSMC_D6 |
PE9 |
數(shù)據(jù)線6 |
FSMC_D7 |
PE10 |
數(shù)據(jù)線7 |
FSMC_D8 |
PE11 |
數(shù)據(jù)線8 |
FSMC_D9 |
PE12 |
數(shù)據(jù)線9 |
FSMC_D10 |
PE13 |
數(shù)據(jù)線10 |
FSMC_D11 |
PE14 |
數(shù)據(jù)線11 |
FSMC_D12 |
PE15 |
數(shù)據(jù)線12 |
FSMC_D13 |
PD8 |
數(shù)據(jù)線13 |
FSMC_D14 |
PD9 |
數(shù)據(jù)線14 |
FSMC_D15 |
PD10 |
數(shù)據(jù)線15 |
FSMC接口框圖
?
DM9000網(wǎng)卡接在FSMC的第2塊上,數(shù)據(jù)線地址: 0x64000000
PA7地址線作為命令與數(shù)據(jù)線切換引腳。
外接16位寬度存儲(chǔ)器:HADDR[25:1] ? FSMC_A[24:0]
外接8位寬度存儲(chǔ)器: HADDR[25:0] ? FSMC_A[25:0]
0x64000000基地址:01100100000000000000000000000000 0x64000000寫數(shù)據(jù):01100100000000000000000000000000 0x64000100寫命令:01100100000000000000000100000000 |
2.8 DM9000時(shí)序圖介紹
IOR和IOW是DM9000的讀寫選擇引腳,低電平有效,即低電平時(shí)進(jìn)行讀(IOR)寫(IOW)操作;AEN是芯片選通引腳,低電平有效,該引腳為低時(shí)才能進(jìn)行讀寫操作;CMD的命令/數(shù)據(jù)切換引腳,低電平時(shí)讀寫命令操作,高電平時(shí)讀寫數(shù)據(jù)操作。
讀時(shí)序:
?
寫時(shí)序:
?
三、LWIP(TCP/IP)網(wǎng)絡(luò)協(xié)議棧介紹
根據(jù)以太網(wǎng)幀頭攜帶的上層協(xié)議類型值傳遞數(shù)據(jù)。
以太網(wǎng)幀格式定義:
目的MAC地址 源MAC地址 類型/長度 數(shù)據(jù) 校驗(yàn)
6字節(jié) 6字節(jié) 2字節(jié) 46-1500字節(jié) 4字節(jié)
ip:0x0800
ARP:0x0806
最大幀長1518字節(jié) 最小字節(jié)64字節(jié)
3.1 LWIP介紹
lwip是瑞典計(jì)算機(jī)科學(xué)院網(wǎng)絡(luò)嵌入式系統(tǒng)小組(SICS)的Adam Dunkels(亞當(dāng)·鄧克爾) 開發(fā)的一個(gè)小型開源的TCP/IP協(xié)議棧。實(shí)現(xiàn)的重點(diǎn)是在保持TCP協(xié)議主要功能的基礎(chǔ)上減少對(duì)RAM 的占用。
LwIP是Light Weight (輕型)IP協(xié)議,有無操作系統(tǒng)的支持都可以運(yùn)行。LwIP實(shí)現(xiàn)的重點(diǎn)是在保持TCP協(xié)議主要功能的基礎(chǔ)上減少對(duì)RAM 的占用,它只需十幾KB的RAM和40K左右的ROM就可以運(yùn)行,這使LwIP協(xié)議棧適合在低端的嵌入式系統(tǒng)中使用。lwip提供三種API:1)RAW API 2)(NETCONN)lwip API 3)BSD API。
RAW 編程接口使得程序效率高,但是需要對(duì) LWIP 有深入的了解,而且不適合大數(shù)據(jù)量等場(chǎng)合。 NETCONN 編程接口,使用 NETCONN API 時(shí)需要有操作系統(tǒng)的支持。
RAW API把協(xié)議棧和應(yīng)用程序放到一個(gè)進(jìn)程里邊,該接口基于函數(shù)回調(diào)技術(shù),使用該接口的應(yīng)用程序可以不用進(jìn)行連續(xù)操作。不過,這會(huì)使應(yīng)用程序編寫難度加大且代 碼不易被理解。為了接收數(shù)據(jù),應(yīng)用程序會(huì)向協(xié)議棧注冊(cè)一個(gè)回調(diào)函數(shù)。該回調(diào)函數(shù)與特定的連接相關(guān)聯(lián),當(dāng)該關(guān)聯(lián)的連接到達(dá)一個(gè)信息包,該回調(diào)函數(shù)就會(huì)被協(xié)議 棧調(diào)用。這既有優(yōu)點(diǎn)也有缺點(diǎn)。優(yōu)點(diǎn)是既然應(yīng)用程序和TCP/IP協(xié)議棧駐留在同一個(gè)進(jìn)程中,那么發(fā)送和接收數(shù)據(jù)就不再產(chǎn)生進(jìn)程切換。主要缺點(diǎn)是應(yīng)用程序不 能使自己陷入長期的連續(xù)運(yùn)算中,這樣會(huì)導(dǎo)致通訊性能下降,原因是TCP/IP處理與連續(xù)運(yùn)算是不能并行發(fā)生的。這個(gè)缺點(diǎn)可以通過把應(yīng)用程序分為兩部分來克 服,一部分處理通訊,一部分處理運(yùn)算。
lwip API把接收與處理放在一個(gè)線程里面。這樣只要處理流程稍微被延遲,接收就會(huì)被阻塞,直接造成頻繁丟包、響應(yīng)不及時(shí)等嚴(yán)重問題。因此,接收與協(xié)議處理必須 分開。LwIP的作者顯然已經(jīng)考慮到了這一點(diǎn),他為我們提供了 tcpip_input() 函數(shù)來處理這個(gè)問題, 雖然他并沒有在 rawapi 一文中說明。 講到這里,讀者應(yīng)該知道tcpip_input()函數(shù)投遞的消息從哪里來的答案了吧,沒錯(cuò),它們來自于由底層網(wǎng)絡(luò)驅(qū)動(dòng)組成的接收線程。我們?cè)诰帉懢W(wǎng)絡(luò)驅(qū)動(dòng)時(shí), 其接收部分以任務(wù)的形式創(chuàng)建。 數(shù)據(jù)包到達(dá)后, 去掉以太網(wǎng)包頭得到IP包, 然后直接調(diào)用tcpip_input()函數(shù)將其 投遞到mbox郵箱。投遞結(jié)束,接收任務(wù)繼續(xù)下一個(gè)數(shù)據(jù)包的接收,而被投遞得IP包將由TCPIP線程繼續(xù)處理。這樣,即使某個(gè)IP包的處理時(shí)間過長也不 會(huì)造成頻繁丟包現(xiàn)象的發(fā)生。這就是lwip API。
BSD API提供了基于open-read-write-close模型的UNIX標(biāo)準(zhǔn)API,它的最大特點(diǎn)是使應(yīng)用程序移植到其它系統(tǒng)時(shí)比較容易,但用在嵌入式系統(tǒng)中效率比較低,占用資源多。這對(duì)于我們的嵌入式應(yīng)用有時(shí)是不能容忍的
lwIP協(xié)議棧主要關(guān)注的是怎么樣減少內(nèi)存的使用和代碼的大小,這樣就可以讓lwIP適用于資源有限的小型平臺(tái)例如嵌入式系統(tǒng)。為了簡(jiǎn)化處理過程和內(nèi)存要求,lwIP對(duì)API進(jìn)行了裁減,可以不需要復(fù)制一些數(shù)據(jù)。
其主要特性如下:
(1)支持多網(wǎng)絡(luò)接口下的IP轉(zhuǎn)發(fā);
(2)支持ICMP協(xié)議;
(3)包括實(shí)驗(yàn)性擴(kuò)展的UDP(用戶數(shù)據(jù)報(bào)協(xié)議);
(4)包括阻塞控制、RTT 估算、快速恢復(fù)和快速轉(zhuǎn)發(fā)的TCP(傳輸控制協(xié)議);
(5)提供專門的內(nèi)部回調(diào)接口(Raw API),用于提高應(yīng)用程序性能;
(6)可選擇的Berkeley接口API (在多線程情況下使用) 。
(7)在最新的版本中支持ppp
(8) 新版本中增加了的IP fragment(IP分片)的支持.
(9) 支持DHCP協(xié)議,動(dòng)態(tài)分配ip地址.
3.2 幾種開源TCPIP協(xié)議概述
1、BSD TCP/IP協(xié)議棧
BSD棧歷史上是商業(yè)棧的起點(diǎn),大多數(shù)專業(yè)TCP/IP棧(VxWorks內(nèi)嵌的TCP/IP棧)是BSD棧派生的。這是因?yàn)锽SD棧在BSD許可協(xié)議下提供了這些專業(yè)棧的雛形,BSD許用證允許BSD棧以修改或未修改的形式結(jié)合這些專業(yè)棧的代碼而無須向創(chuàng)建者付版稅。同時(shí),BSD也是許多TCP/IP協(xié)議中的創(chuàng)新(如廣域網(wǎng)中餓擁塞控制和避免)的點(diǎn)。
2、uC/IP
uC/IP是由Guy Lancaster編寫的一套基于uC/OS且開放源碼的TCP/IP協(xié)議棧,亦可移植到操作系統(tǒng),是一套完全免費(fèi)的、可供研究的TCP/IP協(xié)議棧,uC/IP大部分源碼是從公開源碼BSD發(fā)布站點(diǎn)和KA9Q(一個(gè)基于DOS單任務(wù)環(huán)境運(yùn)行的TCP/IP協(xié)議棧)移植過來。uC/IP具有如下一些特點(diǎn):帶身份驗(yàn)證和報(bào)頭壓縮支持的PPP協(xié)議,優(yōu)化的單一請(qǐng)求/回復(fù)交互過程,支持IP/TCP/UDP協(xié)議,可實(shí)現(xiàn)的網(wǎng)絡(luò)功能較為強(qiáng)大,并可裁減。UCIP協(xié)議棧被為一個(gè)帶最小化用戶接口及可應(yīng)用串行鏈路網(wǎng)絡(luò)模塊。根據(jù)采用CPU、編譯器和系統(tǒng)所需實(shí)現(xiàn)協(xié)議的多少,協(xié)議棧需要的代碼容量空間在30-60KB之間。http://ucip.sourceforge.net
3、LwIP
LwIP是瑞士計(jì)算機(jī)科學(xué)院(Swedish Institute of Computer Science)的Adam Dunkels等開發(fā)的一套用于嵌入式系統(tǒng)的開放源代碼TCP/IP協(xié)議棧。LwIP的含義是Light Weight(輕型)IP協(xié)議,相對(duì)于uip。LwIP可以移植到操作系統(tǒng)上,也可以在無操作系統(tǒng)的情況下獨(dú)立運(yùn)行。LwIP TCP/IP實(shí)現(xiàn)的重點(diǎn)是在保持TCP協(xié)議主要功能的基礎(chǔ)上減少對(duì)RAM的占用,一般它只需要幾十K的RAM和40K左右的ROM就可以運(yùn)行,這使LwIP協(xié)議棧適合在低端嵌入式系統(tǒng)中使用。LwIP的特性如下:支持多網(wǎng)絡(luò)接口下的IP轉(zhuǎn)發(fā),支持ICMP協(xié)議 ,包括實(shí)驗(yàn)性擴(kuò)展的的UDP(用戶數(shù)據(jù)報(bào)協(xié)議),包括阻塞控制,RTT估算和快速恢復(fù)和快速轉(zhuǎn)發(fā)的TCP(傳輸控制協(xié)議),提供專門的內(nèi)部回調(diào)接口(Raw API)用于提高應(yīng)用程序性能,并提供了可選擇的Berkeley接口API。Svensk forskning f?r h?llbar tillv?xt| RISE或lwIP - A Lightweight TCP/IP stack - Summary [Savannah]
4、uIP
uIP是專門為8位和16位控制器設(shè)計(jì)的一個(gè)非常小的TCP/IP棧。完全用C編寫,因此可移植到各種不同的結(jié)構(gòu)和操作系統(tǒng)上,一個(gè)編譯過的??梢栽趲譑B ROM或幾百字節(jié)RAM中運(yùn)行。uIP中還包括一個(gè)HTTP服務(wù)器作為服務(wù)內(nèi)容。許可:BSD許用證Svensk forskning f?r h?llbar tillv?xt| RISE
uIP是一個(gè)完全由C語言編寫的開源軟件, 它的文檔和源代碼可用于商業(yè)和非商業(yè)用途, 它已經(jīng)移植到了大部分的8位微控制器, 而且已在很多的嵌入式產(chǎn)品和項(xiàng)目中使用.
5、TinyTcp
TinyTcp 棧是TCP/IP的一個(gè)非常小和簡(jiǎn)單的實(shí)現(xiàn),它包括一個(gè)FTP客戶。TinyTcp是為了燒入ROM設(shè)計(jì)的并且現(xiàn)在開始對(duì)大端結(jié)構(gòu)似乎是有用的(初始目標(biāo)是68000芯片)。TinyTcp也包括一個(gè)簡(jiǎn)單的以太網(wǎng)驅(qū)動(dòng)器用于3COM多總線卡http://ftp.ecs.soton.ac.uk/pub/elks/utils/tiny-tcp.txt
選擇一個(gè)開源協(xié)議??梢詮乃膫€(gè)方面來考慮:
是否提供易用的底層硬件API,即與硬件平臺(tái)的無關(guān)性;
協(xié)議棧需要調(diào)用的系統(tǒng)函數(shù)接口是否容易構(gòu)造,另一個(gè)對(duì)于應(yīng)用支持程度。
最關(guān)鍵的是占用的系統(tǒng)資源是否在可接受范圍內(nèi),有裁減優(yōu)化的空間否? 其中,
BSD ??赏暾麑?shí)現(xiàn)TCP/IP協(xié)議,但代碼龐大,70KB-150KB之間,裁減優(yōu)化有難度,
uIP和TinyTcp代碼容量小巧,實(shí)現(xiàn)功能精簡(jiǎn),限制了在一些較高要求場(chǎng)合下的應(yīng)用,如可靠性與大容量數(shù)據(jù)傳輸。
LwIP和uC/IP是同量級(jí)別的兩個(gè)開源協(xié)議棧,兩者代碼容量和實(shí)現(xiàn)功能相似,LwIP沒有操作系統(tǒng)針對(duì)性,它將協(xié)議棧與平臺(tái)相關(guān)的代碼抽象出來,用戶如果要移植到自己的系統(tǒng),需要完成該部分代碼的封裝,并為網(wǎng)絡(luò)應(yīng)用支持提供了API接口的可選性。
uC/IP協(xié)議最初是針對(duì)uC/OS設(shè)計(jì),為方便用戶移植實(shí)現(xiàn),同樣也抽象了協(xié)議棧與平臺(tái)相關(guān)代碼,但是協(xié)議棧所需調(diào)用的系統(tǒng)函數(shù)大多參照uC/OS內(nèi)核函數(shù)原型設(shè)計(jì),并提供了協(xié)議棧的函數(shù),方便用戶參考,其不足在于該協(xié)議棧對(duì)網(wǎng)絡(luò)應(yīng)用支持不足。
根據(jù)以上分析,從應(yīng)用和開發(fā)的角度看,似乎LWIP更得到了網(wǎng)上很多朋友使用的青睞;uC/IP在文檔支持與軟件升級(jí)管理上有很多不足,但是它最初是針對(duì)UC/OS而設(shè)計(jì),如果選用UC/OS作為軟件基礎(chǔ)的話,在系統(tǒng)函數(shù)構(gòu)造方面有優(yōu)勢(shì)。當(dāng)然你選擇其他操作系統(tǒng)的話,可參照OS_NULL文件夾下的文件修改。 以上的這些開源協(xié)議棧也并非免費(fèi),拿來就可以用,據(jù)我所知,UC/OS的母公司推出UC/OS-TCP/IP花了6人*2年的工作量,國內(nèi)某公司使用LWIP作為移植的參照,花了4-5人*2年的工作量來測(cè)試與優(yōu)化協(xié)議,使用商用TCP/IP棧的高費(fèi)用就不足為奇了。 作為廣大的愛好者學(xué)習(xí)而言,如果只是跑跑原型,實(shí)驗(yàn)一下效果,以上的幾種開源協(xié)議棧都提供了測(cè)試的例子,應(yīng)該是不錯(cuò)的選擇。
終上所述:LWIP可優(yōu)先考慮,參考的資料較多
四、LWIP協(xié)議棧移植
4.1 LWIP源碼下載
源碼下載地址: http://ftp.yzu.edu.tw/nongnu/lwip/
?
下載LWIP1.4.1版本、并下載contrib-1.4.1版本。
?
4.2 將LWIP源碼加入到工程目錄
?
?
?
?
?
?
?
?
?
?
?
?
4.3 配置lwipopts.h文件
?
?
?
?
4.4 修改ethernetif.c文件
ethernetif.c文件默認(rèn)是不編譯的,該文件是網(wǎng)卡底層接口的模板文件,需要根據(jù)修改網(wǎng)卡發(fā)送接口和接收接口。
?
?
?
?
4.5 修改sys_arch.c文件
修改sys_arch.c只是留下sys_now()函數(shù),其他代碼全部刪除掉。刪除windows.h頭文件。
sys_now()函數(shù)用于返回一個(gè)32位的系統(tǒng)時(shí)鐘,單位是ms。沒有操作系統(tǒng)的情況下,使用定時(shí)器提供時(shí)間即可。
?
4.6 新建lwip_config.c文件
在LWIP/app目錄下新建一個(gè)lwip_config.c/lwip_config.h文件。用于編寫動(dòng)態(tài)IP地址分配處理代碼,和LWIP事物輪詢、初始化代碼。
編寫一個(gè)LWIP初始化配置函數(shù),向LWIP協(xié)議棧添加一個(gè)新的網(wǎng)卡設(shè)備
/*
函數(shù)功能: LWIP協(xié)議棧初始化
*/
void lwip_config_init(void)
{
ip_addr_t ipaddr; //IP地址
ip_addr_t netmask; //子網(wǎng)掩碼
ip_addr_t gw; //網(wǎng)關(guān)
//全部初始化為0 -因?yàn)槭褂昧藙?dòng)態(tài)IP地址分配
ipaddr.addr=0;
netmask.addr=0;
gw.addr=0;
/*1. 初始化LWIP內(nèi)核*/
lwip_init();
/*2. 向網(wǎng)卡列表中添加一個(gè)網(wǎng)絡(luò)設(shè)備*/
netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,eernetif_init,eernet_input);
/*3. 開啟DHCP服務(wù) */
dhcp_start(&lwip_netif);
/*4. 設(shè)置netif為默認(rèn)網(wǎng)口*/
netif_set_default(&lwip_netif);
/*5. 打開netif網(wǎng)口*/
netif_set_up(&lwip_netif);
}
編寫LWIP事物輪詢函數(shù)與DHCP處理函數(shù)
u32 TCPTimer=0; //TCP查詢計(jì)時(shí)器
u32 ARPTimer=0; //ARP查詢計(jì)時(shí)器
u32 DHCPfineTimer=0; //DHCP精細(xì)處理計(jì)時(shí)器
u32 DHCPcoarseTimer=0; //DHCP粗糙處理計(jì)時(shí)器
u32 DHCP_State=1; //保存DHCP狀態(tài) 1表示沒有分配成功 0表示分配成功
/*
函數(shù)功能: LWIP輪詢?nèi)蝿?wù)
*/
void lwip_periodic_handle()
{
//每250ms調(diào)用一次tcp_tmr()函數(shù)
if(TCPTimer >= TCP_TMR_INTERVAL)
{
TCPTimer=0;
tcp_tmr(); //處理TCP協(xié)議請(qǐng)求
}
//ARP每5s周期性調(diào)用一次
if(ARPTimer >= ARP_TMR_INTERVAL)
{
ARPTimer=0;
etharp_tmr();
}
//每500ms調(diào)用一次dhcp_fine_tmr()
if(DHCPfineTimer >= DHCP_FINE_TIMER_MSECS)
{
DHCPfineTimer=0;
dhcp_fine_tmr(); //動(dòng)態(tài)IP地址分配的事物處理
if(DHCP_State)lwip_dhcp_process_handle(); //DHCP處理
}
//每60s執(zhí)行一次DHCP粗糙處理
if(DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS)
{
DHCPcoarseTimer=0;
dhcp_coarse_tmr();
}
}
//lwip控制結(jié)構(gòu)體
typedef struct
{
u8 remoteip[4]; //服務(wù)器主機(jī)IP地址
u8 ip[4]; //本機(jī)IP地址
u8 netmask[4]; //子網(wǎng)掩碼
u8 gateway[4]; //默認(rèn)網(wǎng)關(guān)的IP地址
}__lwip_dev;
extern __lwip_dev lwipdev; //lwip信息結(jié)構(gòu)體
__lwip_dev lwipdev; //lwip信息結(jié)構(gòu)體
/*
函數(shù)功能: DHCP處理任務(wù)
*/
void lwip_dhcp_process_handle(void)
{
u32 ip=0,netmask=0,gw=0;
ip=lwip_netif.ip_addr.addr; //讀取新IP地址
netmask=lwip_netif.netmask.addr; //讀取子網(wǎng)掩碼
gw=lwip_netif.gw.addr; //讀取默認(rèn)網(wǎng)關(guān)
if(ip!=0) //正確獲取到IP地址的時(shí)候
{
DHCP_State=0; //表示分配成功
//解析出通過DHCP獲取到的IP地址
lwipdev.ip[3]=(uint8_t)(ip>>24);
lwipdev.ip[2]=(uint8_t)(ip>>16);
lwipdev.ip[1]=(uint8_t)(ip>>8);
lwipdev.ip[0]=(uint8_t)(ip);
printf("動(dòng)態(tài)分配
IP:..............%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
//解析通過DHCP獲取到的子網(wǎng)掩碼地址
lwipdev.netmask[3]=(uint8_t)(netmask>>24);
lwipdev.netmask[2]=(uint8_t)(netmask>>16);
lwipdev.netmask[1]=(uint8_t)(netmask>>8);
lwipdev.netmask[0]=(uint8_t)(netmask);
printf("子網(wǎng)掩
碼............%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);
//解析出通過DHCP獲取到的默認(rèn)網(wǎng)關(guān)
lwipdev.gateway[3]=(uint8_t)(gw>>24);
lwipdev.gateway[2]=(uint8_t)(gw>>16);
lwipdev.gateway[1]=(uint8_t)(gw>>8);
lwipdev.gateway[0]=(uint8_t)(gw);
printf("網(wǎng)
關(guān).........%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
}
}
4.7 配置一個(gè)定時(shí)器提供時(shí)間基準(zhǔn)
?
4.8 初始化lwip動(dòng)態(tài)獲取IP地址
?
4.9 LWIP內(nèi)存配置選擇
LWIP可以選擇使用系統(tǒng)庫自帶的函數(shù)malloc/free進(jìn)行管理空間,也可以使用lwip自己的內(nèi)存管理函數(shù)進(jìn)行管理,源碼默認(rèn)就是使用lwip自己的內(nèi)存管理方法,就是在初始化內(nèi)存的時(shí)候定義一個(gè)數(shù)組,數(shù)組的大小在lwipopts.h文件MEM_SIZE宏定義的。
?
?
?
?
五、LWIP函數(shù)使用(RAW編程接口)
5.1 LWIP初始化配置
?
ip_addr_t ipaddr; //IP地址
ip_addr_t netmask; //子網(wǎng)掩碼
ip_addr_t gw; //網(wǎng)關(guān)
//全部初始化為0 -因?yàn)槭褂昧藙?dòng)態(tài)IP地址分配
ipaddr.addr=0;
netmask.addr=0;
gw.addr=0;
/*1. 初始化LWIP內(nèi)核*/
lwip_init();
/*2. 向網(wǎng)卡列表中添加一個(gè)網(wǎng)絡(luò)設(shè)備*/
netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,eernetif_init,eernet_input);
/*3. 開啟DHCP服務(wù) */
dhcp_start(&lwip_netif);
/*4. 設(shè)置netif為默認(rèn)網(wǎng)口*/
netif_set_default(&lwip_netif);
/*5. 打開netif網(wǎng)口*/
netif_set_up(&lwip_netif);
5.2 LWIP輪詢函數(shù)處理
LWIP輪詢期間:
1. 推薦每250ms周期性調(diào)用一次tcp_tmr()函數(shù),處理TCP協(xié)議請(qǐng)求。
超時(shí)時(shí)間LWIP使用TCP_TMR_INTERVAL宏進(jìn)行了定義。
2. 推薦每5s周期性調(diào)用一次etharp_tmr()函數(shù),清除ARP表中過期的數(shù)據(jù)。
超時(shí)時(shí)間LWIP使用ARP_TMR_INTERVAL宏進(jìn)行了定義。
3. (如果開啟了動(dòng)態(tài)IP分配功能)推薦每500ms周期性調(diào)用一次dhcp_fine_tmr()函數(shù),處理DHCP動(dòng)態(tài)IP地址分配請(qǐng)求。 如果IP地址獲取成功,將會(huì)放在初始化時(shí)注冊(cè)的網(wǎng)絡(luò)設(shè)備結(jié)構(gòu)體里(struct netif)。
超時(shí)時(shí)間LWIP使用DHCP_FINE_TIMER_MSECS宏進(jìn)行了定義。
4. (如果開啟了動(dòng)態(tài)IP分配功能)推薦每60s調(diào)用一次dhcp_coarse_tmr()函數(shù),用于檢查DHCP租約時(shí)間,并進(jìn)行重新綁定。
超時(shí)時(shí)間LWIP使用DHCP_COARSE_TIMER_MSECS宏進(jìn)行了定義。
5. 在LWIP運(yùn)行期間,當(dāng)網(wǎng)卡收到數(shù)據(jù)時(shí),還需要調(diào)用ethernetif_input函數(shù)讀取網(wǎng)卡數(shù)據(jù)。
在函數(shù)ethernetif_input()主要完成兩個(gè)工作
1、調(diào)用low_level_input(); 讀取網(wǎng)卡實(shí)際數(shù)據(jù)。
2、調(diào)用netif->input();
所以,為了能夠?qū)崟r(shí)的讀取數(shù)據(jù),需要最快的速度輪詢調(diào)用ethernetif_input函數(shù)。
5.3 LWIP編程RAW接口函數(shù)
tcp_new() 創(chuàng)建一個(gè) TCP 的 PCB 控制塊
tcp_bind() 為 TCP 的 PCB 控制塊綁定一個(gè)本地 IP 地址和端口號(hào)
tcp_listen() 開始 TCP 的 PCB 監(jiān)聽
tcp_accept() 控制塊 accept字段注冊(cè)的回調(diào)函數(shù),偵聽到連接時(shí)被調(diào)用
tcp_accepted() 通知 LWIP 協(xié)議棧一個(gè) TCP 連接被接受了
tcp_conect() 連接遠(yuǎn)端主機(jī)
tcp_write() 構(gòu)造一個(gè)報(bào)文并放到控制塊的發(fā)送緩沖隊(duì)列中
tcp_sent() 控制塊 sent 字段注冊(cè)的回調(diào)函數(shù),數(shù)據(jù)發(fā)送成功后被回調(diào)
tcp_output() 將發(fā)送緩沖隊(duì)列中的數(shù)據(jù)發(fā)送出去
tcp_recv()控制塊 recv 字段注冊(cè)的回調(diào)函數(shù),當(dāng)接收到新數(shù)據(jù)時(shí)被調(diào)用
tcp_recved()當(dāng)程序處理完數(shù)據(jù)后一定要調(diào)用這個(gè)函數(shù),通知內(nèi)核更新接收窗口
tcp_poll() 控制塊 poll 字段注冊(cè)的回調(diào)函數(shù),該函數(shù)周期性調(diào)用
tcp_close() 關(guān)閉一個(gè) TCP 連接
tcp_err() 控制塊 err 字段注冊(cè)的回調(diào)函數(shù),遇到錯(cuò)誤時(shí)被調(diào)用
tcp_abort() 中斷 TCP 連接
5.4 創(chuàng)建TCP服務(wù)器示例
下面演示了TCP服務(wù)器創(chuàng)建步驟,測(cè)試服務(wù)器是否正常。
u8 TCP_Create(u16_t port)
{
struct tcp_pcb *pcb=NULL;
pcb=tcp_new(); //創(chuàng)建套接字
if(pcb==NULL)return 1;
if(tcp_bind(pcb,IP_ADDR_ANY,port)!=ERR_OK)return 2; //綁定端口號(hào)
pcb=tcp_listen(pcb); //開始監(jiān)聽
tcp_accept(pcb,TCP_accept);//等待連接
return 0;
}
err_t TCP_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
u8 addr[4];
//tcp_setprio(newpcb, TCP_PRIO_MIN); 設(shè)置優(yōu)先級(jí)
printf("有新的客戶端連接!\n");
addr[3]=(newpcb->remote_ip.addr>>24)&0xFF;
addr[2]=(newpcb->remote_ip.addr>>16)&0xFF;
addr[1]=(newpcb->remote_ip.addr>>8)&0xFF;
addr[0]=(newpcb->remote_ip.addr>>0)&0xFF;
printf("ip地址:%d.%d.%d.%d\n",addr[0],addr[1],addr[2],addr[3]);
printf("端口號(hào):%d\n",newpcb->remote_port);
printf("當(dāng)前隊(duì)列剩余字節(jié):%d\n",tcp_sndbuf(newpcb));
tcp_write(newpcb,"1234567890",10,1); //將要發(fā)送的數(shù)據(jù)提交到發(fā)送隊(duì)列(不會(huì)立即發(fā)送)
tcp_output(newpcb); //提示系統(tǒng)現(xiàn)在,發(fā)送數(shù)據(jù)
tcp_sent(newpcb,TCP_sent); //發(fā)送成功的回調(diào)函數(shù)
tcp_recv(newpcb,TCP_recv);
return ERR_OK;
}
err_t TCP_sent(void *arg, struct tcp_pcb *tpcb,u16_t len)
{
printf("成功發(fā)送:%d字節(jié)\n",len);
//tcp_close(tpcb); //關(guān)閉客戶端連接
return ERR_OK;
}
u8 rx_buff[1024];
err_t TCP_recv(void *arg, struct tcp_pcb *tpcb,struct pbuf *p, err_t err)
{
u32 rx_cnt=0;
struct pbuf *q;
memset(rx_buff,0,sizeof(rx_buff));
if(p==NULL)
{
printf("客戶端已經(jīng)斷開連接!\n");
}
else
{
for(q=p;q!=NULL;q=q->next)
{
memcpy(rx_buff+rx_cnt,q->payload,q->len);
rx_cnt+=q->len;
}
pbuf_free(p); //釋放PUFF
printf("成功接收:%d字節(jié)\n",rx_cnt);
printf("收到的數(shù)據(jù)=%s\n",rx_buff);
}
return ERR_OK;
}
5.5 創(chuàng)建TCP客戶端示例
u8 TCP_Create(u16_t port)
{
struct tcp_pcb *pcb=NULL;
pcb=tcp_new(); //創(chuàng)建套接字
ip_addr_t ipaddr;
if(pcb==NULL)return 1;
IP4_ADDR(&ipaddr,192,168,31,54); //在ip_addr.h里定義
tcp_connect(pcb,&ipaddr,port,TCP_connected);
return 0;
}
err_t TCP_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
u8 addr[4];
//tcp_setprio(newpcb, TCP_PRIO_MIN); 設(shè)置優(yōu)先級(jí)
printf("服務(wù)器連接成功!\n");
addr[3]=(tpcb->remote_ip.addr>>24)&0xFF;
addr[2]=(tpcb->remote_ip.addr>>16)&0xFF;
addr[1]=(tpcb->remote_ip.addr>>8)&0xFF;
addr[0]=(tpcb->remote_ip.addr>>0)&0xFF;
printf("服務(wù)器ip地址:%d.%d.%d.%d\n",addr[0],addr[1],addr[2],addr[3]);
printf("服務(wù)器端口號(hào):%d\n",tpcb->remote_port);
printf("當(dāng)前隊(duì)列剩余字節(jié):%d\n",tcp_sndbuf(tpcb));
tcp_write(tpcb,"1234567890",10,1); //將要發(fā)送的數(shù)據(jù)提交到發(fā)送隊(duì)列(不會(huì)立即發(fā)送)
tcp_output(tpcb); //提示系統(tǒng)現(xiàn)在,發(fā)送數(shù)據(jù)
tcp_sent(tpcb,TCP_sent); //發(fā)送成功的回調(diào)函數(shù)
tcp_recv(tpcb,TCP_recv);
return ERR_OK;
}
err_t TCP_sent(void *arg, struct tcp_pcb *tpcb,u16_t len)
{
printf("成功發(fā)送:%d字節(jié)\n",len);
//tcp_close(tpcb); //關(guān)閉客戶端連接
return ERR_OK;
}
u8 rx_buff[1024];
err_t TCP_recv(void *arg, struct tcp_pcb *tpcb,struct pbuf *p, err_t err)
{
u32 rx_cnt=0;
struct pbuf *q;
memset(rx_buff,0,sizeof(rx_buff));
if(p==NULL)
{
printf("服務(wù)器已經(jīng)斷開連接!\n");
}
else
{
for(q=p;q!=NULL;q=q->next)
{
memcpy(rx_buff+rx_cnt,q->payload,q->len);
rx_cnt+=q->len;
}
printf("成功接收:%d字節(jié)\n",rx_cnt);
printf("收到的數(shù)據(jù)=%s\n",rx_buff);
}
return ERR_OK;
}
-
網(wǎng)卡
+關(guān)注
關(guān)注
3文章
301瀏覽量
27312 -
STM32
+關(guān)注
關(guān)注
2264文章
10854瀏覽量
354313 -
LwIP
+關(guān)注
關(guān)注
2文章
85瀏覽量
27056 -
FSMC
+關(guān)注
關(guān)注
0文章
54瀏覽量
38088 -
keil5
+關(guān)注
關(guān)注
6文章
44瀏覽量
20626
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論