本实验利用开发板自带的网口和 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:

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 地址处。

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进行了编程)

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~0X640000FEvu16 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.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外部中断,中断线6GPIO_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;            //外部中断线6NVIC_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;//使用模式AFSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2;    //NE2FSMC_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_NORSRAM2temp=*(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的唯一IDdm9000cfg.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();                         //复位DM9000delay_ms(100);temp=DM9000_Get_DeiviceID();           //获取DM9000IDprintf("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 RegistorDM9000_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 Registorreturn 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 bit7temp|=((DM9000_ReadReg(DM9000_NCR)>>3)&0X01); //获取DM9000 双工状态,NCR bit4return 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为1DM9000_WriteReg(DM9000_GPR,0);             //第二步:设置寄存器GPR(0X1F)的Bit1为0,DM9000内部的PHY上电DM9000_WriteReg(DM9000_NCR,(0x02|NCR_RST));   //第三步:软件复位DM9000do {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 SRAMif(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内存池分配pbufif(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);    //复位DM9000delay_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();}
}















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

打开从官网上下载下来的 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 提供的一些测试程序。

打开 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地址)

网络通信实验(DM9000,LWIP TCP/IP)相关推荐

  1. stm32 网络 服务器通信协议,利用stm32的lwip TCP/IP协议栈的通信的思路

    利用stm32f103vet6作为平台,enc28j60网卡,lwip tcp/ip作为协议栈进行相应的程序编写. Stm32作为服务器与stm32作为客户端程序编写的基本步骤,思路清理: 1.stm ...

  2. lwIP TCP/IP 协议栈笔记之十八: Socket接口编程

    目录 1. Socket 概述 2. LwIP 中的socket 3. Socket API 3.1 socket() 3.2 bind() 3.3 connect() 3.4 listen() 3. ...

  3. lwIP TCP/IP 协议栈笔记之十九: JPerf 工具测试网速

    目录 1. iPerf 与JPerf 2. 测试网络速度 2.1 获取JPerf 网络测速工具 2.2 测试开发板接收速度(NETCONN API) 2.3 测试开发板接收速度(Socket API) ...

  4. lwIP TCP/IP 协议栈笔记之十五: TCP协议

    目录 1. TCP 服务简介 2. TCP 的特性 2.1 连接机制 2.2 确认与重传 2.3 缓冲机制 2.4 全双工通信 2.5 流量控制 2.6 差错控制 2.7 拥塞控制 3. 端口号的概念 ...

  5. LwIP tcp/ip socket编程listen函数分析

    函数原型为: [cpp] view plain copy int listen(int  sockfd, int  backlog); <span style="font-family ...

  6. TCP/IP攻击实验(ARP,ICMP,SYN,RST,TCP会话劫持)

    一.实验背景 由于TCP/IP协议是Internet的基础协议,从开始设计的时候并没有考虑到现在网络上如此多的威胁,由此导致了许多形形色色的攻击方法,一般如果是针对协议原理的攻击,尤其DDOS,我们将 ...

  7. 几种开源的TCP/IP协议栈分析

    1.BSD TCP/IP协议栈 BSD栈历史上是其他商业栈的起点,大多数专业TCP/IP栈(VxWorks内嵌的TCP/IP栈)是BSD栈派生的.这是因为BSD栈在BSD许可协 议下提供了这些专业栈的 ...

  8. 几种开放源码的TCP/IP协议栈比较

    http://blog.chinaunix.net/uid-28785506-id-3828286.html 原文地址:几种开放源码的TCP/IP协议栈比较 作者:三点水兽 1.BSD TCP/IP协 ...

  9. TCP/IP协议栈Lwip的设计与实现:之一

    目录 摘要: 1.介绍 2.协议分层 3.综述 4.进程模型 5.操作系统仿真层 6.缓冲与存储管理 6.1包缓冲----pbufs 6.2内存管理 摘要: LWIP是TCP/IP协议栈的实现.LWI ...

  10. tcp ip协议_网络通信-TCP/IP协议族简述

    导读:计算机与网络设备要相互通信需要遵守同样的规则.例如,如何找到通信目标.该使用哪种语言通信.怎么结束通信等规则.不同的硬件.操作系统之间的通信都需要遵循同一种规则,这种规则也称为是协议.下面本文主 ...

最新文章

  1. Integer vs int
  2. Windows7 apache启动失败的解决方法
  3. EOS Dawn 1.0
  4. @Controller和@RestController的区别
  5. jasmine.spy对象的and.returnValue方法单步调试
  6. 浅析 Sunday 算法
  7. java mac jconsole_解决java maven项目找不到jconsole-1.8.0.jar和tools-1.8.0.jar包问题
  8. 软件研发作为一项工程而言,纳闷!
  9. java运算符重载_为什么Java不支持运算符重载?
  10. python横向输出字符串_Python字符串及用法详解
  11. 3D建模与处理软件简介 刘利刚 中国科技大学
  12. 字下挂星星的字体_星星掉了字体下载|星星掉了字体 最新版(TTF格式) 下载 - 巴士下载站...
  13. JLINK 驱动安装和配置ADS使用,ADX调试
  14. matlab if语句小于等于,matlab的if语句运算符与或怎么用
  15. 慧之声科技- 程序员的爱情故事
  16. Zedboard 运行linaro操作系统
  17. 汉语语法和英语语法的区别
  18. 老徐小程序官方教程地址
  19. 针式 PKM 个人知识管理软件 帮助
  20. 微软rd服务器,远程桌面rd服务器

热门文章

  1. 破解各类加密文件密码
  2. android led 字体下载,Android LED数字/电子表字体digital font
  3. DNA序列的机器学习方法
  4. 机器学习(2)——监督学习
  5. 在线图书销售系统顺序图_苏宁易购网上商城在线购物
  6. 添加打印机,错误为0x0000011b
  7. ioncube linux,lamp安装版,安装ionCube不成功
  8. python做数学计算器_python作为计算器 数学用法
  9. 重现Struts1的操纵classLoader漏洞
  10. mysql分区 seect_实战mysql分区(PARTITION)