24.1 DM9000概述
DM9000是一款完全集成的、性價比高、引腳數(shù)少、帶有通用處理器接口的單芯片快速以太網(wǎng)控制器。一個10/100MPHY和4K雙字的SRAM,它是出于低功耗和高性能目的設(shè)計的,其IO端口支持3.3V與5V電壓。DM9000為適應(yīng)各種處理器提供了8位、16位數(shù)據(jù)接口訪問內(nèi)部存儲器。DM9000協(xié)議層接口完全支持使用10Mbps下3類、4類、5類非屏蔽雙絞線和100Mbps下5類非屏蔽雙絞線,這是完全遵照IEEE802.3u標(biāo)準(zhǔn)。它的自動協(xié)商功能將自動完成DM9000配置以使其發(fā)揮出最佳性能,它還支持IEEE802.3x全雙工流量控制,DM9000的特性如下:
(1)支持處理器接口:I/O口的字節(jié)或字命令對內(nèi)部存儲器進行讀寫操作
(2)集成自適應(yīng)(AUTO-MDIX)10/100M收發(fā)器
(3)半雙工模式流量控制的背壓模式
(4)IEEE802.3x全雙工模式的流量控制
(5)支持喚醒幀,鏈路狀態(tài)改變和遠程喚醒
(6)內(nèi)置16K字節(jié)SRAM
(7)內(nèi)置3.3V至2.5V的調(diào)節(jié)器
(8)支持IP/TCP/UDP的校驗和生成以及校驗支持MAC接口
(9)支持自動加載EEPROM里面生產(chǎn)商ID和產(chǎn)品ID
(10)可選EEPROM配置
(11)超低功耗模式
A.功率降低模式(電纜偵測)
B.掉電模式
C.可選擇1:1或1.25:1變壓比例降低額外功率
(12)兼容3.3V和5.0V輸入輸出電壓
DM9000的結(jié)構(gòu)框圖如下圖所示。
1、DM9000中斷引腳電平設(shè)置
DM9000的INT引腳為中斷輸出引腳,默認(rèn)情況下該引腳高電平有效??梢酝ㄟ^設(shè)置DM9000的EECK引腳來改變INT的有效電平,當(dāng)EECK拉高以后,INT低電平有效,否則的話INT是高電平有效的。
2、DM9000數(shù)據(jù)位寬設(shè)置
DM9000支持8位和16位兩種數(shù)據(jù)位寬,可以通過DM9000的EECS引腳設(shè)置其數(shù)據(jù)位寬,當(dāng)EECS上拉的時候DM9000選擇8位數(shù)據(jù)位寬,否則的話選擇16位數(shù)據(jù)位寬。一般設(shè)置默認(rèn)位寬為16位。
3、DM9000直接內(nèi)存訪問控制DMAC
DM9000支持DMA方式以簡化對內(nèi)部存儲器的訪問。在我們編程寫好內(nèi)部存儲器地址后,就可以用一個讀/寫命令偽指令把當(dāng)前數(shù)據(jù)加載到內(nèi)部數(shù)據(jù)緩沖區(qū),這樣,內(nèi)部存儲器指定位置就可以被讀/寫命令寄存器訪問。存儲器地址將會自動增加,增加的大小與當(dāng)前總線操作模式相同(8-bit或16-bit),接著下一個地址數(shù)據(jù)將會自動加載到內(nèi)部數(shù)據(jù)緩沖區(qū)。內(nèi)部存儲器空間大小為16K字節(jié)。前3K字節(jié)單元用作發(fā)送包的緩沖區(qū),其他13K字節(jié)用作接收包的緩沖區(qū)。所以在寫存儲器操作時,如果地址越界(即超出3K空間),在IMR寄存器bit7置位的情況下,地址指針將會返回到存儲器0地址處。同樣,在讀存儲器操作時,如果地址越界(即超出16K空間),在IMR寄存器bit7置位的情況下,地址指針將會返回到存儲器0C00H地址處。
4、DM9000數(shù)據(jù)包發(fā)送
DM9000有兩個發(fā)送數(shù)據(jù)包:index1和index2,同時存儲在TXSRAM中。發(fā)送控制寄存器控制循環(huán)冗余校驗碼和填充的插入,其狀態(tài)分別記錄在發(fā)送狀態(tài)寄存器1和發(fā)送狀態(tài)寄存器2中。發(fā)送器的起始地址為00H,在軟件或硬件復(fù)位后,默認(rèn)的數(shù)據(jù)發(fā)送包為index1。首先,將數(shù)據(jù)寫入TXSRAM中,然后,在發(fā)送數(shù)據(jù)包長度寄存器中把數(shù)據(jù)字節(jié)數(shù)寫入字節(jié)計數(shù)寄存器。置位發(fā)送控制寄存器(02H)的bit0位,則DM9000開始發(fā)送index1數(shù)據(jù)包。在index1數(shù)據(jù)包發(fā)送結(jié)束之前,數(shù)據(jù)發(fā)送包index2被移入TXSRAM中。在index1數(shù)據(jù)包發(fā)送結(jié)束后,將index2數(shù)據(jù)字節(jié)數(shù)寫入字節(jié)計數(shù)寄存器中,然后,置位發(fā)送控制寄存器(02H)的bit0位,則index2數(shù)據(jù)包開始發(fā)送。以此類推,后面的數(shù)據(jù)包都以此方式進行發(fā)送。
5、DM9000數(shù)據(jù)包接收
RXSRAM是一個環(huán)形數(shù)據(jù)結(jié)構(gòu)。在軟件或硬件復(fù)位后,RXSRAM的起始地址為C00H。每個接收數(shù)據(jù)包都包含有CRC校驗域,數(shù)據(jù)域,以及緊跟其后的4字節(jié)包頭域。4字節(jié)包頭格式為:01H、狀態(tài)、BYTE_COUNT低、BYTE_COUNT高。請注意:每個接收包的起始地址處在適當(dāng)?shù)牡刂愤吔纾@取決于當(dāng)前總線操作模式(8bit或者16bit)。DM9000是通過16位數(shù)據(jù)總線,掛在STM32的FSMC上面,DM9000的片選由FSMC_NE2控制,CMD則由FSMC_A7控制。這個連接方法,類似于TFTLCD顯示,總共用到了22個IO口。
24.2 TCP/IP協(xié)議概述
TCP/IP中文名為傳輸控制協(xié)議/因特網(wǎng)互聯(lián)協(xié)議,又名網(wǎng)絡(luò)通訊協(xié)議,是Internet最基本的協(xié)議、Internet國際互聯(lián)網(wǎng)絡(luò)的基礎(chǔ),由網(wǎng)絡(luò)層的IP協(xié)議和傳輸層的TCP協(xié)議組成。TCP/IP定義了電子設(shè)備如何連入因特網(wǎng),以及數(shù)據(jù)如何在它們之間傳輸?shù)臉?biāo)準(zhǔn)。協(xié)議采用了4層的層級結(jié)構(gòu),每一層都呼叫它的下一層所提供的協(xié)議來完成自己的需求。通俗而言:TCP負(fù)責(zé)發(fā)現(xiàn)傳輸?shù)膯栴},一有問題就發(fā)出信號,要求重新傳輸,直到所有數(shù)據(jù)安全正確地傳輸?shù)侥康牡?。而IP是給因特網(wǎng)的每一臺聯(lián)網(wǎng)設(shè)備規(guī)定一個地址。
TCP/IP協(xié)議不是TCP和IP這兩個協(xié)議的合稱,而是指因特網(wǎng)整個TCP/IP協(xié)議族。從協(xié)議分層模型方面來講,TCP/IP由四個層次組成:網(wǎng)絡(luò)接口層、網(wǎng)絡(luò)層、傳輸層、應(yīng)用層。OSI是傳統(tǒng)的開放式系統(tǒng)互連參考模型,該模型將TCP/IP分為七層:物理層、數(shù)據(jù)鏈路層(網(wǎng)絡(luò)接口層)、網(wǎng)絡(luò)層(網(wǎng)絡(luò)層)、傳輸層(傳輸層)、會話層、表示層和應(yīng)用層(應(yīng)用層)。TCP/IP模型與OSI模型對比如下表所示。
層級 | OSI模型 | TCP/IP模型 |
---|---|---|
1 | 應(yīng)用層 | 應(yīng)用層 |
2 | 表示層 | |
3 | 會話層 | |
4 | 傳輸層 | 傳輸層 |
5 | 網(wǎng)絡(luò)層 | 互聯(lián)層 |
6 | 數(shù)據(jù)鏈路層 | 鏈路層 |
7 | 物理層 |
我們剛才介紹的DM9000相當(dāng)于鏈路層,而LWIP提供的就是網(wǎng)絡(luò)層的功能,應(yīng)用層則需要用戶自己編寫代碼去實現(xiàn)。
24.3 LWIP概述
LWIP是瑞典計算機科學(xué)院(SICS)的AdamDunkels等開發(fā)的一個小型開源的TCP/IP協(xié)議棧。LWIP是輕量級IP協(xié)議,有無操作系統(tǒng)的支持都可以運行,LWIP實現(xiàn)的重點是在保持TCP協(xié)議主要功能的基礎(chǔ)上減少對RAM的占用,它只需十幾KB的RAM和40K左右的ROM就可以運行,這使LWIP協(xié)議棧適合在低端的嵌入式系統(tǒng)中使用。目前LWIP的最新版本是2.1.2。在這里采用比較常用的1.4.1版本進行介紹。
LWIP的主要特性如下:
(1)ARP協(xié)議:以太網(wǎng)地址解析協(xié)議
(2)IP協(xié)議:包括IPv4和IPv6,支持IP分片與重裝,支持多網(wǎng)絡(luò)接口下數(shù)據(jù)轉(zhuǎn)發(fā)
(3)ICMP協(xié)議:用于網(wǎng)絡(luò)調(diào)試與維護
(4)IGMP協(xié)議:用于網(wǎng)絡(luò)組管理,可以實現(xiàn)多播數(shù)據(jù)的接收
(5)UDP協(xié)議:用戶數(shù)據(jù)報協(xié)議
(6)TCP協(xié)議:支持TCP擁塞控制,RTT估計,快速恢復(fù)與重傳等
(7)提供三種用戶編程接口方式:raw/callbackAPI、sequentialAPI、BSD-stylesocketAPI
(8)DNS:域名解析
(9)SNMP:簡單網(wǎng)絡(luò)管理協(xié)議
(10)DHCP:動態(tài)主機配置協(xié)議
(11)AUTOIP:IP地址自動配置
(12)PPP:點對點協(xié)議,支持PPPoE
24.4 DM9000驅(qū)動編寫
24.4.1 DM9000寄存器介紹
(1) 網(wǎng)絡(luò)控制寄存器 :NCR
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|
- | WAKEEN | - | FCTOL | FDX | LBK | RST |
Bit 6:置位時將啟用喚醒功能。清除該位還將清除所有喚醒事件狀態(tài),軟件復(fù)位后,該位將不受影響
0:開啟
1:關(guān)閉
Bit 4:強制沖突模式,用于檢測
Bit 3:內(nèi)部PHY全雙工模式
Bit 2~Bit 1:回環(huán)模式
00:正常
01:MAC內(nèi)部回環(huán)
10:內(nèi)部PHY100M模式數(shù)字回環(huán)
11:保留
Bit 0:軟件復(fù)位,寫1啟動復(fù)位,10us后自動清零
(2) 網(wǎng)絡(luò)狀態(tài)寄存器 :NSR
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|
SPEED | LINKST | WAKEST | - | TXD2END | TX1END | RXOV | - |
Bit 7:網(wǎng)絡(luò)速度,在使用內(nèi)部PHY的情況下,0表示100Mbps,1表示10Mbps,當(dāng)LINKST=0時,該位無意義
Bit 6:連接狀態(tài)
0:連接失敗
1:連接成功
Bit 5:喚醒狀態(tài),讀或者對該位寫1清0,復(fù)位后該位不受影響
0:產(chǎn)生喚醒事件
1:沒有喚醒事件
Bit 3:發(fā)送數(shù)據(jù)包2完成標(biāo)志,讀或者對該位寫1清0
Bit 2:發(fā)送數(shù)據(jù)包1完成標(biāo)志,讀或者對該位寫1清0
Bit 1:接收緩存溢出標(biāo)志
(3) 發(fā)送控制寄存器 :TCR
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|
- | TJDIS | EXCECM | PAD_DIS2 | CRC_DIS2 | PAD_DIS1 | CRC_DIS1 | TXREQ |
Bit 6:Jabber傳輸禁止
0:進制Jabber傳輸定時器(2K字節(jié))
1:使能
Bit 5:嚴(yán)重沖突模式檢測
0:當(dāng)沖突計數(shù)多于15則終止本次數(shù)據(jù)包
1:始終嘗試發(fā)送本次數(shù)據(jù)包
Bit 4:禁止為數(shù)據(jù)包2添加填充
Bit 3:禁止為數(shù)據(jù)包2添加CRC校驗
Bit 2:禁止為數(shù)據(jù)包1添加填充
Bit 1:禁止為數(shù)據(jù)包1添加CRC校驗
Bit 0:發(fā)送請求,發(fā)送完成后自動清除該位
(4) 接收控制寄存器 :RCR
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|
- | WTDIS | DIS_LONG | DIS_CRC | ALL | RUNT | PRMSC | RXEN |
Bit 6:看門狗定時器進制
0:使能
1:禁止
Bit 5:丟棄包長度超過1522字節(jié)的數(shù)據(jù)包
Bit 4:丟棄CRC校驗錯誤的數(shù)據(jù)包
Bit 3:允許廣播
Bit 2:允許小于最小長度的數(shù)據(jù)包
Bit 1:各種模式
Bit 0:接收使能
(5) 流控制閾值寄存器 :FCTR
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|
HWOT | LWOT |
Bit 7~Bit 4:接收緩存高位溢出門限
當(dāng)接收SRAM空閑空間小于該門限值則發(fā)送一個暫停時間為FFFF H的暫停包,若該值為0,則沒有接收條件,若該值為1,則默認(rèn)值為3K字節(jié)的空閑空間
Bit 3~Bit 0:接收緩存低位溢出門限
當(dāng)接收SRAM空閑空間大于該門限值則發(fā)送一個暫停時間為0000 H的暫停包,當(dāng)溢出門限最高值的暫停包發(fā)送之后,溢出門限值最低值的暫停包才有效,默認(rèn)值為8K
(6) 背壓閾值寄存器 :BPTR
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|
BPHW | JPT |
Bit 7~Bit 4:背壓閾值最高值
當(dāng)接收SRAM空閑空間低于該閾值,則MAC將產(chǎn)生一個擁擠狀態(tài),默認(rèn)值3K字節(jié)空閑空間
Bit 3~Bit 0:擁擠狀態(tài)時間,默認(rèn)為200us
JPT值 | 擁擠狀態(tài)時間(us) | JPT值 | 擁擠狀態(tài)時間(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 |
(7) 發(fā)送控制寄存器2 :TCR2
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|
LED | RLCP | DTU | ONEPM | IFGS |
Bit 7:LED模式
0:設(shè)置LED引腳為模式0或者根據(jù)EEPROM的設(shè)定
1:設(shè)置LED引腳為模式1
Bit 6:重試沖突延遲數(shù)據(jù)包
Bit 5:禁止重新發(fā)送“underruned”數(shù)據(jù)包
Bit 4:單包模式
0:發(fā)送完成前發(fā)送最多兩個數(shù)據(jù)包的命令能被執(zhí)行
1:發(fā)送完成前發(fā)送一個數(shù)據(jù)包的命令能被執(zhí)行
Bit 3~Bit 0:幀間隔設(shè)置
0xxx:96 bit
1000:64 bit
1001:72 bit
1010:80 bit
1011:88 bit
1100:96 bit
1101:104 bit
1110:112 bit
1111:120 bit
(8) 中斷狀態(tài)寄存器 :ISR
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|
IOMODE | - | LNKCHG | UNRUN | ROO | ROS | PT | PR |
Bit 7:數(shù)據(jù)寬度選擇
0:16位模式
1:8位模式
Bit 5:連接狀態(tài)改變
Bit 4:發(fā)送“underrun”
Bit 3:接收計數(shù)器溢出
Bit 2:接收溢出
Bit 1:數(shù)據(jù)包發(fā)送
Bit 0:數(shù)據(jù)包接收
(9) 中斷狀態(tài)寄存器 :IMR
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|
PAR | - | LNKCHGI | UDRUNI | ROOI | ROSI | PTI | PRI |
Bit 7:使能SRAM讀寫指針在指針地址超過SRAM的大小時自動跳回起始位置,需要驅(qū)動程序設(shè)置該位,若設(shè)置該位,REG_F5將自動設(shè)置為0x H
Bit 5:使能連接狀態(tài)改變中斷
Bit 4:使能發(fā)送“underrun”中斷
Bit 3:使能接收計數(shù)器溢出中斷
Bit 2:使能接收溢出中斷
Bit 1:使能數(shù)據(jù)包發(fā)送中斷
Bit 0:使能數(shù)據(jù)包接收中斷
24.4.2 DM9000驅(qū)動
(1)初始化DM9000外設(shè)與寄存器配置
u8 DM9000_Init()
{
u32 temp;
//初始化和DM9000連接的IO和FSMC
RCC->AHBENR |= 1<<8 ; //使能FSMC時鐘
RCC->APB2ENR |= 1<<5 ; //使能PORTD時鐘
RCC->APB2ENR |= 1<<6 ; //使能PORTE時鐘
RCC->APB2ENR |= 1<<7 ; //使能PORTF時鐘
RCC->APB2ENR |= 1<<8 ; //使能PORTG時鐘
//PD7->DM9000_RST
GPIOD->CRH &= 0x00FFF000 ;
GPIOD->CRH |= 0xBB000BBB ;
GPIOD->CRL &= 0x0F00FF00 ;
GPIOD->CRL |= 0x30BB00BB ;
//PE
GPIOE->CRH &= 0x00000000 ;
GPIOE->CRH |= 0xBBBBBBBB ;
GPIOE->CRL &= 0x0FFFFFFF ;
GPIOE->CRL |= 0xB0000000 ;
//PF13-->FSMC_A7
GPIOF->CRH &= 0xFF0FFFFF ;
GPIOF->CRH |= 0x00B00000 ;
//PG9->NE2
GPIOG->CRH &= 0xFFFFFF0F ;
GPIOG->CRH |= 0x000000B0 ;
GPIOG->CRL &= 0xF0FFFFFF ;
GPIOG->CRL |= 0x08000000 ;
GPIOG->ODR |= 1<<6 ;
EXIT_Config( 6, 6, 1 ) ; //下降沿觸發(fā)
NVIC_Init( 0, 0, EXTI9_5_IRQn, 2 ) ; //優(yōu)先級最高
//寄存器清零
FSMC_Bank1->BTCR[ 2 ] = 0x00000000 ;
FSMC_Bank1->BTCR[ 3 ] = 0x00000000 ;
FSMC_Bank1E->BWTR[ 2 ] = 0x00000000 ;
FSMC_Bank1->BTCR[ 2 ] |= 1<<12 ; //存儲器寫使能
FSMC_Bank1->BTCR[ 2 ] |= 1<<4 ; //存儲器數(shù)據(jù)寬度為16bit
//操作BTR寄存器
FSMC_Bank1->BTCR[ 3 ] |= 3<<8 ; //數(shù)據(jù)保持時間(DATAST)為3個HCLK 4/72M=55ns
FSMC_Bank1->BTCR[ 3 ] |= 0<<4 ; //地址保持時間(ADDHLD)未用到
FSMC_Bank1->BTCR[ 3 ] |= 1<<0 ; //地址建立時間(ADDSET)為2個HCLK 2/72M=27ns
FSMC_Bank1E->BWTR[ 2 ] = 0x0FFFFFFF ; //閃存寫時序寄存器
FSMC_Bank1->BTCR[ 2 ] |= 1<<0 ; //使能BANK1區(qū)域2
temp = *( vu32* )( 0x1FFFF7E8 ) ; //獲取STM32的唯一ID的前24位作為MAC地址后三字節(jié)
dm9000cfg.mode = DM9000_AUTO ;
dm9000cfg.queue_packet_len = 0 ;
//DM9000的SRAM的發(fā)送和接收指針自動返回到開始地址,并開啟接收中斷
dm9000cfg.imr_all = IMR_PAR|IMR_PRI ;
//初始化MAC地址
dm9000cfg.mac_addr[ 0 ] = 2 ;
dm9000cfg.mac_addr[ 1 ] = 0 ;
dm9000cfg.mac_addr[ 2 ] = 0 ;
dm9000cfg.mac_addr[ 3 ] = ( temp>>16 )&0xFF ; //低三字節(jié)用STM32的唯一ID
dm9000cfg.mac_addr[ 4 ] = ( temp>>8 )&0xFFF ;
dm9000cfg.mac_addr[ 5 ] = temp&0xFF ;
//初始化組播地址
dm9000cfg.multicase_addr[ 0 ] = 0xFF ;
dm9000cfg.multicase_addr[ 1 ] = 0xFF ;
dm9000cfg.multicase_addr[ 2 ] = 0xFF ;
dm9000cfg.multicase_addr[ 3 ] = 0xFF ;
dm9000cfg.multicase_addr[ 4 ] = 0xFF ;
dm9000cfg.multicase_addr[ 5 ] = 0xFF ;
dm9000cfg.multicase_addr[ 6 ] = 0xFF ;
dm9000cfg.multicase_addr[ 7 ] = 0xFF ;
DM9000_Reset() ; //復(fù)位DM9000
delay_ms( 100 ) ;
//獲取DM9000ID
temp = DM9000_ReadReg( DM9000_VIDL ) ;
temp |= DM9000_ReadReg( DM9000_VIDH )<<8 ;
temp |= DM9000_ReadReg( DM9000_PIDL )<<16 ;
temp |= DM9000_ReadReg( DM9000_PIDH )<<24 ;
//讀取ID錯誤
if( temp!=DM9000_ID )
return 1 ;
DM9000_Set_PHYMode( dm9000cfg.mode ) ; //設(shè)置PHY工作模式
DM9000_WriteReg( DM9000_NCR, 0x00 ) ;
DM9000_WriteReg( DM9000_TCR, 0x00 ) ; //發(fā)送控制寄存器清零
DM9000_WriteReg( DM9000_BPTR, 0x3F ) ;
DM9000_WriteReg( DM9000_FCTR, 0x38 ) ;
DM9000_WriteReg( DM9000_FCR, 0x00 ) ;
DM9000_WriteReg( DM9000_SMCR, 0x00 ) ; //特殊模式
DM9000_WriteReg( DM9000_NSR, NSR_WAKEST|NSR_TX2END|NSR_TX1END ) ; //清除發(fā)送狀態(tài)
DM9000_WriteReg( DM9000_ISR, 0x0F ) ; //清除中斷狀態(tài)
DM9000_WriteReg( DM9000_TCR2, 0x80 ) ; //切換LED到mode1
//設(shè)置MAC地址和組播地址
DM9000_Set_MACAddress( dm9000cfg.mac_addr ) ;//設(shè)置MAC地址
DM9000_Set_Multicast( dm9000cfg.multicase_addr ) ; //設(shè)置組播地址
DM9000_WriteReg( DM9000_RCR, RCR_DIS_LONG|RCR_DIS_CRC|RCR_RXEN ) ;
DM9000_WriteReg( DM9000_IMR, IMR_PAR ) ;
DM9000_Get_SpeedAndDuplex() ; //獲取DM9000的連接速度和雙工狀態(tài)
DM9000_WriteReg( DM9000_IMR, dm9000cfg.imr_all ) ; //設(shè)置中斷
return 0 ;
}
注:函數(shù)中用到了未定義的數(shù)據(jù)類型,需要在sys.h中添加該類型的定義typedef volatile uint32_t vu32。
(2)DM9000內(nèi)部寄存器讀寫函數(shù)
u8 DM9000_Init()
{
u32 temp;
//初始化和DM9000連接的IO和FSMC
RCC->AHBENR |= 1<<8 ; //使能FSMC時鐘
RCC->APB2ENR |= 1<<5 ; //使能PORTD時鐘
RCC->APB2ENR |= 1<<6 ; //使能PORTE時鐘
RCC->APB2ENR |= 1<<7 ; //使能PORTF時鐘
RCC->APB2ENR |= 1<<8 ; //使能PORTG時鐘
//PD7->DM9000_RST
GPIOD->CRH &= 0x00FFF000 ;
GPIOD->CRH |= 0xBB000BBB ;
GPIOD->CRL &= 0x0F00FF00 ;
GPIOD->CRL |= 0x30BB00BB ;
//PE
GPIOE->CRH &= 0x00000000 ;
GPIOE->CRH |= 0xBBBBBBBB ;
GPIOE->CRL &= 0x0FFFFFFF ;
GPIOE->CRL |= 0xB0000000 ;
//PF13-->FSMC_A7
GPIOF->CRH &= 0xFF0FFFFF ;
GPIOF->CRH |= 0x00B00000 ;
//PG9->NE2
GPIOG->CRH &= 0xFFFFFF0F ;
GPIOG->CRH |= 0x000000B0 ;
GPIOG->CRL &= 0xF0FFFFFF ;
GPIOG->CRL |= 0x08000000 ;
GPIOG->ODR |= 1<<6 ;
EXIT_Config( 6, 6, 1 ) ; //下降沿觸發(fā)
NVIC_Init( 0, 0, EXTI9_5_IRQn, 2 ) ; //優(yōu)先級最高
//寄存器清零
FSMC_Bank1->BTCR[ 2 ] = 0x00000000 ;
FSMC_Bank1->BTCR[ 3 ] = 0x00000000 ;
FSMC_Bank1E->BWTR[ 2 ] = 0x00000000 ;
FSMC_Bank1->BTCR[ 2 ] |= 1<<12 ; //存儲器寫使能
FSMC_Bank1->BTCR[ 2 ] |= 1<<4 ; //存儲器數(shù)據(jù)寬度為16bit
//操作BTR寄存器
FSMC_Bank1->BTCR[ 3 ] |= 3<<8 ; //數(shù)據(jù)保持時間(DATAST)為3個HCLK 4/72M=55ns
FSMC_Bank1->BTCR[ 3 ] |= 0<<4 ; //地址保持時間(ADDHLD)未用到
FSMC_Bank1->BTCR[ 3 ] |= 1<<0 ; //地址建立時間(ADDSET)為2個HCLK 2/72M=27ns
FSMC_Bank1E->BWTR[ 2 ] = 0x0FFFFFFF ; //閃存寫時序寄存器
FSMC_Bank1->BTCR[ 2 ] |= 1<<0 ; //使能BANK1區(qū)域2
temp = *( vu32* )( 0x1FFFF7E8 ) ; //獲取STM32的唯一ID的前24位作為MAC地址后三字節(jié)
dm9000cfg.mode = DM9000_AUTO ;
dm9000cfg.queue_packet_len = 0 ;
//DM9000的SRAM的發(fā)送和接收指針自動返回到開始地址,并開啟接收中斷
dm9000cfg.imr_all = IMR_PAR|IMR_PRI ;
//初始化MAC地址
dm9000cfg.mac_addr[ 0 ] = 2 ;
dm9000cfg.mac_addr[ 1 ] = 0 ;
dm9000cfg.mac_addr[ 2 ] = 0 ;
dm9000cfg.mac_addr[ 3 ] = ( temp>>16 )&0xFF ; //低三字節(jié)用STM32的唯一ID
dm9000cfg.mac_addr[ 4 ] = ( temp>>8 )&0xFFF ;
dm9000cfg.mac_addr[ 5 ] = temp&0xFF ;
//初始化組播地址
dm9000cfg.multicase_addr[ 0 ] = 0xFF ;
dm9000cfg.multicase_addr[ 1 ] = 0xFF ;
dm9000cfg.multicase_addr[ 2 ] = 0xFF ;
dm9000cfg.multicase_addr[ 3 ] = 0xFF ;
dm9000cfg.multicase_addr[ 4 ] = 0xFF ;
dm9000cfg.multicase_addr[ 5 ] = 0xFF ;
dm9000cfg.multicase_addr[ 6 ] = 0xFF ;
dm9000cfg.multicase_addr[ 7 ] = 0xFF ;
DM9000_Reset() ; //復(fù)位DM9000
delay_ms( 100 ) ;
//獲取DM9000ID
temp = DM9000_ReadReg( DM9000_VIDL ) ;
temp |= DM9000_ReadReg( DM9000_VIDH )<<8 ;
temp |= DM9000_ReadReg( DM9000_PIDL )<<16 ;
temp |= DM9000_ReadReg( DM9000_PIDH )<<24 ;
//讀取ID錯誤
if( temp!=DM9000_ID )
return 1 ;
DM9000_Set_PHYMode( dm9000cfg.mode ) ; //設(shè)置PHY工作模式
DM9000_WriteReg( DM9000_NCR, 0x00 ) ;
DM9000_WriteReg( DM9000_TCR, 0x00 ) ; //發(fā)送控制寄存器清零
DM9000_WriteReg( DM9000_BPTR, 0x3F ) ;
DM9000_WriteReg( DM9000_FCTR, 0x38 ) ;
DM9000_WriteReg( DM9000_FCR, 0x00 ) ;
DM9000_WriteReg( DM9000_SMCR, 0x00 ) ; //特殊模式
DM9000_WriteReg( DM9000_NSR, NSR_WAKEST|NSR_TX2END|NSR_TX1END ) ; //清除發(fā)送狀態(tài)
DM9000_WriteReg( DM9000_ISR, 0x0F ) ; //清除中斷狀態(tài)
DM9000_WriteReg( DM9000_TCR2, 0x80 ) ; //切換LED到mode1
//設(shè)置MAC地址和組播地址
DM9000_Set_MACAddress( dm9000cfg.mac_addr ) ;//設(shè)置MAC地址
DM9000_Set_Multicast( dm9000cfg.multicase_addr ) ; //設(shè)置組播地址
DM9000_WriteReg( DM9000_RCR, RCR_DIS_LONG|RCR_DIS_CRC|RCR_RXEN ) ;
DM9000_WriteReg( DM9000_IMR, IMR_PAR ) ;
DM9000_Get_SpeedAndDuplex() ; //獲取DM9000的連接速度和雙工狀態(tài)
DM9000_WriteReg( DM9000_IMR, dm9000cfg.imr_all ) ; //設(shè)置中斷
return 0 ;
}
(3)設(shè)置MAC地址與組播地址
void DM9000_Set_MACAddress( u8 *macaddr )
{
u8 i ;
for( i=0; i<6; i++ )
DM9000_WriteReg( DM9000_PAR+i, macaddr[ i ] ) ;
}
void DM9000_Set_Multicast( u8 *multicastaddr )
{
u8 i;
for( i=0; i<8; i++ )
DM9000_WriteReg( DM9000_MAR+i, multicastaddr[ i ] ) ;
}
(4)獲取與設(shè)置PHY工作模式
void DM9000_Set_MACAddress( u8 *macaddr )
{
u8 i ;
for( i=0; i<6; i++ )
DM9000_WriteReg( DM9000_PAR+i, macaddr[ i ] ) ;
}
void DM9000_Set_Multicast( u8 *multicastaddr )
{
u8 i;
for( i=0; i<8; i++ )
DM9000_WriteReg( DM9000_MAR+i, multicastaddr[ i ] ) ;
}
(5)復(fù)位
void DM9000_Reset()
{
DM9000_RST = 0 ; //DM9000硬件復(fù)位
delay_ms( 10 ) ;
DM9000_RST = 1 ; //DM9000硬件復(fù)位結(jié)束
delay_ms( 100 ) ; //等待DM9000準(zhǔn)備就緒
DM9000_WriteReg( DM9000_GPCR, 0x01 ) ; //第1步:設(shè)置GPCR寄存器的bit0為1
DM9000_WriteReg( DM9000_GPR, 0 ) ; //第2步:設(shè)置GPR寄存器的bit1為0
DM9000_WriteReg( DM9000_NCR, 0x02|NCR_RST ) ; //第3步:軟件復(fù)位DM9000
//等待DM9000軟復(fù)位完成
do
{
delay_ms( 25 ) ;
}while( DM9000_ReadReg( DM9000_NCR )&0x01 ) ;
DM9000_WriteReg( DM9000_NCR, 0 ) ;
DM9000_WriteReg( DM9000_NCR, 0x02|NCR_RST ) ; //DM9000第2次軟復(fù)位
do
{
delay_ms( 25 ) ;
}while( DM9000_ReadReg( DM9000_NCR )&0x01 ) ;
}
(6)發(fā)送
void DM9000_SendPacket( struct pbuf *p )
{
struct pbuf *q ;
u16 pbuf_index=0 ;
u8 word[ 2 ], word_index=0 ;
DM9000_WriteReg( DM9000_IMR,IMR_PAR ) ; //關(guān)閉網(wǎng)卡中斷
DM9000->REG = DM9000_MWCMD ; //發(fā)送的數(shù)據(jù)搬到DM9000 TX SRAM中
q = p ;
//向DM9000的TX SRAM中寫入數(shù)據(jù),一次寫入兩個字節(jié)數(shù)據(jù)
//當(dāng)要發(fā)送的數(shù)據(jù)長度為奇數(shù)的時候,需要將最后一個字節(jié)單獨寫入DM9000的TX SRAM中
while( q )
{
if( pbuf_index
(7)接收
struct pbuf *DM9000_Receive_Packet()
{
struct pbuf *p ;
struct pbuf *q ;
u32 rxbyte ;
vu16 rx_status, rx_length ;
u16 *data ;
u16 dummy ;
int len ;
p = NULL ;
__error_retry:
DM9000_ReadReg( DM9000_MRCMDX ) ; //假讀
rxbyte = ( u8 )DM9000->DATA ; //進行第二次讀取
//接收到數(shù)據(jù)
if( rxbyte )
{
//rxbyte大于1,接收到的數(shù)據(jù)錯誤,掛了
if( rxbyte>1 )
{
DM9000_WriteReg( DM9000_RCR, 0x00 ) ;
DM9000_WriteReg( DM9000_ISR, 0x80 ) ;
return ( struct pbuf* )p ;
}
DM9000->REG = DM9000_MRCMD ;
rx_status = DM9000->DATA ;
rx_length = DM9000->DATA ;
p = pbuf_alloc( PBUF_RAW, rx_length, PBUF_POOL ) ; //pbufs內(nèi)存池分配pbuf
//內(nèi)存申請成功
if( p!=NULL )
{
for( q=p; q!=NULL; q=q->next )
{
data = ( u16* )q->payload ;
len = q->len ;
while( len>0 )
{
*data = DM9000->DATA ;
data ++ ;
len -= 2 ;
}
}
}
//內(nèi)存申請失敗
else
{
data = &dummy ;
len = rx_length ;
while( len )
{
*data = DM9000->DATA ;
len -= 2 ;
}
}
//根據(jù)rx_status判斷接收數(shù)據(jù)是否出現(xiàn)錯誤,如果有任何一個出現(xiàn)的話丟棄該數(shù)據(jù)幀,
//當(dāng)rx_length小于64或者大于最大數(shù)據(jù)長度的時候也丟棄該數(shù)據(jù)幀
if( ( rx_status&0xBF00 )||( rx_length<64 )||( rx_length>DM9000_PKT_MAX ) )
{
if( rx_length>DM9000_PKT_MAX )
{
DM9000_WriteReg( DM9000_NCR, NCR_RST ) ; //復(fù)位DM9000
delay_ms( 5 ) ;
}
//釋放內(nèi)存
if( p!=NULL )
pbuf_free( ( struct pbuf* )p ) ;
p = NULL ;
goto __error_retry ;
}
}
else
{
DM9000_WriteReg( DM9000_ISR, ISR_PTS ) ; //清除所有中斷標(biāo)志位
dm9000cfg.imr_all = IMR_PAR|IMR_PRI ; //重新接收中斷
DM9000_WriteReg( DM9000_IMR, dm9000cfg.imr_all ) ;
}
return ( struct pbuf* )p ;
}
24.5 LWIP協(xié)議移植
24.5.1 源碼下載
可以從官網(wǎng)進行源碼下載,LWIP官網(wǎng):http://savannah.nongnu.org/projects/lwip/
24.5.2 源碼結(jié)構(gòu)
打開從官網(wǎng)上下載下來的源碼其中包括doc,src和test三個文件夾和5個其他文件。doc文件夾下包含了幾個與協(xié)議棧使用相關(guān)的文本文檔,doc文件夾里面有兩個比較重要的文檔:rawapi.txt和sys_arch.txt。rawapi.txt告訴讀者怎么使用raw/callbackAPI進行編程,sys_arch.txt包含了移植說明,在移植的時候會用到。src文件夾是我們的重點,里面包含了LWIP的源碼。test是LWIP提供的一些測試程序。打開src源碼文件夾,src文件夾由4個文件夾組成:api、core、include、netif四個文件夾。api文件夾里面是LWIP的sequentialAPI(Netconn)和socketAPI兩種接口函數(shù)的源碼,要使用這兩種API需要操作系統(tǒng)支持。core文件夾是LWIP內(nèi)核源碼,include文件夾里面是LWIP使用到的頭文件,netif文件夾里面是與網(wǎng)絡(luò)底層接口有關(guān)的文件。
24.5.3 添加LWIP源代碼文件
(1)將lwip-2.1.2\\src目錄下的5個文件夾復(fù)制到工程文件夾中新建的LWIP文件夾中,刪除apps文件夾核include/lwip/apps文件夾。
(2)在LWIP文件夾中新建app和arch兩個文件夾。
(3)在app文件夾中新建comm,tcp_client,tcp_server和udp_demo文件夾,最終文件結(jié)構(gòu)如下圖所示。
(4)在app/comm目錄下創(chuàng)建comm.c,comm.h,lwipopts.h三個文件。
(5)在arch目錄下創(chuàng)建cc.h,cpu.h,perf.h,sys_arch.c和sys_arch.h五個文件。
(6)在include/netif目錄下創(chuàng)建ethernetif.h文件。
(7)將之前的DM9000驅(qū)動,LCD驅(qū)動和內(nèi)存管理驅(qū)動程序?qū)牍こ讨小?/p>
(8)將下列文件添加到項目LWIP-NETIF,LWIP-CORE,LWIP-API,LWIP-APP,LWIP-ARCH中。
項目目錄 | 文件 |
---|---|
LWIP-NETIF | etharp.c |
LWIP-CORE | autoip.c |
ip.c | ip_addr.c |
dns.c | init.c |
netif.c | pbuf.c |
tcp_in.c | tcp_out.c |
LWIP-API | api_lib.c |
netifapi.c | sockets.c |
LWIP-APP | comm.c |
LWIP-ARCH | sys_arch.c |
注:將lwip/core目錄下的sys.c文件與lwip/include/lwip目錄下的sys.h重命名為lwip_sys.c和lwip_sys.h,以免與SYSTEM目錄下的sys.c重名,產(chǎn)生編譯錯誤。
24.5.4 arch目錄下源碼文件的修改
(1)arch/cc.h文件代碼(該文件主要完成協(xié)議使用的數(shù)據(jù)類型的定義)
#ifndef _CC_H_
#define _CC_H_
#include "cpu.h"
#include "stdio.h"
//定義與平臺無關(guān)的數(shù)據(jù)類型
typedef unsigned char u8_t; //無符號8位整數(shù)
typedef signed char s8_t; //有符號8位整數(shù)
typedef unsigned short u16_t; //無符號16位整數(shù)
typedef signed short s16_t; //有符號16位整數(shù)
typedef unsigned long u32_t; //無符號32位整數(shù)
typedef signed long s32_t; //有符號32位整數(shù)
typedef u32_t mem_ptr_t ; //內(nèi)存地址型數(shù)據(jù)
typedef int sys_prot_t ; //臨界保護型數(shù)據(jù)
#if OS_CRITICAL_METHOD == 1
#define SYS_ARCH_DECL_PROTECT(lev)
#define SYS_ARCH_PROTECT(lev) CPU_INT_DIS()
#define SYS_ARCH_UNPROTECT(lev) CPU_INT_EN()
#endif
//method 3 is used in this port
#if OS_CRITICAL_METHOD == 3
#define SYS_ARCH_DECL_PROTECT(lev) u32_t lev
#define SYS_ARCH_PROTECT(lev) lev = OS_CPU_SR_Save()
#define SYS_ARCH_UNPROTECT(lev) OS_CPU_SR_Restore(lev)
#endif
//根據(jù)不同的編譯器定義一些符號
#if defined (__ICCARM__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES
#elif defined (__CC_ARM)
#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__GNUC__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__TASKING__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#endif
//LWIP用printf調(diào)試時使用的一些數(shù)據(jù)類型
#define U16_F "4d"
#define S16_F "4d"
#define X16_F "4x"
#define U32_F "8ld"
#define S32_F "8ld"
#define X32_F "8lx"
//宏定義
#ifndef LWIP_PLATFORM_ASSERT
#define LWIP_PLATFORM_ASSERT(x) \\
do \\
{ printf("Assertion \"%s\" failed at line %d in %s\\r\\n", x, __LINE__, __FILE__); \\
} while(0)
#endif
#ifndef LWIP_PLATFORM_DIAG
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
#endif
#endif
(2)arch/cpu.h文件代碼(負(fù)責(zé)定義CPU的大端小端模式)
#ifndef _CPU_H_
#define _CPU_H_
#define BYTE_ORDER LITTLE_ENDIAN //小端模式
#endif
(3)arch/perf.h文件代碼(用于系統(tǒng)測量與統(tǒng)計)
#ifndef _PERF_H_
#define _PERF_H_
#define PERF_START //空定義
#define PERF_STOP(x) //空定義
#endif
(4)arch/sys_arch.h文件代碼(為了與操作系統(tǒng)共存使用的獲取時間的函數(shù),用于為LWIP提供時鐘)
#ifndef _ARCH_SYS_ARCH_H_
#define _ARCH_SYS_ARCH_H_
#include "cc.h"
u32_t sys_now( void ) ;
#endif
(5)arch/sys_arch.c文件代碼
#include "lwip/debug.h"
#include "lwip/def.h"
#include "lwip/lwip_sys.h"
#include "lwip/mem.h"
#include "tim.h"
//為LWIP提供計時
extern uint32_t lwip_localtime;//lwip本地時間計數(shù)器,單位:ms
u32_t sys_now()
{
return lwip_localtime ;
}
24.5.5 app/comm目錄下源碼文件的修改
(1)app/comm.c文件代碼
#include "lwip/tcpip.h"
#include "malloc.h"
#include "delay.h"
#include "usart1.h"
__lwip_dev lwipdev ; //lwip控制結(jié)構(gòu)體
struct netif lwip_netif ; //定義一個全局的網(wǎng)絡(luò)接口
extern u32 memp_get_memorysize( void ) ; //在memp.c里面定義
extern u8_t *memp_memory ; //在memp.c里面定義
extern u8_t *ram_heap ; //在mem.c里面定義
u32 TCPTimer=0 ; //TCP查詢計時器
u32 ARPTimer=0 ; //ARP查詢計時器
u32 lwip_localtime ; //lwip本地時間計數(shù)器,單位:ms
#if LWIP_DHCP
u32 DHCPfineTimer=0 ; //DHCP精細(xì)處理計時器
u32 DHCPcoarseTimer=0 ; //DHCP粗糙處理計時器
#endif
u8 lwip_comm_mem_malloc()
{
u32 mempsize ;
u32 ramheapsize ;
mempsize = memp_get_memorysize() ; //得到memp_memory數(shù)組大小
memp_memory = mymalloc( SRAMIN, mempsize ) ; //為memp_memory申請內(nèi)存
//得到ram heap大小
ramheapsize = LWIP_MEM_ALIGN_SIZE( MEM_SIZE )+2*LWIP_MEM_ALIGN_SIZE( 4*3 )+MEM_ALIGNMENT ;
ram_heap = mymalloc( SRAMIN, ramheapsize ) ; //為ram_heap申請內(nèi)存
//有申請失敗的
if( !memp_memory||!ram_heap )
{
lwip_comm_mem_free() ;
return 1 ;
}
return 0 ;
}
void lwip_comm_mem_free()
{
myfree( SRAMIN, memp_memory ) ;
myfree( SRAMIN, ram_heap ) ;
}
void lwip_comm_default_ip_set( __lwip_dev *lwipx )
{
//默認(rèn)遠端IP為:192.168.1.100
lwipx->remoteip[ 0 ] = 192 ;
lwipx->remoteip[ 1 ] = 168 ;
lwipx->remoteip[ 2 ] = 1 ;
lwipx->remoteip[ 3 ] = 104 ;
//MAC地址設(shè)置(高三字節(jié)固定為:2.0.0,低三字節(jié)用STM32唯一ID)
lwipx->mac[ 0 ] = dm9000cfg.mac_addr[ 0 ] ;
lwipx->mac[ 1 ] = dm9000cfg.mac_addr[ 1 ] ;
lwipx->mac[ 2 ] = dm9000cfg.mac_addr[ 2 ] ;
lwipx->mac[ 3 ] = dm9000cfg.mac_addr[ 3 ] ;
lwipx->mac[ 4 ] = dm9000cfg.mac_addr[ 4 ] ;
lwipx->mac[ 5 ] = dm9000cfg.mac_addr[ 5 ] ;
//默認(rèn)本地IP為:192.168.1.30
lwipx->ip[ 0 ] = 192 ;
lwipx->ip[ 1 ] = 168 ;
lwipx->ip[ 2 ] = 1 ;
lwipx->ip[ 3 ] = 30 ;
//默認(rèn)子網(wǎng)掩碼:255.255.255.0
lwipx->netmask[ 0 ] = 255 ;
lwipx->netmask[ 1 ] = 255 ;
lwipx->netmask[ 2 ] = 255 ;
lwipx->netmask[ 3 ] = 0 ;
//默認(rèn)網(wǎng)關(guān):192.168.1.1
lwipx->gateway[ 0 ] = 192 ;
lwipx->gateway[ 1 ] = 168 ;
lwipx->gateway[ 2 ] = 1 ;
lwipx->gateway[ 3 ] = 1 ;
lwipx->dhcpstatus = 0 ; //沒有DHCP
}
u8 lwip_comm_init()
{
struct netif *Netif_Init_Flag ; //調(diào)用netif_add()函數(shù)時的返回值,用于判斷網(wǎng)絡(luò)初始化是否成功
struct ip_addr ipaddr ; //ip地址
struct ip_addr netmask ; //子網(wǎng)掩碼
struct ip_addr gw ; //默認(rèn)網(wǎng)關(guān)
//內(nèi)存申請失敗
if( lwip_comm_mem_malloc() )
return 1 ;
//初始化DM9000AEP
if( DM9000_Init() )
return 2 ;
lwip_init() ; //初始化LWIP內(nèi)核
lwip_comm_default_ip_set( &lwipdev ) ; //設(shè)置默認(rèn)IP等信息
//使用動態(tài)IP
#if LWIP_DHCP
ipaddr.addr = 0 ;
netmask.addr = 0 ;
gw.addr = 0 ;
//使用靜態(tài)IP
#else
IP4_ADDR( &ipaddr, lwipdev.ip[ 0 ], lwipdev.ip[ 1 ], lwipdev.ip[ 2 ], lwipdev.ip[ 3 ] ) ;
IP4_ADDR( &netmask, lwipdev.netmask[ 0 ], lwipdev.netmask[1] , lwipdev.netmask[ 2 ], lwipdev.netmask[ 3 ] ) ;
IP4_ADDR( &gw, lwipdev.gateway[ 0 ], lwipdev.gateway[ 1 ], lwipdev.gateway[ 2 ], lwipdev.gateway[ 3 ] );
#endif
//向網(wǎng)卡列表中添加一個網(wǎng)口
Netif_Init_Flag = netif_add( &lwip_netif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input ) ;
//如果使用DHCP的話
#if LWIP_DHCP
lwipdev.dhcpstatus = 0 ; //DHCP標(biāo)記為0
dhcp_start( &lwip_netif ) ; //開啟DHCP服務(wù)
#endif
//網(wǎng)卡添加失敗
if( Netif_Init_Flag==NULL )
return 3 ;
//網(wǎng)口添加成功后,設(shè)置netif為默認(rèn)值,并且打開netif網(wǎng)口
else
{
netif_set_default( &lwip_netif ) ; //設(shè)置netif為默認(rèn)網(wǎng)口
netif_set_up( &lwip_netif ) ; //打開netif網(wǎng)口
}
return 0 ; //操作OK
}
void lwip_pkt_handle()
{
ethernetif_input( &lwip_netif ) ; //從網(wǎng)絡(luò)緩沖區(qū)中讀取接收到的數(shù)據(jù)包并將其發(fā)送給LWIP處理
}
void lwip_periodic_handle()
{
#if LWIP_TCP
//每250ms調(diào)用一次tcp_tmr()函數(shù)
if( lwip_localtime-TCPTimer>=TCP_TMR_INTERVAL )
{
TCPTimer = lwip_localtime ;
tcp_tmr() ;
}
#endif
//ARP每5s周期性調(diào)用一次
if( ( lwip_localtime-ARPTimer )>=ARP_TMR_INTERVAL )
{
ARPTimer = lwip_localtime ;
etharp_tmr() ;
}
//如果使用DHCP的話
#if LWIP_DHCP
//每500ms調(diào)用一次dhcp_fine_tmr()
if( lwip_localtime-DHCPfineTimer>=DHCP_FINE_TIMER_MSECS )
{
DHCPfineTimer = lwip_localtime ;
dhcp_fine_tmr() ;
if( ( lwipdev.dhcpstatus!=2 )&&( lwipdev.dhcpstatus!=0xFF ) )
lwip_dhcp_process_handle() ; //DHCP處理
}
//每60s執(zhí)行一次DHCP粗糙處理
if( lwip_localtime-DHCPcoarseTimer>=DHCP_COARSE_TIMER_MSECS )
{
DHCPcoarseTimer = lwip_localtime ;
dhcp_coarse_tmr() ;
}
#endif
}
//如果使能了DHCP
#if LWIP_DHCP
void lwip_dhcp_process_handle()
{
u32 ip=0, netmask=0, gw=0 ;
switch( lwipdev.dhcpstatus )
{
//開啟DHCP
case 0:
dhcp_start( &lwip_netif ) ;
lwipdev.dhcpstatus = 1 ; //等待通過DHCP獲取到的地址
break ;
//等待獲取到IP地址
case 1:
{
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)
//正確獲取到IP地址的時候
if( ip!=0 )
{
lwipdev.dhcpstatus = 2 ; //DHCP成功
//解析出通過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 ) ;
//解析通過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 ) ;
//解析出通過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 ) ;
}
//通過DHCP服務(wù)獲取IP地址失敗,且超過最大嘗試次數(shù)
else if( lwip_netif.dhcp->tries>LWIP_MAX_DHCP_TRIES )
{
lwipdev.dhcpstatus = 0xFF ; //DHCP超時失敗
//使用靜態(tài)IP地址
IP4_ADDR( &( lwip_netif.ip_addr ), lwipdev.ip[ 0 ], lwipdev.ip[ 1 ], lwipdev.ip[ 2 ], lwipdev.ip[ 3 ] ) ;
IP4_ADDR( &( lwip_netif.netmask ), lwipdev.netmask[ 0 ], lwipdev.netmask[ 1 ], lwipdev.netmask[ 2 ], lwipdev.netmask[ 3 ] ) ;
IP4_ADDR( &( lwip_netif.gw ), lwipdev.gateway[ 0 ], lwipdev.gateway[ 1 ], lwipdev.gateway[ 2 ], lwipdev.gateway[ 3 ] ) ;
}
}
break;
default :
break;
}
}
#endif
(2)app/comm.h文件代碼
#ifndef _COMM_H_
#define _COMM_H_
#include "dm9000.h"
#define LWIP_MAX_DHCP_TRIES 4 //DHCP服務(wù)器最大重試次數(shù)
//lwip控制結(jié)構(gòu)體
typedef struct
{
u8 mac[ 6 ] ; //MAC地址
u8 remoteip[ 4 ] ; //遠端主機IP地址
u8 ip[ 4 ] ; //本機IP地址
u8 netmask[ 4 ] ; //子網(wǎng)掩碼
u8 gateway[ 4 ] ; //默認(rèn)網(wǎng)關(guān)的IP地址
vu8 dhcpstatus ; //dhcp狀態(tài)
//0,未獲取DHCP地址
//1,進入DHCP獲取狀態(tài)
//2,成功獲取DHCP地址
//0XFF,獲取失敗
}__lwip_dev ;
extern __lwip_dev lwipdev ; //lwip控制結(jié)構(gòu)體
void lwip_pkt_handle( void ) ;
void lwip_periodic_handle( void ) ;
void lwip_comm_default_ip_set( __lwip_dev *lwipx ) ;
u8 lwip_comm_mem_malloc( void ) ;
void lwip_comm_mem_free( void ) ;
u8 lwip_comm_init( void ) ;
void lwip_dhcp_process_handle( void ) ;
#endif
注:函數(shù)中用到了未定義的數(shù)據(jù)類型,需要在sys.h中添加該類型的定義typedef volatile uint8_t vu8。
(3)app/lwipopts文件代碼
#ifndef _LWIPOPTS_H_
#define _LWIPOPTS_H_
#define SYS_LIGHTWEIGHT_PROT 0
//NO_SYS==1:不使用操作系統(tǒng)
#define NO_SYS 1 //不使用UCOS操作系統(tǒng)
#define MEM_ALIGNMENT 4 //使用4字節(jié)對齊模式
//heap內(nèi)存的大小,如果在應(yīng)用中有大量數(shù)據(jù)發(fā)送的話這個值最好設(shè)置大一點
#define MEM_SIZE 10*1024 //內(nèi)存堆大小
//memp結(jié)構(gòu)的pbuf數(shù)量,如果應(yīng)用從ROM或者靜態(tài)存儲區(qū)發(fā)送大量數(shù)據(jù)時,這個值應(yīng)該設(shè)置大一點
#define MEMP_NUM_PBUF 10
#define MEMP_NUM_UDP_PCB 6 //UDP協(xié)議控制塊(PCB)數(shù)量.每個活動UDP"連接"需要一個PCB
#define MEMP_NUM_TCP_PCB 10 //同時建立激活的TCP數(shù)量
#define MEMP_NUM_TCP_PCB_LISTEN 6 //能夠監(jiān)聽的TCP連接數(shù)量
#define MEMP_NUM_TCP_SEG 20 //最多同時在隊列中的TCP段數(shù)量
#define MEMP_NUM_SYS_TIMEOUT 5 //能夠同時激活的timeout個數(shù)
//Pbuf選項
//PBUF_POOL_SIZE:pbuf內(nèi)存池個數(shù)
#define PBUF_POOL_SIZE 10
//PBUF_POOL_BUFSIZE:每個pbuf內(nèi)存池大小
#define PBUF_POOL_BUFSIZE 1500
//TCP選項
#define LWIP_TCP 1 //為1是使用TCP
#define TCP_TTL 255 //生存時間
//當(dāng)TCP的數(shù)據(jù)段超出隊列時的控制位,當(dāng)設(shè)備的內(nèi)存過小的時候此項應(yīng)為0
#define TCP_QUEUE_OOSEQ 0
//最大TCP分段
#define TCP_MSS ( 1500-40 ) //TCP_MSS = (MTU - IP報頭大小 - TCP報頭大小
//TCP發(fā)送緩沖區(qū)大小(bytes)
#define TCP_SND_BUF ( 4*TCP_MSS )
//TCP_SND_QUEUELEN: TCP發(fā)送緩沖區(qū)大小(pbuf).這個值最小為(2 * TCP_SND_BUF/TCP_MSS)
#define TCP_SND_QUEUELEN ( 4* TCP_SND_BUF/TCP_MSS )
//TCP發(fā)送窗口
#define TCP_WND ( 2*TCP_MSS )
//ICMP選項
#define LWIP_ICMP 1 //使用ICMP協(xié)議
//DHCP選項
//當(dāng)使用DHCP時此位應(yīng)該為1,LwIP 0.5.1版本中沒有DHCP服務(wù)
#define LWIP_DHCP 1
//UDP選項
#define LWIP_UDP 1 //使用UDP服務(wù)
#define UDP_TTL 255 //UDP數(shù)據(jù)包生存時間
//靜態(tài)選項
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1
//SequentialAPI選項
//LWIP_NETCONN==1:使能NETCON函數(shù)(要求使用api_lib.c)
#define LWIP_NETCONN 0
//Socket API選項
//LWIP_SOCKET==1:使能Socket API(要求使用sockets.c)
#define LWIP_SOCKET 0
#define LWIP_COMPAT_MUTEX 1
#define LWIP_SO_RCVTIMEO 1 //通過定義可以避免阻塞線程
//Lwip調(diào)試選項
//#define LWIP_DEBUG 1 //開啟DEBUG選項
#define ICMP_DEBUG LWIP_DBG_OFF //開啟/關(guān)閉ICMPdebug
#endif
24.5.6 include/netif/ethernetif.h文件修改
#ifndef _ETHERNETIF_H_
#define _ETHERNETIF_H_
#include "lwip/err.h"
#include "lwip/netif.h"
//網(wǎng)卡的名字
#define IFNAME0 'e'
#define IFNAME1 'n'
err_t ethernetif_init( struct netif *netif ) ;
err_t ethernetif_input( struct netif *netif ) ;
#endif
24.5.7 netif/ethernetif.c文件修改
#include "netif/ethernetif.h"
#include "dm9000.h"
#include "comm.h"
#include "malloc.h"
#include "netif/etharp.h"
#include "string.h"
static err_t low_level_init( struct netif *netif )
{
netif->hwaddr_len = ETHARP_HWADDR_LEN ; //設(shè)置MAC地址長度,為6個字節(jié)
//初始化MAC地址,不能與網(wǎng)絡(luò)中其他設(shè)備MAC地址重復(fù)
netif->hwaddr[ 0 ] = lwipdev.mac[ 0 ] ;
netif->hwaddr[ 1 ] = lwipdev.mac[ 1 ] ;
netif->hwaddr[ 2 ] = lwipdev.mac[ 2 ] ;
netif->hwaddr[ 3 ] = lwipdev.mac[ 3 ] ;
netif->hwaddr[ 4 ] = lwipdev.mac[ 4 ] ;
netif->hwaddr[ 5 ] = lwipdev.mac[ 5 ] ;
netif->mtu = 1500 ; //最大允許傳輸單元,允許該網(wǎng)卡廣播和ARP功能
netif->flags = NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP ;
return ERR_OK ;
}
static err_t low_level_output( struct netif *netif, struct pbuf *p )
{
DM9000_SendPacket( p ) ;
return ERR_OK ;
}
static struct pbuf *low_level_input( struct netif *netif )
{
struct pbuf *p ;
p = DM9000_Receive_Packet() ;
return p ;
}
err_t ethernetif_input( struct netif *netif )
{
err_t err ;
struct pbuf *p ;
p = low_level_input( netif ) ; //調(diào)用low_level_input函數(shù)接收數(shù)據(jù)
if( p==NULL )
return ERR_MEM ;
err = netif->input( p, netif ); //調(diào)用netif結(jié)構(gòu)體中的input字段(一個函數(shù))來處理數(shù)據(jù)包
if( err!=ERR_OK )
{
LWIP_DEBUGF( NETIF_DEBUG, ( "ethernetif_input: IP input error\\n" ) ) ;
pbuf_free( p ) ;
p = NULL ;
}
return err ;
}
err_t ethernetif_init( struct netif *netif )
{
LWIP_ASSERT( "netif!=NULL", ( netif!=NULL ) ) ;
//LWIP_NETIF_HOSTNAME
#if LWIP_NETIF_HOSTNAME
netif->hostname = "lwip" ; //初始化名稱
#endif
netif->name[ 0 ] = IFNAME0 ; //初始化變量netif的name字段
netif->name[ 1 ] = IFNAME1 ; //在文件外定義這里不用關(guān)心具體值
netif->output = etharp_output ; //IP層發(fā)送數(shù)據(jù)包函數(shù)
netif->linkoutput = low_level_output ; //ARP模塊發(fā)送數(shù)據(jù)包函數(shù)
low_level_init( netif ) ; //底層硬件初始化函數(shù)
return ERR_OK ;
}
24.5.8 其他文件修改
(1)頭文件修改:主要是將lwip/sys.h修改為lwip/lwip_sys.h,因為我們在移植的時候?qū)wip/core/sys.c和lwip/include/lwip目錄下的一個文件名稱從sys改為了lwip_sys,所以導(dǎo)致程序引用的頭文件也需要修改,需要修改的頭文件有:timers.c,init.c,lwip_sys.c,mem.c,pbuf.c和memp.c。
(2)memp.c文件修改
①修改memp_memory的定義,之前的定義是位于170行的這么幾行代碼。
static u8_t memp_memory[MEM_ALIGNMENT - 1
#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
#include "lwip/memp_std.h"
];
我們將這段代碼屏蔽掉,重新添加定義u8_t *memp_memory;
②添加memp_get_memorysize函數(shù)
在333行插入以下函數(shù)代碼
u32_t memp_get_memorysize()
{
u32_t length=0;
length=(
MEM_ALIGNMENT-1 //全局型數(shù)組 為所有POOL分配的內(nèi)存空間
//MEMP_SIZE表示需要在每個POOL頭部預(yù)留的空間 MEMP_SIZE = 0
#define LWIP_MEMPOOL(name,num,size,desc)+((num)*(MEMP_SIZE+MEMP_ALIGN_SIZE(size)))
#include "lwip/memp_std.h"
);
return length;
}
24.6 主函數(shù)編寫
經(jīng)過上面的步驟,我們已經(jīng)成功移植了LWIP 1.4.1版本,現(xiàn)在我們通過編寫主函數(shù)來初始化LWIP,讓LWIP跑起來。
(1)添加定時器驅(qū)動,我們這里采用通用定時器3來完成LWIP的定時功能。
在tim.c文件中添加以下代碼
#include "tim.h"
extern u32 lwip_localtime; //lwip本地時間計數(shù)器,單位:ms
void TIM3_IRQHandler()
{
//溢出中斷
if( TIM3->SR&0x0001 )
lwip_localtime +=10 ; //加10
TIM3->SR &= ~( 1<<0 ) ; //清除中斷標(biāo)志位
}
void TIM3_Init( u16 arr, u16 psc )
{
RCC->APB1ENR |= 1<<1 ; //TIM3時鐘使能
TIM3->ARR = arr ; //設(shè)定計數(shù)器自動重裝值//剛好1ms
TIM3->PSC = psc ; //預(yù)分頻器7200,得到10Khz的計數(shù)時鐘
TIM3->DIER |= 1<<0 ; //允許更新中斷
TIM3->CR1 |= 0x01 ; //使能定時器3
NVIC_Init( 1, 3, TIM3_IRQn, 2 ) ; //組2
}
在tim.h文件中添加以下代碼
#ifndef _TIM_H_
#define _TIM_H_
#include "sys.h"
void TIM3_Init( u16 arr, u16 psc ) ; //定時器3初始化
#endif
(2)主函數(shù)添加以下代碼
#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "tim.h"
#include "lcd.h"
#include "malloc.h"
#include "dm9000.h"
#include "lwip/netif.h"
#include "comm.h"
#include "lwipopts.h"
int main()
{
u8 buf[ 30 ];
STM32_Clock_Init( 9 ) ; //系統(tǒng)時鐘設(shè)置
SysTick_Init( 72 ) ; //延時初始化
USART1_Init( 72, 115200 ) ; //串口初始化為115200
LCD_Init() ; //初始化LCD
TIM3_Init( 1000, 719 ) ; //定時器3頻率為100hz
my_mem_init( SRAMIN ) ; //初始化內(nèi)部內(nèi)存池
while( lwip_comm_init() ) ; //lwip初始化
//等待DHCP獲取成功/超時溢出
while( ( lwipdev.dhcpstatus!=2 )&&( lwipdev.dhcpstatus!=0xFF ) )
{
lwip_periodic_handle() ; //LWIP內(nèi)核需要定時處理的函數(shù)
lwip_pkt_handle() ;
}
POINT_COLOR=RED;
LCD_ShowString( 30, 110, "LWIP Init Successed" ) ;
//打印動態(tài)IP地址
if( lwipdev.dhcpstatus==2 )
sprintf( ( char* )buf, "DHCP IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
//打印靜態(tài)IP地址
else
sprintf( ( char* )buf, "Static IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
LCD_ShowString( 30, 130, buf ) ;
//得到網(wǎng)速
if( ( DM9000_Get_SpeedAndDuplex()&0x02 )==0x02 )
LCD_ShowString( 30, 150, "Ethernet Speed:10M" ) ;
else
LCD_ShowString( 30, 150, "Ethernet Speed:100M" ) ;
while( 1 )
{
lwip_periodic_handle() ;
lwip_pkt_handle() ;
delay_ms( 2 ) ;
}
}
-
存儲器
+關(guān)注
關(guān)注
38文章
7484瀏覽量
163761 -
DM9000
+關(guān)注
關(guān)注
0文章
24瀏覽量
16887 -
以太網(wǎng)控制器
+關(guān)注
關(guān)注
0文章
39瀏覽量
12717
發(fā)布評論請先 登錄
相關(guān)推薦
評論