网络通信实验(DM9000,LWIP TCP/IP)

本实验利用开发板自带的网口和 LWIP 实现:TCP 服务器、 TCP 客服端、UDP 以及 WEB 服务器等四个功能。

DM9000,TCP/IP和LWIP简介

本实验我们需要用到 DM9000 以太网芯片和 LWIP TCP/IP 协议栈。接下来分别介绍这两个部分。
DM9000以太网芯片
一个 10/100M PHY 和 4K 双字的 SRAM,它是出于低功耗和高性能目的设计的,其 IO 端口支持 3.3V 与 5V 电压。DM9000 为适应各种处理器提供了 8 位、16 位数据接口访问内部存储器。DM9000 理协议层接口完全支持使用 10Mbps 下 3 类、4 类、5 类非屏蔽双绞线和 100Mbps 下 5 类非屏蔽双绞线,这是完全遵照 IEEE 802.3u 标准。它的自动协商功能将自动完成 DM9000 配置以使其发挥出最佳性能,它还支持 IEEE 802.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有48引脚和100 引脚的,本实验采用48引脚的DM9000CEP:
网络通信实验(DM9000,LWIP TCP/IP)_第1张图片
1,中断引脚电平设置
通过设置DM9000 的 20(EECK)引脚来改变 INT (34引脚)的有效电平,当 EECK 拉高以后,INT 低电平有效,否则的话 INT 是高电平有效的。(本实验低电平有效)
2,数据位宽设置
当 EECS 上拉的时候 DM9000 选择 8 位数据位宽,否则的话选择 16 位数据位宽。(本实验数据位宽为16位)
3,DM9000直接内存访问控制(DMAC)
DM9000 支持 DMA 方式以简化对内部存储器的访问。在我们编程写好内部存储器地址后,就可以用一个读/写命令伪指令把当前数据加载到内部数据缓冲区,这样,内部存储器指定位置就可以被读/写命令寄存器访问。存储器地址将会自动增加,增加的大小与当前总线操作模式相同(8-bit 或 16-bit),接着下一个地址数据将会自动加载到内部数据缓冲区。
内部存储器空间大小为 16K 字节。前 3K 字节单元用作发送包的缓冲区,其他 13K 字节用作接收包的缓冲区。所以在写存储器操作时,如果地址越界(即超出 3K 空间),在 IMR 寄存器bit7 置位的情况下,地址指针将会返回到存储器 0 地址处。同样,在读存储器操作时,如果地址越界(即超出 16K 空间),在 IMR 寄存器 bit7 置位的情况下,地址指针将会返回到存储器0x0C00 地址处。
网络通信实验(DM9000,LWIP TCP/IP)_第2张图片

4,DM9000数据包发送
DM9000 有两个发送数据包:index1 和 index2,同时存储在 TX SRAM 中。发送控制寄存器TRC(02h)控制循环冗余校验码(CRC)和填充(pads)的插入,其状态分别记录在发送状态寄存I(03H)和发送状态寄存器 II(04H)中。
发送器的起始地址为 0x00H,在软件或硬件复位后,默认的数据发送包为 index1。首先,将数据写入 TX SRAM 中,然后,在发送数据包长度寄存器中把数据字节数写入字节计数寄存器。置位发送控制寄存器(02H)的 bit0 位,则 DM9000 开始发送 index1 数据包。在 index1 数据包发送结束之前,数据发送包index2被移入TX SRAM中。在index1数据包发送结束后,将index2数据字节数写入字节计数寄存器中,然后,置位发送控制寄存器(02H)的 bit0 位,则 index2 数据包开始发送。以此类推,后面的数据包都以此方式进行发送。(例程中只是对index1进行了编程)
网络通信实验(DM9000,LWIP TCP/IP)_第3张图片

5,DM9000数据包接收
RX SRAM 是一个环形数据结构。在软件或硬件复位后,RX SRAM 的起始地址为 0X0C00。
每个接收数据包都包含有 CRC 校验域,数据域,以及紧跟其后的 4 字节包头域。4 字节包头格式为:01h、状态、BYTE_COUNT 低、BYTE_COUNT 高。

驱动程序

dm9000.h

#ifndef _DM9000AEP_H
#define _DM9000AEP_H
#include "sys.h"
#include "lwip/pbuf.h"

#define DM9000_RST		PDout(7)		//DM9000复位引脚
#define DM9000_INT		PGin(6)			//DM9000中断引脚
//DM9000地址结构体
typedef struct
{
	vu16 REG;    //0X64000000~0X640000FE
	vu16 DATA;    //0X64000100~0X67FFFFFF
}DM9000_TypeDef;

//使用NOR/SRAM的BANK1.Sector2, 地址位HADDR[27,26]=01,FSMC_A7作为数据命令区分线
//注意设置16位数据时STM32内部会右移一位对齐			    
#define DM9000_BASE        ((u32)(0x64000000|0x000000FE))  //CMD为1:读数据
#define DM9000             ((DM9000_TypeDef *) DM9000_BASE)

#define DM9000_ID			0X90000A46	//DM9000 ID
#define DM9000_PKT_MAX		1536		//DM9000最大接收包长度

#define DM9000_PHY			0X40		//DM9000 PHY寄存器访问标志
//DM9000寄存器
#define DM9000_NCR			0X00
#define DM9000_NSR			0X01
#define DM9000_TCR			0X02
#define DM9000_TSRI			0X03
#define DM9000_TSRII		0X04
#define DM9000_RCR			0X05
#define DM9000_RSR			0X06
#define DM9000_ROCR			0X07	
#define DM9000_BPTR			0X08
#define DM9000_FCTR			0X09
#define DM9000_FCR			0X0A
#define DM9000_EPCR			0X0B
#define DM9000_EPAR			0X0C
#define DM9000_EPDRL		0X0D	
#define DM9000_EPDRH		0X0E
#define DM9000_WCR			0X0F
#define DM9000_PAR			0X10		//物理地址·0X10~0X15
#define DM9000_MAR			0X16		//多播地址·0X16~0X1D
#define DM9000_GPCR			0X1E
#define DM9000_GPR			0X1F
#define DM9000_TRPAL		0X22
#define DM9000_TRPAH		0X23
#define DM9000_RWPAL		0X24
#define DM9000_RWPAH		0X25

#define DM9000_VIDL			0X28
#define DM9000_VIDH			0X29
#define DM9000_PIDL			0X2A
#define DM9000_PIDH			0X2B

#define DM9000_CHIPR		0X2C
#define DM9000_TCR2			0X2D
#define DM9000_OCR			0X2E
#define DM9000_SMCR			0X2F
#define DM9000_ETXCSR		0X30
#define DM9000_TCSCR		0X31
#define DM9000_RCSCSR		0X32
#define DM9000_MRCMDX		0XF0
#define DM9000_MRCMDX1		0XF1
#define DM9000_MRCMD		0XF2
#define DM9000_MRRL			0XF4
#define DM9000_MRRH			0XF5
#define DM9000_MWCMDX		0XF6
#define DM9000_MWCMD		0XF8
#define DM9000_MWRL			0XFA
#define DM9000_MWRH			0XFB
#define DM9000_TXPLL		0XFC
#define DM9000_TXPLH		0XFD
#define DM9000_ISR			0XFE
#define DM9000_IMR			0XFF

#define NCR_RST             0X01
#define NSR_SPEED           0X80
#define NSR_LINKST         	0X40
#define NSR_WAKEST          0X20
#define NSR_TX2END          0X08
#define NSR_TX1END          0X04
#define NSR_RXOV            0X02

#define RCR_DIS_LONG        0X20
#define RCR_DIS_CRC         0X10
#define RCR_ALL             0X08
#define RCR_RXEN            0X01

#define IMR_PAR             0X80	
#define IMR_ROOI            0X08	
#define IMR_POI             0X04		//使能接收溢出中断
#define IMR_PTI             0X02		//使能发送中断
#define IMR_PRI             0X01		//使能接收中断

#define ISR_LNKCHGS         (1<<5)
#define ISR_ROOS            (1<<3)
#define ISR_ROS             (1<<2)
#define ISR_PTS             (1<<1)
#define ISR_PRS             (1<<0)
#define ISR_CLR_STATUS      (ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS)

//DM9000内部PHY寄存器
#define DM9000_PHY_BMCR		0X00
#define DM9000_PHY_BMSR		0X01
#define DM9000_PHY_PHYID1	0X02
#define DM9000_PHY_PHYID2	0X03
#define DM9000_PHY_ANAR		0X04
#define DM9000_PHY_ANLPAR	0X05
#define DM9000_PHY_ANER		0X06
#define DM9000_PHY_DSCR		0X10
#define DM9000_PHY_DSCSR	0X11
#define DM9000_PHY_10BTCSR	0X12
#define DM9000_PHY_PWDOR	0X13
#define DM9000_PHY_SCR		0X14

//DM9000工作模式定义
enum DM9000_PHY_mode
{
	DM9000_10MHD 	= 	0, 					//10M半双工
	DM9000_100MHD 	= 	1,					//100M半双工	
	DM9000_10MFD 	= 	4, 					//10M全双工
	DM9000_100MFD 	= 	5,					//100M全双工
	DM9000_AUTO  	= 	8, 					//自动协商
};

//DM9000配置结构体
struct dm9000_config
{
	enum DM9000_PHY_mode mode;				//工作模式
	u8  imr_all;							//中断类型
	u16 queue_packet_len;					//每个数据包大小
	u8  mac_addr[6];						//MAC地址
	u8  multicase_addr[8];					//组播地址
};
extern struct dm9000_config dm9000cfg;		//dm9000配置结构体


u8   DM9000_Init(void);
u16  DM9000_ReadReg(u16 reg);
void DM9000_WriteReg(u16 reg,u16 data);
u16  DM9000_PHY_ReadReg(u16 reg);
void DM9000_PHY_WriteReg(u16 reg,u16 data);
u32  DM9000_Get_DeiviceID(void);
u8   DM9000_Get_SpeedAndDuplex(void);	
void DM9000_Set_PHYMode(u8 mode);
void DM9000_Set_MACAddress(u8 *macaddr);
void DM9000_Set_Multicast(u8 *multicastaddr);
void DM9000_Reset(void);
void DM9000_SendPacket(struct pbuf *p);
struct pbuf *DM9000_Receive_Packet(void);
void DMA9000_ISRHandler(void);
#endif

注:
网络通信实验(DM9000,LWIP TCP/IP)_第4张图片
网络通信实验(DM9000,LWIP TCP/IP)_第5张图片
网络通信实验(DM9000,LWIP TCP/IP)_第6张图片
网络通信实验(DM9000,LWIP TCP/IP)_第7张图片
网络通信实验(DM9000,LWIP TCP/IP)_第8张图片
网络通信实验(DM9000,LWIP TCP/IP)_第9张图片
网络通信实验(DM9000,LWIP TCP/IP)_第10张图片

dm9000.c

#include "dm9000.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
#include "lwip_comm.h"

struct dm9000_config dm9000cfg;				//DM9000配置结构体

//初始化DM9000
//返回值:
//0,初始化成功
//1,DM9000A ID读取错误
u8 DM9000_Init(void)
{
	u32 temp;
	GPIO_InitTypeDef GPIO_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
	FSMC_NORSRAMTimingInitTypeDef ReadWriteTiming; 	//DM9000的读写时序
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|\
						   RCC_APB2Periph_GPIOF|RCC_APB2Periph_GPIOG,ENABLE);	//使能GPIOD E F G 时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);	//使能FSMC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);	//使能复用功能时钟

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; 		//PD7 推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
	GPIO_Init(GPIOD,&GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; 		//PG6 推挽输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	//上拉输入
	GPIO_Init(GPIOG,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5|\
								  GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_14|GPIO_Pin_15; //PD0 1 4 5 8 9 10 14 15复用
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_Init(GPIOD,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|\
								  GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; 			//PG7 8 9 10 11 12 13 14 15复用
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_Init(GPIOE,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;		//PF13复用
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_Init(GPIOF,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;		//PG9复用
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_Init(GPIOG,&GPIO_InitStructure);
	

	//PG6外部中断,中断线6
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOG,GPIO_PinSource6);
	
	EXTI_InitStructure.EXTI_Line = EXTI_Line6;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 
	//因为DM9000_INT是低电平有效,故此处设置下降沿触发
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	EXTI_ClearITPendingBit(EXTI_Line6); //清除中断线6挂起标志位
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;			//外部中断线6
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;			//子优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	ReadWriteTiming.FSMC_AddressSetupTime = 0;		//地址建立时间
	ReadWriteTiming.FSMC_AddressHoldTime = 0;
	ReadWriteTiming.FSMC_DataSetupTime = 3;		//数据建立时间
	ReadWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
	ReadWriteTiming.FSMC_CLKDivision = 0x00;
	ReadWriteTiming.FSMC_DataLatency = 0x00;
	ReadWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;//使用模式A
	
	FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2;	//NE2
	FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
	FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM;
	FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
	FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
	FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
	FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
	FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
	FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
	FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
	FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
	FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
	FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
	FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &ReadWriteTiming;
	FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &ReadWriteTiming;
	FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);
	FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM2,ENABLE); //使用FSMC的Bank1_Bank1_NORSRAM2

	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)&0XFF;
	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);
	temp=DM9000_Get_DeiviceID();			//获取DM9000ID
	printf("DM9000 ID:%#x\r\n",temp);
	if(temp!=DM9000_ID) return 1; 			//读ID错误
	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); 
	temp=DM9000_Get_SpeedAndDuplex();		//获取DM9000的连接速度和双工状态
	if(temp!=0XFF)							//连接成功,通过串口显示连接速度和双工状态
	{
		printf("DM9000 Speed:%dMbps,Duplex:%s duplex mode\r\n",(temp&0x02)?10:100,(temp&0x01)?"Full":"Half");
	}else printf("DM9000 Establish Link Failed!\r\n");
	DM9000_WriteReg(DM9000_IMR,dm9000cfg.imr_all);	//设置中断
	return 0;		
}

//读取DM9000指定寄存器的值
//reg:寄存器地址
//返回值:DM9000指定寄存器的值
u16 DM9000_ReadReg(u16 reg)
{
	DM9000->REG=reg;
	return DM9000->DATA; 
}

//向DM9000指定寄存器中写入值
//reg:要写入的寄存器
//data:要写入的值
void DM9000_WriteReg(u16 reg,u16 data)
{
	DM9000->REG=reg;
	DM9000->DATA=data;
}

//读取DM9000的PHY的指定寄存器
//reg:要读的PHY寄存器
//返回值:读取到的PHY寄存器的值
u16 DM9000_PHY_ReadReg(u16 reg)
{
	u16 temp;
	DM9000_WriteReg(DM9000_EPAR,DM9000_PHY|reg);   //EEPROM & PHY Address Registor
	DM9000_WriteReg(DM9000_EPCR,0X0C);				//选中PHY,发送读命令
	delay_ms(10);
	DM9000_WriteReg(DM9000_EPCR,0X00);				//清除读命令
	temp=(DM9000_ReadReg(DM9000_EPDRH)<<8)|(DM9000_ReadReg(DM9000_EPDRL));//EEPROM & PHY Data Registor
	return temp;
}

//向DM9000的PHY寄存器写入指定值
//reg:PHY寄存器
//data:要写入的值
void DM9000_PHY_WriteReg(u16 reg,u16 data)
{
	DM9000_WriteReg(DM9000_EPAR,DM9000_PHY|reg);
	DM9000_WriteReg(DM9000_EPDRL,(data&0xff));		//写入低字节
	DM9000_WriteReg(DM9000_EPDRH,((data>>8)&0xff));	//写入高字节
	DM9000_WriteReg(DM9000_EPCR,0X0A);				//选中PHY,发送写命令
	delay_ms(50);
	DM9000_WriteReg(DM9000_EPCR,0X00);				//清除写命令	
}

//获取DM9000的芯片ID
//返回值:DM9000的芯片ID值
u32 DM9000_Get_DeiviceID(void)
{
	u32 value;
	value =DM9000_ReadReg(DM9000_VIDL);
	value|=DM9000_ReadReg(DM9000_VIDH) << 8;
	value|=DM9000_ReadReg(DM9000_PIDL) << 16;
	value|=DM9000_ReadReg(DM9000_PIDH) << 24;
	return value;
}

//获取DM9000的连接速度和双工模式
//返回值:	0,100半双工
//			1,100M全双工
//			2,10M半双工
//			3,10M全双工
//			0XFF,连接失败
u8 DM9000_Get_SpeedAndDuplex(void)
{
	u8 temp;
	u8 i=0;	
	if(dm9000cfg.mode==DM9000_AUTO)				//如果开启了自动协商模式,一定要等待协商完成
	{
		while(!(DM9000_PHY_ReadReg(0X01)&0X0020))	//BMSR(0X01)Bit5表示自动协商是否完成
		{
			delay_ms(100);                         //超时判断					
			i++;
			if(i>100)return 0XFF;					//自动协商失败
		}	
	}else											//自定义模式,一定要等待连接成功
	{
		while(!(DM9000_ReadReg(DM9000_NSR)&0X40))//等待连接成功,Network Control Register
		{
			delay_ms(100);					//超时判断
			i++;
			if(i>100)return 0XFF;					//连接失败		
		}
	}
	temp =((DM9000_ReadReg(DM9000_NSR)>>6)&0X02);	//获取DM9000 连接速度,NSR bit7
	temp|=((DM9000_ReadReg(DM9000_NCR)>>3)&0X01);	//获取DM9000 双工状态,NCR bit4
	return temp;
}

//设置DM90000的PHY工作模式
//mode:PHY模式
void DM9000_Set_PHYMode(u8 mode)
{
	u16 BMCR_Value,ANAR_Value;	
	switch(mode)
	{
		case DM9000_10MHD:		//10M半双工
			BMCR_Value=0X0000;
			ANAR_Value=0X21;
			break;
		case DM9000_10MFD:		//10M全双工
			BMCR_Value=0X0100;
			ANAR_Value=0X41;
			break;
		case DM9000_100MHD:		//100M半双工
			BMCR_Value=0X2000;
			ANAR_Value=0X81;
			break;
		case DM9000_100MFD:		//100M全双工
			BMCR_Value=0X2100;
			ANAR_Value=0X101;
			break;
		case DM9000_AUTO:		//自动协商模式
			BMCR_Value=0X1000;
			ANAR_Value=0X01E1;
			break;		
	}
	DM9000_PHY_WriteReg(DM9000_PHY_BMCR,BMCR_Value);
	DM9000_PHY_WriteReg(DM9000_PHY_ANAR,ANAR_Value);
 	DM9000_WriteReg(DM9000_GPR,0X00);	//使能PHY
}

//设置DM9000的MAC地址
//macaddr:指向MAC地址
void DM9000_Set_MACAddress(u8 *macaddr)
{
	u8 i;
	for(i=0;i<6;i++)
	{
		DM9000_WriteReg(DM9000_PAR+i,macaddr[i]);
	}
}
//设置DM9000的组播地址
//multicastaddr:指向多播地址
void DM9000_Set_Multicast(u8 *multicastaddr)
{
	u8 i;
	for(i=0;i<8;i++)
	{
		DM9000_WriteReg(DM9000_MAR+i,multicastaddr[i]);
	}
}
//复位DM9000
void DM9000_Reset(void)
{
	DM9000_RST = 0;								//DM9000硬件复位
	delay_ms(10);
	DM9000_RST = 1; 							//DM9000硬件复位完成
	delay_ms(100);								//一定要有这个延时,让DM9000准备就绪
 	DM9000_WriteReg(DM9000_GPCR,0x01);			//第一步:设置寄存器GPCR(0X1E)的Bit0为1
	DM9000_WriteReg(DM9000_GPR,0);				//第二步:设置寄存器GPR(0X1F)的Bit1为0,DM9000内部的PHY上电
 	DM9000_WriteReg(DM9000_NCR,(0x02|NCR_RST));	//第三步:软件复位DM9000
	do 
	{
		delay_ms(25); 	
	}while(DM9000_ReadReg(DM9000_NCR)&1);		//等待DM9000软复位完成
	DM9000_WriteReg(DM9000_NCR,0);
	DM9000_WriteReg(DM9000_NCR,(0x02|NCR_RST));	//DM9000第二次软复位
	do 
	{
		delay_ms(25);	
	}while (DM9000_ReadReg(DM9000_NCR)&1);
} 

//通过DM9000发送数据包
//p:pbuf结构体指针,是LWIP所能接受的一种格式
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发送数据时,要关闭中断。结束后,要再次打开中断。
	DM9000->REG=DM9000_MWCMD;			//发送此命令后可以将要发送的u搬到DM9000 TX SRAM中	
	q=p;
	//向DM9000的TX SRAM中写入数据,一次写入两个字节数据
	//当要发送的数据长度为奇数时,我们需要将最后一个数据单独存到TX SRAM中
 	while(q)
	{
		if (pbuf_index < q->len)
		{
			word[word_index++] = ((u8_t*)q->payload)[pbuf_index++];
			if (word_index == 2)
			{
				DM9000->DATA=((u16)word[1]<<8)|word[0];
				word_index = 0;
			}
		}else
		{
			q=q->next;
			pbuf_index = 0;
		}
	}
	//还有一个字节未写入TX SRAM
	if(word_index==1) DM9000->DATA=word[0];
	//向DM9000写入字节长度
	DM9000_WriteReg(DM9000_TXPLL,p->tot_len&0XFF);
	DM9000_WriteReg(DM9000_TXPLH,(p->tot_len>>8)&0XFF);		//设置要发送数据的数据长度
	DM9000_WriteReg(DM9000_TCR,0X01);						//启动发送
	while((DM9000_ReadReg(DM9000_ISR)&0X02)==0);			//等待发送完成
	DM9000_WriteReg(DM9000_ISR,0X02);						//清除发送完成中断,Interrupt Status Register(FEH)
 	DM9000_WriteReg(DM9000_IMR,dm9000cfg.imr_all);			//DM9000网卡接收中断使能, Interrupt Mask Register(FFH)					
}

//DM9000接收数据包
//接收到的数据包存放在DM9000的RX FIFO中,地址为0X0C00~0X3FFF
//接收到的数据包的前四个字节并不是真实的数据,而是有特殊含义的
//byte1:表示是否接收到数据,为0x00或者0X01如果两个都不是的话,一定要复位DM9000
//		0x01:接收到数据
//		0x00:未接收到数据
//byte2:第二个字节表示一些状态信息,和DM9000的RSR(0X06)寄存器一致
//byte3:本帧数据长度的低字节
//byte4:本帧数据长度的高字节
//返回值:pbuf格式的接收到的数据包
struct pbuf *DM9000_Receive_Packet(void)
{
	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)										//接收到数据
	{
		if(rxbyte>1)								//rxbyte大于1,接收到的数据错误,挂了			
		{
            printf("dm9000 rx: rx error, stop device\r\n");
			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;  
		//if(rx_length>512)printf("rxlen:%d\r\n",rx_length);
        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										//内存申请失败
		{
			printf("pbuf内存申请失败:%d\r\n",rx_length);
            data=&dummy;
			len=rx_length;
			while(len)
			{
				*data=DM9000->DATA;
				len-=2;
			}
        }	
		//根据rx_status判断接收到的数据是否存在以下错误:FIFO溢出,CRC错误
		//对齐错误,物理层错误,如果有任何一个错误出现的话,丢弃该数据帧
		//当x_length小于64或者大于最大数据长度时也丢弃该数据帧
		if((rx_status&0XBF00) || (rx_length < 0X40) || (rx_length > DM9000_PKT_MAX))
		{
			printf("rx_status:%#x\r\n",rx_status);
			if (rx_status & 0x100)printf("rx fifo error\r\n");
            if (rx_status & 0x200)printf("rx crc error\r\n");
            if (rx_status & 0x8000)printf("rx length error\r\n");
            if (rx_length>DM9000_PKT_MAX)
			{
				printf("rx length too big\r\n");
				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; 
}
//中断处理函数,注意不是中断服函数,需要在中断服务函数中调用
void DMA9000_ISRHandler(void)
{
	u16 int_status;
	u16 last_io; 
	last_io = DM9000->REG;
	int_status=DM9000_ReadReg(DM9000_ISR); 
	DM9000_WriteReg(DM9000_ISR,int_status);				//清除中断标志位
	if(int_status & ISR_ROS)printf("overflow \r\n");
    if(int_status & ISR_ROOS)printf("overflow counter overflow \r\n");	
	if(int_status & ISR_PRS)		//接收中断
	{  
 		//接收完成中断,用户自己添加代码
	} 
	if(int_status&ISR_PTS)			//发送中断
	{ 
		//发送完成中断,用户自己添加代码
	}
	DM9000->REG=last_io;	
}

//外部中断线6的中断服务函数
void EXTI9_5_IRQHandler(void)
{
	EXTI_ClearITPendingBit(EXTI_Line6); //清除中断线6挂起标志位
	while(DM9000_INT == 0)
	{
		DMA9000_ISRHandler();
	}
}


网络通信实验(DM9000,LWIP TCP/IP)_第11张图片
在这里插入图片描述
网络通信实验(DM9000,LWIP TCP/IP)_第12张图片
网络通信实验(DM9000,LWIP TCP/IP)_第13张图片
在这里插入图片描述
网络通信实验(DM9000,LWIP TCP/IP)_第14张图片
网络通信实验(DM9000,LWIP TCP/IP)_第15张图片
网络通信实验(DM9000,LWIP TCP/IP)_第16张图片
网络通信实验(DM9000,LWIP TCP/IP)_第17张图片
在这里插入图片描述
网络通信实验(DM9000,LWIP TCP/IP)_第18张图片
网络通信实验(DM9000,LWIP TCP/IP)_第19张图片
在这里插入图片描述
在这里插入图片描述

TCP/IP协议
TCP/IP 中文名为传输控制协议/因特网互联协议,又名网络通讯协议。由网络层的 IP 协议和传输层的 TCP 协议组成。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。TCP 负责发现传输的问题,一有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地。而IP 是给因特网的每一台联网设备规定一个地址。
OSI 是传统的开放式系统互连参考模型,该模型将 TCP/IP 分为七层:物理层、数据链路层(网络接口层)、网络层(网络层)、传输层(传输层)、会话层、表示层和应用层(应用层)。在我们的 LWIP 实验中 DM9000 相当于 PHY+MAC 层,而 LWIP 提供的就是网络层、传输层的功能,应用层是需要用户自己根据自己想要的功能去实现的。
LWIP协议栈
LWIP 是轻量级 IP 协议,有无操作系统的支持都可以运行,LWIP 实现的重点是在保持 TCP协议主要功能的基础上减少对 RAM 的占用,它只需十几 KB 的 RAM 和 40K 左右的 ROM 就可
以运行,这使 LWIP 协议栈适合在低端的嵌入式系统中使用。本实验 LWIP 的版本是 1.4.1。
LWIP 的主要特性如下:

  ARP 协议,以太网地址解析协议;
  IP 协议,包括 IPv4 和 IPv6,支持 IP 分片与重装,支持多网络接口下数据转发;
  ICMP 协议,用于网络调试与维护;
  IGMP 协议,用于网络组管理,可以实现多播数据的接收;
  UDP 协议,用户数据报协议;
  TCP 协议,支持 TCP 拥塞控制,RTT 估计,快速恢复与重传等;
  提供三种用户编程接口方式:raw/callback API、sequential API、BSD-style socket API;
  DNS,域名解析;
  SNMP,简单网络管理协议;
  DHCP,动态主机配置协议;
  AUTOIP,IP 地址自动配置;
  PPP,点对点协议,支持 PPPoE

网络通信实验(DM9000,LWIP TCP/IP)_第20张图片

打开从官网上下载下来的 LWIP1.4.1 其中包括 doc,src 和 test 三个文件夹和 5 个其他文件。doc 文件夹下包含了几个与协议栈使用相关的文本文档,doc 文件夹里面有两个比较重要的文
档:rawapi.txt 和 sys_arch.txt。rawapi.txt 告诉读者怎么使用 raw/callback API 进行编程,sys_arch.txt 包含了移植说明,在移植的时候会用到。src 文件夹是我们的重点,里面包含了 LWIP 的源码。test 是 LWIP 提供的一些测试程序。
网络通信实验(DM9000,LWIP TCP/IP)_第21张图片

打开 src 源码文件夹src 文件夹由 4 个文件夹组成:api、core、include、netif 四个文件夹。api 文件夹里面是 LWIP的 sequential API(Netconn)和 socket API 两种接口函数的源码,要使用这两种 API 需要操作系统支持。core 文件夹是 LWIP 内核源码,include 文件夹里面是 LWIP 使用到的头文件,netif 文件夹里面是与网络底层接口有关的文件。

LWIP 的移植

移植准备
1,添加DM9000 .c;添加头文件路径:HARDWARE\DM9000
2,建立LWIP文件夹,添加lwip 1.4.1
建立三个分组,并且添加相应的文件:
LWIP_NETIF -->src–netif:etharp.c, ethernetif.c
LWIP_API -->src–api–all
LWIP_CORE -->src–core–all/ipv4-all
添加路径:
src\include
src\include\ipv4
src\include\netif
4,复制arch文件–>到LWIP文件夹
新建分组LWIP_ARCH ,添加sys_arch.c
添加头文件路径:/LWIP,/LWIP/arch
5,复制LWIP_APP文件–>到LWIP文件夹
新建分组LWIP_APP ,添加lwip_comm.c
添加头文件路径:/LWIP/lwip_app/lwip_comm
6,复制lwip-1.4.1–>src–>include–>netif ethernetif.h
7,需要提供时钟支持:
建立文件夹HARDWARE/TIMER;添加timer.c;添加头文件路径:timer.h。但是,带操作系统移植时,ucos操作系统就可以提供时钟。
8,FwLib库中添加stmf10x_exit.c
源码修改
1,SySTEM文件夹存在和lwip-1.4.1重名的文件:

lwip-1.4.1-->src-->core-->sys.c 改为lwip-sys.c
lwip-1.4.1-->src-->include-->sys.h 改为lwip-sys.h

lwip源码中的#include "sys.h"也作修改
2,ethernetif.c文件,原作者只是给了一个框架,需要开发者重点修改
3,错误处理:no space in execution region…

malloc.h: MEM1_MAX_SIAZE 40x1024; //最大管理内存,占了40K,此处改为30

LWIP动态内存管理技术,LWIP有一个内存堆ram_heap和内存池memp_memory,这两个是LWIP的内存来源。这两个分别在mem.c和memp.c中,我们将这两个分组改用原子的内存分配函数对齐进行分配。(如果默认LWIP的SRAM不够用,要attrbute_ant函数来定义到外部)
在mem.c文件中将ram_heap数组注销掉,定义为指向u8_t的指针
在memp.c文件中,将memp_memory数组屏蔽掉改为指针
在memp.c文件中添加memp_get_memorysize()函数来获取memp_memory数组的大小
4,修改icmp.c文件,使其支持硬件帧检测
5,LWIP的源码中有opt文件,这个文件是裁剪和配置LWIP的,不过最好不要在opt.h(在LWIP_CORE–>autoip.c中)里做修改。所以要建立一个lwipopts.h 文件。
软件设计
复制main.c文件
下载验证
1,通过网线连接开发板到路由器,如果没有路由器也可以直接连接到电脑RJ45接口,但是,不能使用DHCP功能,需要使用静态地址:(默认静态IP:192.168.1.30:;默认网关GateWay:192.168.1.1;子网掩码:255.255.255.0)
2,连接上电脑端的RJ45后,我们还需要设置一下电脑的网络设置,打开本地连接–>TCP/IPv4属性,使用IP地址,DNS服务器地址。(IP地址:192.168.1.x=2-254:;默认网关GateWay:192.168.1.1;子网掩码:255.255.255.0;首选DNS服务器:192.168.1.1)
3,在电脑上Ping开发板的IP地址。 >ping (IP地址)

你可能感兴趣的:(STM32,嵌入式,操作系统,网络)