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接口
(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文章
7487浏览量
163800 -
DM9000
+关注
关注
0文章
24浏览量
16890 -
以太网控制器
+关注
关注
0文章
39浏览量
12724
发布评论请先 登录
相关推荐
评论