女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

有線網絡通信實驗1之LWIP移植

汽車電子技術 ? 來源:滑小稽筆記 ? 作者:電子技術園地 ? 2023-03-01 14:30 ? 次閱讀

24.1 DM9000概述

DM9000是一款完全集成的、性價比高、引腳數少、帶有通用處理器接口的單芯片快速以太網控制器。一個10/100MPHY和4K雙字的SRAM,它是出于低功耗和高性能目的設計的,其IO端口支持3.3V與5V電壓。DM9000為適應各種處理器提供了8位、16位數據接口訪問內部存儲器。DM9000協議層接口完全支持使用10Mbps下3類、4類、5類非屏蔽雙絞線和100Mbps下5類非屏蔽雙絞線,這是完全遵照IEEE802.3u標準。它的自動協商功能將自動完成DM9000配置以使其發揮出最佳性能,它還支持IEEE802.3x全雙工流量控制,DM9000的特性如下:

(1)支持處理器接口:I/O口的字節或字命令對內部存儲器進行讀寫操作

(2)集成自適應(AUTO-MDIX)10/100M收發器

(3)半雙工模式流量控制的背壓模式

(4)IEEE802.3x全雙工模式的流量控制

(5)支持喚醒幀,鏈路狀態改變和遠程喚醒

(6)內置16K字節SRAM

(7)內置3.3V至2.5V的調節器

(8)支持IP/TCP/UDP的校驗和生成以及校驗支持MAC接口

(9)支持自動加載EEPROM里面生產商ID和產品ID

(10)可選EEPROM配置

(11)超低功耗模式

A.功率降低模式(電纜偵測)

B.掉電模式

C.可選擇1:1或1.25:1變壓比例降低額外功率

(12)兼容3.3V和5.0V輸入輸出電壓

DM9000的結構框圖如下圖所示。

圖片

1、DM9000中斷引腳電平設置

DM9000的INT引腳為中斷輸出引腳,默認情況下該引腳高電平有效。可以通過設置DM9000的EECK引腳來改變INT的有效電平,當EECK拉高以后,INT低電平有效,否則的話INT是高電平有效的。

2、DM9000數據位寬設置

DM9000支持8位和16位兩種數據位寬,可以通過DM9000的EECS引腳設置其數據位寬,當EECS上拉的時候DM9000選擇8位數據位寬,否則的話選擇16位數據位寬。一般設置默認位寬為16位。

3、DM9000直接內存訪問控制DMAC

DM9000支持DMA方式以簡化對內部存儲器的訪問。在我們編程寫好內部存儲器地址后,就可以用一個讀/寫命令偽指令把當前數據加載到內部數據緩沖區,這樣,內部存儲器指定位置就可以被讀/寫命令寄存器訪問。存儲器地址將會自動增加,增加的大小與當前總線操作模式相同(8-bit或16-bit),接著下一個地址數據將會自動加載到內部數據緩沖區。內部存儲器空間大小為16K字節。前3K字節單元用作發送包的緩沖區,其他13K字節用作接收包的緩沖區。所以在寫存儲器操作時,如果地址越界(即超出3K空間),在IMR寄存器bit7置位的情況下,地址指針將會返回到存儲器0地址處。同樣,在讀存儲器操作時,如果地址越界(即超出16K空間),在IMR寄存器bit7置位的情況下,地址指針將會返回到存儲器0C00H地址處。

4、DM9000數據包發送

DM9000有兩個發送數據包:index1和index2,同時存儲在TXSRAM中。發送控制寄存器控制循環冗余校驗碼和填充的插入,其狀態分別記錄在發送狀態寄存器1和發送狀態寄存器2中。發送器的起始地址為00H,在軟件或硬件復位后,默認的數據發送包為index1。首先,將數據寫入TXSRAM中,然后,在發送數據包長度寄存器中把數據字節數寫入字節計數寄存器。置位發送控制寄存器(02H)的bit0位,則DM9000開始發送index1數據包。在index1數據包發送結束之前,數據發送包index2被移入TXSRAM中。在index1數據包發送結束后,將index2數據字節數寫入字節計數寄存器中,然后,置位發送控制寄存器(02H)的bit0位,則index2數據包開始發送。以此類推,后面的數據包都以此方式進行發送。

5、DM9000數據包接收

RXSRAM是一個環形數據結構。在軟件或硬件復位后,RXSRAM的起始地址為C00H。每個接收數據包都包含有CRC校驗域,數據域,以及緊跟其后的4字節包頭域。4字節包頭格式為:01H、狀態、BYTE_COUNT低、BYTE_COUNT高。請注意:每個接收包的起始地址處在適當的地址邊界,這取決于當前總線操作模式(8bit或者16bit)。DM9000是通過16位數據總線,掛在STM32的FSMC上面,DM9000的片選由FSMC_NE2控制,CMD則由FSMC_A7控制。這個連接方法,類似于TFTLCD顯示,總共用到了22個IO口。

24.2 TCP/IP協議概述

TCP/IP中文名為傳輸控制協議/因特網互聯協議,又名網絡通訊協議,是Internet最基本的協議、Internet國際互聯網絡的基礎,由網絡層的IP協議和傳輸層的TCP協議組成。TCP/IP定義了電子設備如何連入因特網,以及數據如何在它們之間傳輸的標準。協議采用了4層的層級結構,每一層都呼叫它的下一層所提供的協議來完成自己的需求。通俗而言:TCP負責發現傳輸的問題,一有問題就發出信號,要求重新傳輸,直到所有數據安全正確地傳輸到目的地。而IP是給因特網的每一臺聯網設備規定一個地址。

TCP/IP協議不是TCP和IP這兩個協議的合稱,而是指因特網整個TCP/IP協議族。從協議分層模型方面來講,TCP/IP由四個層次組成:網絡接口層、網絡層、傳輸層、應用層。OSI是傳統的開放式系統互連參考模型,該模型將TCP/IP分為七層:物理層、數據鏈路層(網絡接口層)、網絡層(網絡層)、傳輸層(傳輸層)、會話層、表示層和應用層(應用層)。TCP/IP模型與OSI模型對比如下表所示。

層級 OSI模型 TCP/IP模型
1 應用層 應用層
2 表示層
3 會話層
4 傳輸層 傳輸層
5 網絡層 互聯層
6 數據鏈路層 鏈路層
7 物理層

我們剛才介紹的DM9000相當于鏈路層,而LWIP提供的就是網絡層的功能,應用層則需要用戶自己編寫代碼去實現。

24.3 LWIP概述

LWIP是瑞典計算機科學院(SICS)的AdamDunkels等開發的一個小型開源的TCP/IP協議棧。LWIP是輕量級IP協議,有無操作系統的支持都可以運行,LWIP實現的重點是在保持TCP協議主要功能的基礎上減少對RAM的占用,它只需十幾KB的RAM和40K左右的ROM就可以運行,這使LWIP協議棧適合在低端的嵌入式系統中使用。目前LWIP的最新版本是2.1.2。在這里采用比較常用的1.4.1版本進行介紹。

LWIP的主要特性如下:

(1)ARP協議:以太網地址解析協議

(2)IP協議:包括IPv4和IPv6,支持IP分片與重裝,支持多網絡接口下數據轉發

(3)ICMP協議:用于網絡調試與維護

(4)IGMP協議:用于網絡組管理,可以實現多播數據的接收

(5)UDP協議:用戶數據報協議

(6)TCP協議:支持TCP擁塞控制,RTT估計,快速恢復與重傳等

(7)提供三種用戶編程接口方式:raw/callbackAPI、sequentialAPI、BSD-stylesocketAPI

(8)DNS:域名解析

(9)SNMP:簡單網絡管理協議

(10)DHCP:動態主機配置協議

(11)AUTOIP:IP地址自動配置

(12)PPP:點對點協議,支持PPPoE

24.4 DM9000驅動編寫

24.4.1 DM9000寄存器介紹

(1) 網絡控制寄存器 :NCR

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
- WAKEEN - FCTOL FDX LBK RST

Bit 6:置位時將啟用喚醒功能。清除該位還將清除所有喚醒事件狀態,軟件復位后,該位將不受影響

0:開啟

1:關閉

Bit 4:強制沖突模式,用于檢測

Bit 3:內部PHY全雙工模式

Bit 2~Bit 1:回環模式

00:正常

01:MAC內部回環

10:內部PHY100M模式數字回環

11:保留

Bit 0:軟件復位,寫1啟動復位,10us后自動清零

(2) 網絡狀態寄存器 :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:網絡速度,在使用內部PHY的情況下,0表示100Mbps,1表示10Mbps,當LINKST=0時,該位無意義

Bit 6:連接狀態

0:連接失敗

1:連接成功

Bit 5:喚醒狀態,讀或者對該位寫1清0,復位后該位不受影響

0:產生喚醒事件

1:沒有喚醒事件

Bit 3:發送數據包2完成標志,讀或者對該位寫1清0

Bit 2:發送數據包1完成標志,讀或者對該位寫1清0

Bit 1:接收緩存溢出標志

(3) 發送控制寄存器 :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字節)

1:使能

Bit 5:嚴重沖突模式檢測

0:當沖突計數多于15則終止本次數據包

   1:始終嘗試發送本次數據包

Bit 4:禁止為數據包2添加填充

Bit 3:禁止為數據包2添加CRC校驗

Bit 2:禁止為數據包1添加填充

Bit 1:禁止為數據包1添加CRC校驗

Bit 0:發送請求,發送完成后自動清除該位

(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字節的數據包

Bit 4:丟棄CRC校驗錯誤的數據包

Bit 3:允許廣播

Bit 2:允許小于最小長度的數據包

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:接收緩存高位溢出門限

當接收SRAM空閑空間小于該門限值則發送一個暫停時間為FFFF H的暫停包,若該值為0,則沒有接收條件,若該值為1,則默認值為3K字節的空閑空間

Bit 3~Bit 0:接收緩存低位溢出門限

當接收SRAM空閑空間大于該門限值則發送一個暫停時間為0000 H的暫停包,當溢出門限最高值的暫停包發送之后,溢出門限值最低值的暫停包才有效,默認值為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:背壓閾值最高值

當接收SRAM空閑空間低于該閾值,則MAC將產生一個擁擠狀態,默認值3K字節空閑空間

Bit 3~Bit 0:擁擠狀態時間,默認為200us

JPT值 擁擠狀態時間(us) JPT值 擁擠狀態時間(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) 發送控制寄存器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:設置LED引腳為模式0或者根據EEPROM的設定

1:設置LED引腳為模式1

Bit 6:重試沖突延遲數據包

Bit 5:禁止重新發送“underruned”數據包

Bit 4:單包模式

0:發送完成前發送最多兩個數據包的命令能被執行

1:發送完成前發送一個數據包的命令能被執行

Bit 3~Bit 0:幀間隔設置

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) 中斷狀態寄存器 :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:數據寬度選擇

0:16位模式

1:8位模式

Bit 5:連接狀態改變

Bit 4:發送“underrun”

Bit 3:接收計數器溢出

Bit 2:接收溢出

Bit 1:數據包發送

Bit 0:數據包接收

(9) 中斷狀態寄存器 :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的大小時自動跳回起始位置,需要驅動程序設置該位,若設置該位,REG_F5將自動設置為0x H

Bit 5:使能連接狀態改變中斷

Bit 4:使能發送“underrun”中斷

Bit 3:使能接收計數器溢出中斷

Bit 2:使能接收溢出中斷

Bit 1:使能數據包發送中斷

Bit 0:使能數據包接收中斷

24.4.2 DM9000驅動

(1)初始化DM9000外設與寄存器配置

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 ) ;              //下降沿觸發
  NVIC_Init( 0, 0, EXTI9_5_IRQn, 2 ) ;        //優先級最高
  //寄存器清零
  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 ;        //存儲器數據寬度為16bit
  //操作BTR寄存器
  FSMC_Bank1->BTCR[ 3 ] |= 3<<8 ;        //數據保持時間(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區域2
  temp = *( vu32* )( 0x1FFFF7E8 ) ;        //獲取STM32的唯一ID的前24位作為MAC地址后三字節
  dm9000cfg.mode = DM9000_AUTO ;
   dm9000cfg.queue_packet_len = 0 ;
//DM9000的SRAM的發送和接收指針自動返回到開始地址,并開啟接收中斷
  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 ;  //低三字節用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() ;                //復位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 ) ;    //設置PHY工作模式
  DM9000_WriteReg( DM9000_NCR, 0x00 ) ;
  DM9000_WriteReg( DM9000_TCR, 0x00 ) ;      //發送控制寄存器清零
  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 ) ;  //清除發送狀態
  DM9000_WriteReg( DM9000_ISR, 0x0F ) ;      //清除中斷狀態
  DM9000_WriteReg( DM9000_TCR2, 0x80 ) ;    //切換LED到mode1
  //設置MAC地址和組播地址
  DM9000_Set_MACAddress( dm9000cfg.mac_addr ) ;//設置MAC地址
  DM9000_Set_Multicast( dm9000cfg.multicase_addr ) ; //設置組播地址
  DM9000_WriteReg( DM9000_RCR, RCR_DIS_LONG|RCR_DIS_CRC|RCR_RXEN ) ;
  DM9000_WriteReg( DM9000_IMR, IMR_PAR ) ; 
  DM9000_Get_SpeedAndDuplex() ;        //獲取DM9000的連接速度和雙工狀態
  DM9000_WriteReg( DM9000_IMR, dm9000cfg.imr_all ) ;  //設置中斷
  return 0 ;
}

注:函數中用到了未定義的數據類型,需要在sys.h中添加該類型的定義typedef volatile uint32_t vu32。

(2)DM9000內部寄存器讀寫函數

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 ) ;              //下降沿觸發
  NVIC_Init( 0, 0, EXTI9_5_IRQn, 2 ) ;        //優先級最高
  //寄存器清零
  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 ;        //存儲器數據寬度為16bit
  //操作BTR寄存器
  FSMC_Bank1->BTCR[ 3 ] |= 3<<8 ;        //數據保持時間(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區域2
  temp = *( vu32* )( 0x1FFFF7E8 ) ;        //獲取STM32的唯一ID的前24位作為MAC地址后三字節
  dm9000cfg.mode = DM9000_AUTO ;
   dm9000cfg.queue_packet_len = 0 ;
//DM9000的SRAM的發送和接收指針自動返回到開始地址,并開啟接收中斷
  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 ;  //低三字節用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() ;                //復位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 ) ;    //設置PHY工作模式
  DM9000_WriteReg( DM9000_NCR, 0x00 ) ;
  DM9000_WriteReg( DM9000_TCR, 0x00 ) ;      //發送控制寄存器清零
  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 ) ;  //清除發送狀態
  DM9000_WriteReg( DM9000_ISR, 0x0F ) ;      //清除中斷狀態
  DM9000_WriteReg( DM9000_TCR2, 0x80 ) ;    //切換LED到mode1
  //設置MAC地址和組播地址
  DM9000_Set_MACAddress( dm9000cfg.mac_addr ) ;//設置MAC地址
  DM9000_Set_Multicast( dm9000cfg.multicase_addr ) ; //設置組播地址
  DM9000_WriteReg( DM9000_RCR, RCR_DIS_LONG|RCR_DIS_CRC|RCR_RXEN ) ;
  DM9000_WriteReg( DM9000_IMR, IMR_PAR ) ; 
  DM9000_Get_SpeedAndDuplex() ;        //獲取DM9000的連接速度和雙工狀態
  DM9000_WriteReg( DM9000_IMR, dm9000cfg.imr_all ) ;  //設置中斷
  return 0 ;
}

(3)設置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)獲取與設置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)復位

void DM9000_Reset()
{
  DM9000_RST = 0 ;                        //DM9000硬件復位
  delay_ms( 10 ) ;
  DM9000_RST = 1 ;                        //DM9000硬件復位結束
  delay_ms( 100 ) ;                        //等待DM9000準備就緒
   DM9000_WriteReg( DM9000_GPCR, 0x01 ) ;            //第1步:設置GPCR寄存器的bit0為1
  DM9000_WriteReg( DM9000_GPR, 0 ) ;              //第2步:設置GPR寄存器的bit1為0 
   DM9000_WriteReg( DM9000_NCR, 0x02|NCR_RST ) ;        //第3步:軟件復位DM9000
  //等待DM9000軟復位完成
  do 
  {
    delay_ms( 25 ) ;
  }while( DM9000_ReadReg( DM9000_NCR )&0x01 ) ;
  DM9000_WriteReg( DM9000_NCR, 0 ) ;
  DM9000_WriteReg( DM9000_NCR, 0x02|NCR_RST ) ;        //DM9000第2次軟復位
  do
  {
    delay_ms( 25 ) ;
  }while( DM9000_ReadReg( DM9000_NCR )&0x01 ) ;
}

(6)發送

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 ) ;            //關閉網卡中斷
  DM9000->REG = DM9000_MWCMD ;              //發送的數據搬到DM9000 TX SRAM中
  q = p ;
  //向DM9000的TX SRAM中寫入數據,一次寫入兩個字節數據
  //當要發送的數據長度為奇數的時候,需要將最后一個字節單獨寫入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 ;                  //進行第二次讀取
  //接收到數據
  if( rxbyte )
  {
    //rxbyte大于1,接收到的數據錯誤,掛了
    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內存池分配pbuf
    //內存申請成功
    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 ;
        }
      }
    }
    //內存申請失敗
    else
    {
            data = &dummy ;
      len = rx_length ;
      while( len )
      {
        *data = DM9000->DATA ;
        len -= 2 ;
      }
        }
    //根據rx_status判斷接收數據是否出現錯誤,如果有任何一個出現的話丟棄該數據幀,
    //當rx_length小于64或者大于最大數據長度的時候也丟棄該數據幀
    if( ( rx_status&0xBF00 )||( rx_length<64 )||( rx_length>DM9000_PKT_MAX ) )
    {
      if( rx_length>DM9000_PKT_MAX )
      {
        DM9000_WriteReg( DM9000_NCR, NCR_RST ) ;      //復位DM9000
        delay_ms( 5 ) ;
      }
      //釋放內存
      if( p!=NULL )
      pbuf_free( ( struct pbuf* )p ) ;
      p = NULL ;
      goto __error_retry ;
    }
  }
  else
  {
    DM9000_WriteReg( DM9000_ISR, ISR_PTS ) ;            //清除所有中斷標志位
    dm9000cfg.imr_all = IMR_PAR|IMR_PRI ;              //重新接收中斷
    DM9000_WriteReg( DM9000_IMR, dm9000cfg.imr_all ) ;
  }
  return ( struct pbuf* )p ;
}

24.5 LWIP協議移植

24.5.1 源碼下載

可以從官網進行源碼下載,LWIP官網:http://savannah.nongnu.org/projects/lwip/

24.5.2 源碼結構

圖片

打開從官網上下載下來的源碼其中包括doc,src和test三個文件夾和5個其他文件。doc文件夾下包含了幾個與協議棧使用相關的文本文檔,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兩種接口函數的源碼,要使用這兩種API需要操作系統支持。core文件夾是LWIP內核源碼,include文件夾里面是LWIP使用到的頭文件,netif文件夾里面是與網絡底層接口有關的文件。

24.5.3 添加LWIP源代碼文件

(1)將lwip-2.1.2\\src目錄下的5個文件夾復制到工程文件夾中新建的LWIP文件夾中,刪除apps文件夾核include/lwip/apps文件夾。

(2)在LWIP文件夾中新建app和arch兩個文件夾。

(3)在app文件夾中新建comm,tcp_client,tcp_server和udp_demo文件夾,最終文件結構如下圖所示。

圖片

(4)在app/comm目錄下創建comm.c,comm.h,lwipopts.h三個文件。

(5)在arch目錄下創建cc.h,cpu.h,perf.h,sys_arch.c和sys_arch.h五個文件。

(6)在include/netif目錄下創建ethernetif.h文件。

(7)將之前的DM9000驅動,LCD驅動和內存管理驅動程序導入工程中。

(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重名,產生編譯錯誤。

24.5.4 arch目錄下源碼文件的修改

(1)arch/cc.h文件代碼(該文件主要完成協議使用的數據類型的定義)

#ifndef _CC_H_
#define _CC_H_
#include "cpu.h"
#include "stdio.h"
//定義與平臺無關的數據類型
typedef unsigned   char    u8_t;                    //無符號8位整數
typedef signed     char    s8_t;                    //有符號8位整數
typedef unsigned   short   u16_t;                    //無符號16位整數
typedef signed     short   s16_t;                    //有符號16位整數
typedef unsigned   long    u32_t;                    //無符號32位整數
typedef signed     long    s32_t;                    //有符號32位整數
typedef u32_t mem_ptr_t ;                        //內存地址型數據
typedef int sys_prot_t ;                          //臨界保護型數據
#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
//根據不同的編譯器定義一些符號
#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調試時使用的一些數據類型
#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文件代碼(負責定義CPU的大端小端模式)

#ifndef _CPU_H_
#define _CPU_H_
#define BYTE_ORDER LITTLE_ENDIAN                //小端模式
#endif

(3)arch/perf.h文件代碼(用于系統測量與統計)

#ifndef _PERF_H_
#define _PERF_H_
#define PERF_START                        //空定義
#define PERF_STOP(x)                        //空定義
#endif

(4)arch/sys_arch.h文件代碼(為了與操作系統共存使用的獲取時間的函數,用于為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本地時間計數器,單位: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控制結構體 
struct netif lwip_netif ;                          //定義一個全局的網絡接口
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本地時間計數器,單位:ms
#if LWIP_DHCP
u32 DHCPfineTimer=0 ;                        //DHCP精細處理計時器
u32 DHCPcoarseTimer=0 ;                        //DHCP粗糙處理計時器
#endif
u8 lwip_comm_mem_malloc()
{
  u32 mempsize ;
  u32 ramheapsize ;
  mempsize = memp_get_memorysize() ;                //得到memp_memory數組大小
  memp_memory = mymalloc( SRAMIN, mempsize ) ;          //為memp_memory申請內存
//得到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申請內存
  //有申請失敗的
  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 )
{
  //默認遠端IP為:192.168.1.100
  lwipx->remoteip[ 0 ] = 192 ;  
  lwipx->remoteip[ 1 ] = 168 ;
  lwipx->remoteip[ 2 ] = 1 ;
  lwipx->remoteip[ 3 ] = 104 ;
  //MAC地址設置(高三字節固定為:2.0.0,低三字節用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 ] ; 
  //默認本地IP為:192.168.1.30
  lwipx->ip[ 0 ] = 192 ;  
  lwipx->ip[ 1 ] = 168 ;
  lwipx->ip[ 2 ] = 1 ;
  lwipx->ip[ 3 ] = 30 ;
  //默認子網掩碼:255.255.255.0
  lwipx->netmask[ 0 ] = 255 ;  
  lwipx->netmask[ 1 ] = 255 ;
  lwipx->netmask[ 2 ] = 255 ;
  lwipx->netmask[ 3 ] = 0 ;
  //默認網關: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 ;      //調用netif_add()函數時的返回值,用于判斷網絡初始化是否成功
  struct ip_addr ipaddr ;        //ip地址
  struct ip_addr netmask ;      //子網掩碼
  struct ip_addr gw ;          //默認網關
  //內存申請失敗
  if( lwip_comm_mem_malloc() )
    return 1 ;
  //初始化DM9000AEP
  if( DM9000_Init() )
    return 2 ;
  lwip_init() ;            //初始化LWIP內核
  lwip_comm_default_ip_set( &lwipdev ) ; //設置默認IP等信息
//使用動態IP
#if LWIP_DHCP
  ipaddr.addr = 0 ;
  netmask.addr = 0 ;
  gw.addr = 0 ;
//使用靜態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
  //向網卡列表中添加一個網口
  Netif_Init_Flag = netif_add( &lwip_netif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input ) ; 
//如果使用DHCP的話
#if LWIP_DHCP
  lwipdev.dhcpstatus = 0 ;        //DHCP標記為0
  dhcp_start( &lwip_netif ) ;        //開啟DHCP服務
#endif
  //網卡添加失敗
  if( Netif_Init_Flag==NULL )
    return 3 ;
  //網口添加成功后,設置netif為默認值,并且打開netif網口
  else
  {
    netif_set_default( &lwip_netif ) ;    //設置netif為默認網口
    netif_set_up( &lwip_netif ) ;      //打開netif網口
  }
  return 0 ;                //操作OK
}
void lwip_pkt_handle()
{
  ethernetif_input( &lwip_netif ) ;      //從網絡緩沖區中讀取接收到的數據包并將其發送給LWIP處理
}
void lwip_periodic_handle()
{
#if LWIP_TCP
  //每250ms調用一次tcp_tmr()函數
  if( lwip_localtime-TCPTimer>=TCP_TMR_INTERVAL )
  {
    TCPTimer =  lwip_localtime ;
    tcp_tmr() ;
  }
#endif
  //ARP每5s周期性調用一次
  if( ( lwip_localtime-ARPTimer )>=ARP_TMR_INTERVAL )
  {
    ARPTimer = lwip_localtime ;
    etharp_tmr() ;
  }
//如果使用DHCP的話
#if LWIP_DHCP
  //每500ms調用一次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執行一次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 ;        //讀取子網掩碼
      gw = lwip_netif.gw.addr ;            //讀取默認網關 
      //正確獲取到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獲取到的子網掩碼地址
        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獲取到的默認網關
        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服務獲取IP地址失敗,且超過最大嘗試次數
      else if( lwip_netif.dhcp->tries>LWIP_MAX_DHCP_TRIES )
      {
        lwipdev.dhcpstatus = 0xFF ;          //DHCP超時失敗
        //使用靜態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服務器最大重試次數
//lwip控制結構體
typedef struct  
{
  u8 mac[ 6 ] ;                    //MAC地址
  u8 remoteip[ 4 ] ;                  //遠端主機IP地址 
  u8 ip[ 4 ] ;                    //本機IP地址
  u8 netmask[ 4 ] ;                  //子網掩碼
  u8 gateway[ 4 ] ;                  //默認網關的IP地址
  vu8 dhcpstatus ;                  //dhcp狀態
                          //0,未獲取DHCP地址
                          //1,進入DHCP獲取狀態
                          //2,成功獲取DHCP地址
                          //0XFF,獲取失敗
}__lwip_dev ;
extern __lwip_dev lwipdev ;                //lwip控制結構體
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

注:函數中用到了未定義的數據類型,需要在sys.h中添加該類型的定義typedef volatile uint8_t vu8。

(3)app/lwipopts文件代碼

#ifndef _LWIPOPTS_H_
#define _LWIPOPTS_H_
#define SYS_LIGHTWEIGHT_PROT  0
//NO_SYS==1:不使用操作系統
#define NO_SYS          1      //不使用UCOS操作系統
#define MEM_ALIGNMENT      4      //使用4字節對齊模式
//heap內存的大小,如果在應用中有大量數據發送的話這個值最好設置大一點
#define MEM_SIZE        10*1024    //內存堆大小
//memp結構的pbuf數量,如果應用從ROM或者靜態存儲區發送大量數據時,這個值應該設置大一點
#define MEMP_NUM_PBUF      10
#define MEMP_NUM_UDP_PCB    6      //UDP協議控制塊(PCB)數量.每個活動UDP"連接"需要一個PCB
#define MEMP_NUM_TCP_PCB    10      //同時建立激活的TCP數量
#define MEMP_NUM_TCP_PCB_LISTEN  6    //能夠監聽的TCP連接數量
#define MEMP_NUM_TCP_SEG    20      //最多同時在隊列中的TCP段數量
#define MEMP_NUM_SYS_TIMEOUT  5      //能夠同時激活的timeout個數
//Pbuf選項
//PBUF_POOL_SIZE:pbuf內存池個數
#define PBUF_POOL_SIZE    10
//PBUF_POOL_BUFSIZE:每個pbuf內存池大小
#define PBUF_POOL_BUFSIZE  1500
//TCP選項
#define LWIP_TCP      1          //為1是使用TCP
#define TCP_TTL        255        //生存時間
//當TCP的數據段超出隊列時的控制位,當設備的內存過小的時候此項應為0
#define TCP_QUEUE_OOSEQ    0
//最大TCP分段
#define TCP_MSS        ( 1500-40 )    //TCP_MSS = (MTU - IP報頭大小 - TCP報頭大小
//TCP發送緩沖區大小(bytes)
#define TCP_SND_BUF      ( 4*TCP_MSS )
//TCP_SND_QUEUELEN: TCP發送緩沖區大小(pbuf).這個值最小為(2 * TCP_SND_BUF/TCP_MSS) 
#define TCP_SND_QUEUELEN  ( 4* TCP_SND_BUF/TCP_MSS )
//TCP發送窗口
#define TCP_WND        ( 2*TCP_MSS )
//ICMP選項
#define LWIP_ICMP      1        //使用ICMP協議
//DHCP選項
//當使用DHCP時此位應該為1,LwIP 0.5.1版本中沒有DHCP服務
#define LWIP_DHCP      1
//UDP選項
#define LWIP_UDP      1        //使用UDP服務
#define UDP_TTL        255        //UDP數據包生存時間
//靜態選項
#define LWIP_STATS      0
#define LWIP_PROVIDE_ERRNO  1
//SequentialAPI選項
//LWIP_NETCONN==1:使能NETCON函數(要求使用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調試選項
//#define LWIP_DEBUG  1            //開啟DEBUG選項
#define ICMP_DEBUG    LWIP_DBG_OFF    //開啟/關閉ICMPdebug
#endif

24.5.6 include/netif/ethernetif.h文件修改

#ifndef _ETHERNETIF_H_
#define _ETHERNETIF_H_
#include "lwip/err.h"
#include "lwip/netif.h"
//網卡的名字
#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 ;        //設置MAC地址長度,為6個字節
  //初始化MAC地址,不能與網絡中其他設備MAC地址重復
  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 ;                  //最大允許傳輸單元,允許該網卡廣播和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 ) ;                    //調用low_level_input函數接收數據
  if( p==NULL )
    return ERR_MEM ;
  err = netif->input( p, netif );                                    //調用netif結構體中的input字段(一個函數)來處理數據包
  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 ;                  //在文件外定義這里不用關心具體值
  netif->output = etharp_output ;                  //IP層發送數據包函數
  netif->linkoutput = low_level_output ;              //ARP模塊發送數據包函數
  low_level_init( netif ) ;                      //底層硬件初始化函數
  return ERR_OK ;
}

24.5.8 其他文件修改

(1)頭文件修改:主要是將lwip/sys.h修改為lwip/lwip_sys.h,因為我們在移植的時候將lwip/core/sys.c和lwip/include/lwip目錄下的一個文件名稱從sys改為了lwip_sys,所以導致程序引用的頭文件也需要修改,需要修改的頭文件有: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函數

在333行插入以下函數代碼

u32_t memp_get_memorysize()

{

u32_t length=0;

   length=(

                 MEM_ALIGNMENT-1 //全局型數組 為所有POOL分配的內存空間

                 //MEMP_SIZE表示需要在每個POOL頭部預留的空間  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 主函數編寫

經過上面的步驟,我們已經成功移植了LWIP 1.4.1版本,現在我們通過編寫主函數來初始化LWIP,讓LWIP跑起來。

(1)添加定時器驅動,我們這里采用通用定時器3來完成LWIP的定時功能。

在tim.c文件中添加以下代碼

#include "tim.h"
extern u32 lwip_localtime;                    //lwip本地時間計數器,單位:ms
void TIM3_IRQHandler()
{
  //溢出中斷
  if( TIM3->SR&0x0001 )
    lwip_localtime +=10 ;                  //加10
  TIM3->SR &= ~( 1<<0 ) ;                  //清除中斷標志位
}
void TIM3_Init( u16 arr, u16 psc )
{
  RCC->APB1ENR |= 1<<1 ;                  //TIM3時鐘使能
   TIM3->ARR = arr ;                    //設定計數器自動重裝值//剛好1ms
  TIM3->PSC = psc ;                    //預分頻器7200,得到10Khz的計數時鐘
  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)主函數添加以下代碼

#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 ) ;                                        //系統時鐘設置
  SysTick_Init( 72 ) ;                          //延時初始化
  USART1_Init( 72, 115200 ) ;                      //串口初始化為115200
  LCD_Init() ;                            //初始化LCD
  TIM3_Init( 1000, 719 ) ;                        //定時器3頻率為100hz
  my_mem_init( SRAMIN ) ;                      //初始化內部內存池
  while( lwip_comm_init() ) ;                      //lwip初始化
  //等待DHCP獲取成功/超時溢出
  while( ( lwipdev.dhcpstatus!=2 )&&( lwipdev.dhcpstatus!=0xFF ) )
  {
    lwip_periodic_handle() ;                    //LWIP內核需要定時處理的函數
    lwip_pkt_handle() ;
  }
  POINT_COLOR=RED;
  LCD_ShowString( 30, 110, "LWIP Init Successed" ) ;
  //打印動態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] ) ;
  //打印靜態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 ) ; 
  //得到網速
  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 ) ;
  }
}
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 存儲器
    +關注

    關注

    38

    文章

    7624

    瀏覽量

    166223
  • DM9000
    +關注

    關注

    0

    文章

    24

    瀏覽量

    17065
  • 以太網控制器

    關注

    0

    文章

    39

    瀏覽量

    12923
收藏 人收藏

    評論

    相關推薦
    熱點推薦

    求labview和Linux網絡通信實

    求labview和Linux網絡通信實
    發表于 10-13 12:48

    線網絡通信新手求助!

    各位大神,我是剛步入無線網絡通信行業的新手,想問問大家,研究無線網絡通信方向應該學哪些東西?大家有沒有推薦的書什么的,希望得到大神們的指點,謝謝!!!
    發表于 12-14 18:07

    stm32+lwip網絡通信

    stm32+lwip網絡通信 在向上位機發送送數據時,發送的數據必須是常量,但是我要把AD轉換的數據發送給上位機應該怎么辦?求各路大神指點一二,急需,謝謝!!!
    發表于 10-28 21:26

    為什么網絡通信實驗用TCP客戶端模式時連接不上?

    戰艦V3上的實驗50 網絡通信實驗,當用TCP服務器模式時,通信正常,用TCP客戶端模式時連接不上,為何?是否有更新的網絡通信實驗代碼?請提供。
    發表于 08-16 04:35

    如何才能讓網絡通信實驗支持10個UDP鏈接?

    大家好,我在使用探索者STM32F4開發板,寄存器例程,實驗55 網絡通信實驗。我修改了例程,想創建10個UDP端口監聽,udppcb=udp_new()這里最多只能創建5個,5個之后都是返回失敗。請問應該修改那個參數,可以創建10個UDP監聽端口。
    發表于 08-27 22:48

    為什么網絡通信實驗網絡助手上接受到的先是456個字節?

    網絡通信實驗里將ARMF407配置成sever,通過電腦上網絡助手接受ARM發送到數據,將tcp_sever_sendbuf[1000]改成一個1000的大數組并賦值,但是在網絡助手上接受到的先是456個字節然后再是544個字
    發表于 09-02 02:54

    請問誰有F407網絡通信實驗的視頻嗎?

    請問有沒有F407網絡通信實驗的視頻啊,能方便給個鏈接或者地址嗎,謝謝了,急用嘞
    發表于 09-23 01:49

    如何把stm32f407網絡通信實驗調通?

    最近在做網絡通信實驗,沒有調通,有沒有大神指點一下
    發表于 10-30 04:35

    請問探索者網絡通信實驗的控制網頁該怎么寫呢?

    原子哥雖然有比較詳細的講解了網絡通信實驗,但是對于那個控制網頁怎么寫的卻沒有過多的涉及,請問有誰會寫這種控制網頁的,誠心求教!
    發表于 11-06 04:35

    stm32f767網絡通信實驗移植iar少了幾個頭文件

    原子哥,我用iar移植767網絡通信實驗,參考代碼是標準例程~hal庫版本,提示少了幾個頭文件,像ethernetif等我手動添加進去了,但是lwip_check.h中調用的config.h頭文件
    發表于 03-24 23:54

    阿波羅STM32F767網絡通信實驗直接用網線連接電腦通信不成功

    STM32F767網絡通信實驗,直接用網線連接電腦,所有測試通信失敗,但是用路由器所有通信正常。已關閉防火墻
    發表于 04-07 04:36

    基于原子STM32F4的攝像頭與網絡通信實驗

    應單片機課設要求,做了一個攝像頭拍照網絡通信C/S實時LCD顯示。該工程基于原子STM32F4的攝像頭與網絡通信實驗,在此基礎上,將其整合。1.預期功能:攝像頭拍取的內容實時傳輸至LCD進行顯示通過
    發表于 08-03 06:04

    通過演示實驗深化對網絡通信過程的理解

    通過演示實驗深化對網絡通信過程的理解,網絡通信的技術資料,很好很實用。
    發表于 03-28 10:39 ?17次下載

    從入門到精通-西門子工業網絡通信實

    從入門到精通-西門子工業網絡通信實戰教材免費下載。
    發表于 04-21 14:52 ?44次下載

    智多晶LWIP網絡通信系統介紹

    在物聯網蓬勃興起的當下,嵌入式設備的網絡通信能力如同為其插上了騰飛的翅膀,使其能夠自由穿梭于信息的浩瀚海洋。而 LWIP,宛如一位身姿矯健的輕騎兵,在資源有限的嵌入式系統中飛馳,輕松完成各種復雜的網絡通信任務。西安智多晶微電子有
    的頭像 發表于 04-10 16:27 ?476次閱讀
    智多晶<b class='flag-5'>LWIP</b><b class='flag-5'>網絡通信</b>系統介紹