1简介
2硬件连接
3软件编程
3.1 初始化
3.1.1 SPI初始化
这里注意配置 设置为全双工模式、主机SPI、8为数据模式、工作模式0 片选由外部引脚管理,设置预分频值为2 、高位先行、CRC多项式为7 使能SPI
void W5500_SPI1_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1 | RCC_APB2Periph_AFIO, ENABLE);
/* 初始化SCK、MISO、MOSI引脚 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
/* 初始化CS引脚 */
GPIO_InitStructure.GPIO_Pin = W5500_SCS;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(W5500_SCS_PORT, &GPIO_InitStructure);
GPIO_SetBits(W5500_SCS_PORT, W5500_SCS);
/* 初始化配置STM32 SPI1 */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置为主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //SPI发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟悬空低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第1个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS由外部管脚管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //波特率预分频值为2
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC多项式为7
SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPI1寄存器
SPI_Cmd(SPI1, ENABLE); //STM32使能SPI1
}
3.1.2 SPI初始化GPIO设置初始化
这里指的是RST INT引脚的初始化
RST配置为推完输出
INT设置为上拉输入
开启外部中断0,模式为中断模式下降沿触发 中断线使能,
配置中断优先级为0,0最高了
void W5500_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA , ENABLE);
/* W5500_RST引脚初始化配置(PA0) */
GPIO_InitStructure.GPIO_Pin = W5500_RST;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(W5500_RST_PORT, &GPIO_InitStructure);
GPIO_ResetBits(W5500_RST_PORT, W5500_RST);
/* W5500_INT引脚初始化配置(PB1) */
GPIO_InitStructure.GPIO_Pin = W5500_INT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(W5500_INT_PORT, &GPIO_InitStructure);
/* Connect EXTI Line1 to PB0 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
/* PB1 as W5500 interrupt input */
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
3.2 装载网络参数
将本地(本地MCU对应的)IP、目标(服务器的)IP、子网掩码(我不知道子网掩码的具体作用,IPV4这里可以设置为255.255.255.0(0xFFFFFF00))、网关(192.168.1.1)、DNS(192.168.1.1)、物理地址(MAC地址是由stm32的序列号得来的)、本机端口、目标端口
u8 Gateway_IP[4];//网关IP地址
u8 Sub_Mask[4]; //子网掩码
u8 Phy_Addr[6]; //物理地址(MAC)
u8 IP_Addr[4]; //本机IP地址
u8 DNS[4]; //DNS
u8 S0_Port[2]; //端口0的端口号(5000)
u8 S0_DIP[4]; //端口0目的IP地址
u8 S0_DPort[2]; //端口0目的端口号(6000)
void Load_Net_Parameters(void)//加载网关参数
{
IP_Addr[0] = (PNetData->Local_IP & 0xFF000000) >> 24; //加载本机IP地址
IP_Addr[1] = (PNetData->Local_IP & 0x00FF0000) >> 16;
IP_Addr[2] = (PNetData->Local_IP & 0x0000FF00) >> 8;
IP_Addr[3] = (PNetData->Local_IP & 0x000000FF);
S0_DIP[0] = (PNetData->Dest_IP & 0xFF000000) >> 24; //加载端口0的目的IP地址
S0_DIP[1] = (PNetData->Dest_IP & 0x00FF0000) >> 16;
S0_DIP[2] = (PNetData->Dest_IP & 0x0000FF00) >> 8;
S0_DIP[3] = (PNetData->Dest_IP & 0x000000FF);
Sub_Mask[0] = (PNetData->SubMask & 0xFF000000) >> 24; //加载子网掩码
Sub_Mask[1] = (PNetData->SubMask & 0x00FF0000) >> 16;
Sub_Mask[2] = (PNetData->SubMask & 0x0000FF00) >> 8;
Sub_Mask[3] = (PNetData->SubMask & 0x000000FF);
Gateway_IP[0] = (PNetData->GateWay & 0xFF000000) >> 24;
Gateway_IP[1] = (PNetData->GateWay & 0x00FF0000) >> 16;
Gateway_IP[2] = (PNetData->GateWay & 0x0000FF00) >> 8;
Gateway_IP[3] = (PNetData->GateWay & 0x000000FF);
DNS[0] = (PNetData->DNS & 0xFF000000) >> 24;
DNS[1] = (PNetData->DNS & 0x00FF0000) >> 16;
DNS[2] = (PNetData->DNS & 0x0000FF00) >> 8;
DNS[3] = (PNetData->DNS & 0x000000FF);
Phy_Addr[0] = (PNetData->MAC_Addr1 & 0xFF00) >> 8; //加载物理地址
Phy_Addr[1] = (PNetData->MAC_Addr1 & 0x00FF);
Phy_Addr[2] = (PNetData->MAC_Addr2 & 0xFF00) >> 8;
Phy_Addr[3] = (PNetData->MAC_Addr2 & 0x00FF);
Phy_Addr[4] = (PNetData->MAC_Addr3 & 0xFF00) >> 8;
Phy_Addr[5] = (PNetData->MAC_Addr3 & 0x00FF);
S0_Port[0] = (PNetData->S_PORT & 0xFF00) >> 8 ;//加载端口0的端口号5000
S0_Port[1] = (PNetData->S_PORT & 0x00FF);
S0_DPort[0] = (PNetData->D_PORT & 0xFF00) >> 8 ;//加载端口0的目的端口号8080
S0_DPort[1] = PNetData->D_PORT & 0x00FF;
S0_Mode = TCP_CLIENT; //加载端口0的工作模式,TCP客户端模式
}
3.3硬件复位w5500
低电平复位
void W5500_Hardware_Reset(void)
{
u8 i = 0;
GPIO_ResetBits(W5500_RST_PORT, W5500_RST);//复位引脚拉低
delay_ms(50);
//OSTimeDly(50);
GPIO_SetBits(W5500_RST_PORT, W5500_RST);//复位引脚拉高
delay_ms(200);
//OSTimeDly(200);
for(i = 0; i < 3; i++)
{
if((Read_W5500_1Byte(PHYCFGR) & LINK) == 1)
{
break;
}
}
// while((Read_W5500_1Byte(PHYCFGR) & LINK)==0);//等待以太网连接完成 读取W5500 PHY 配置寄存器 判断该字节最低位的连接状态
}
片选拉低发送16位寄存器地址 PHYCFGR 0x002e
发送一帧数据的格式
这个用作寄存器地址
2.2.2是控制段这里不好截图就不接 7:3 选择哪个寄存器00000表示通用寄存器
2表示读写模式 0读 1写
1:0 OM表示SPI工作模式:这个和stm32上说的0123工作模式不是一个意思,这里指的是SPI 模式支持 2 种模式:可变数据长度模式和固定长度模式
可变数据长度模式(VDM):数据长度通过 SCSn 控制;
外设主机使 SCSn 信号拉低(高电平到低电平),并通知 W5500 SPI数据帧地址段的起始地址。然后外设主机传输控制段。此时,OM[1:0]=’00’。在 N 字节数据段传输完毕后,SCSn 信号拉高(低电平到高电平)且通知 W5500SPI 数据帧数据段的结束地址。在可变数据长度模式下,SCSn 必须通过外设主机通过 SPI 数据帧单元控制。(参见图 4)
- 固定数据长度模式(FDM)
: 在固定数据长度模式下,数据的长度通过 OM[1:0]位来设定,但是不能为‘00’。所以,SCSn 信号应该保持低电平状态,然后根据 OM[1:0]的值确定一种长度类型(介于 1 字节,2 字节,4 字节)
这里有点冲突我使用图4接法程序中用的却是1个固定字节
2.2.3 数据段
在 SPI 工作模式位 OM[1:0]设定了控制端之后,数据段被设定为 2 种长度类型:1 种为可变的 N 字节长度(可变数据长度模式),另以一种为确定的 1/2/4 字节长度(固定数据长度模式)。此时,1 字节数据从最大标志位到最小标志位,通过 MOSI 或者 MISO 信号传输。
这里我理解是高位先行然后因为是全双工模式MISO先读一个数据,MOSI还得发一个假数据 然后再读了一下是怕数据出错吗?
这里读的是寄存器PHYCFGR 0x002e 如果bit0=1表示链接成功
unsigned char Read_W5500_1Byte(unsigned short reg)
{
unsigned char i;
GPIO_ResetBits(W5500_SCS_PORT, W5500_SCS);//置W5500的SCS为低电平
SPI1_Send_Short(reg);//通过SPI1写16位寄存器地址
SPI1_Send_Byte(FDM1 | RWB_READ | COMMON_R); //通过SPI1写控制字节,1个字节数据长度,读数据,选择通用寄存器
i = SPI_I2S_ReceiveData(SPI1);
SPI1_Send_Byte(0x00);//发送一个哑数据
i = SPI_I2S_ReceiveData(SPI1); //读取1个字节数据
GPIO_SetBits(W5500_SCS_PORT, W5500_SCS);//置W5500的SCS为高电平
return i;//返回读取到的寄存器数据
}
3.4设置寄存器初始化5500
Write_W5500_1Byte(MR, RST) 内部寄存器初始化
Write_W5500_nByte(GAR, Gateway_IP, 4); 写网关IP寄存器
Write_W5500_nByte(SUBR, Sub_Mask, 4); 写子网掩码寄存器
Write_W5500_nByte(SHAR, Phy_Addr, 6);写mac地址寄存器
Write_W5500_nByte(SIPR, IP_Addr, 4);写本机IP(源IP)
for(i = 0; i < 8; i++)
{
Write_W5500_SOCK_1Byte(i, Sn_RXBUF_SIZE, 0x02); //Socket Rx memory size=2k
Write_W5500_SOCK_1Byte(i, Sn_TXBUF_SIZE, 0x10); //Socket Tx mempry size=16k
}
配置每个socket的收发缓冲区分别为2K和16K
Write_W5500_2Byte(RTR, 0x07d0);设置重传超时时间
Write_W5500_1Byte(IMR, IM_IR7 | IM_IR6);//配置中断屏蔽寄存器
Write_W5500_1Byte(RCR, 8);//设置超时重发次数寄存器
Write_W5500_1Byte(SIMR, S0_IMR);设置socket中断屏蔽寄存器
Write_W5500_SOCK_1Byte(0, Sn_IMR, IMR_SENDOK | IMR_TIMEOUT | IMR_RECV | IMR_DISCON | IMR_CON);//向socket0 的Sn_IMR寄存器写值
void W5500_Init_Reg(void)
{
u8 i = 0;
Write_W5500_1Byte(MR, RST);//软件复位W5500,置1有效 复位,复位后自动清0 模式寄存器用于S/W软件复位
delay_ms(10);//延时10ms,自己定义该函数
//OSTimeDly(10);
//设置网关(Gateway)的IP地址,Gateway_IP为4字节unsigned char数组,自己定义
//使用网关可以使通信突破子网的局限,通过网关可以访问到其它子网或进入Internet
Write_W5500_nByte(GAR, Gateway_IP, 4);
//设置子网掩码(MASK)值,SUB_MASK为4字节unsigned char数组,自己定义
//子网掩码用于子网运算
Write_W5500_nByte(SUBR, Sub_Mask, 4);
//设置物理地址,PHY_ADDR为6字节unsigned char数组,自己定义,用于唯一标识网络设备的物理地址值
//该地址值需要到IEEE申请,按照OUI的规定,前3个字节为厂商代码,后三个字节为产品序号
//如果自己定义物理地址,注意第一个字节必须为偶数
Write_W5500_nByte(SHAR, Phy_Addr, 6);
//设置本机的IP地址,IP_ADDR为4字节unsigned char数组,自己定义
//注意,网关IP必须与本机IP属于同一个子网,否则本机将无法找到网关
Write_W5500_nByte(SIPR, IP_Addr, 4);
//设置发送缓冲区和接收缓冲区的大小,参考W5500数据手册
for(i = 0; i < 8; i++)
{
Write_W5500_SOCK_1Byte(i, Sn_RXBUF_SIZE, 0x02); //Socket Rx memory size=2k
Write_W5500_SOCK_1Byte(i, Sn_TXBUF_SIZE, 0x10); //Socket Tx mempry size=16k
}
//设置重试时间,默认为2000(200ms)
//每一单位数值为100微秒,初始化时值设为2000(0x07D0),等于200毫秒
Write_W5500_2Byte(RTR, 0x07d0);
//设置重试次数,默认为8次
//如果重发的次数超过设定值,则产生超时中断(相关的端口中断寄存器中的Sn_IR 超时位(TIMEOUT)置“1”)
Write_W5500_1Byte(RCR, 8);
//启动中断,参考W5500数据手册确定自己需要的中断类型
//IMR_CONFLICT是IP地址冲突异常中断,IMR_UNREACH是UDP通信时,地址无法到达的异常中断
//其它是Socket事件中断,根据需要添加
Write_W5500_1Byte(IMR, IM_IR7 | IM_IR6);
Write_W5500_1Byte(SIMR, S0_IMR);
Write_W5500_SOCK_1Byte(0, Sn_IMR, IMR_SENDOK | IMR_TIMEOUT | IMR_RECV | IMR_DISCON | IMR_CON);
}
3.5使用
W5500_Socket_Set();//W5500端口初始化配置 每次循环都要重新配置 主要是设置模式为TCP_CLIENT模式打开socket0 连接socket0
W5500端口初始化配置
W5500_Socket_Set();//W5500端口初始化配置
S0_State 是个全局变量
初始化时加载网络参数 已经将S0_Mode 设置为TCP_CLIENT
void W5500_Socket_Set(void)
{
if(S0_State == 0) //端口0初始化配置
{
if(S0_Mode == TCP_CLIENT) //TCP客户端模式
{
if(Socket_Connect(0) == TRUE)
{
S0_State = S_INIT;
}
else
S0_State = 0;
}
}
}
Write_W5500_SOCK_1Byte(s, Sn_MR, MR_TCP); //设置socket为TCP模式
Sn_MR寄存器 socket 模式寄存器设置为TCP模式
Write_W5500_SOCK_1Byte(s, Sn_CR, OPEN); //打开Socket
Sn_CR (Socket n 配置寄存器) 设置为socket打开
Read_W5500_SOCK_1Byte(s, Sn_SR) != SOCK_INI
TCP模式下对应的Sn_SR值
Write_W5500_SOCK_1Byte(s, Sn_CR, CONNECT);
配为链接状态
unsigned char Socket_Connect(SOCKET s)
{
Write_W5500_SOCK_1Byte(s, Sn_MR, MR_TCP); //设置socket为TCP模式
Write_W5500_SOCK_1Byte(s, Sn_CR, OPEN); //打开Socket
delay_ms(10);//延时5ms
// OSTimeDly(5);
if(Read_W5500_SOCK_1Byte(s, Sn_SR) != SOCK_INIT) //如果socket打开失败
{
Write_W5500_SOCK_1Byte(s, Sn_CR, CLOSE); //打开不成功,关闭Socket
return FALSE;//返回FALSE(0x00)
}
Write_W5500_SOCK_1Byte(s, Sn_CR, CONNECT); //设置Socket为Connect模式
return TRUE;//返回TRUE,设置成功
}
获取信号量获取不到被挂起
OSSemPend(W5500INTFLAG, 0, &err);
这个信号量会在终端中被释放 所以每当有中断 就会触发信号量释放 这个任务就会被就绪
void EXTI0_IRQHandler(void)
{
OS_CPU_SR cpu_sr;
OS_ENTER_CRITICAL(); // Tell uC/OS-II that we are starting an ISR //
OSIntNesting++;
OS_EXIT_CRITICAL();
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line0);
OSSemPost(W5500INTFLAG);
}
OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */
}
然后是中断处理
先看是什么中断
i = Read_W5500_1Byte(IR);//读取中断标志寄存器
再把中断清除由于只有高4位有用这里与上0xf0
Write_W5500_1Byte(IR, (i & 0xf0)); //回写清除中断标志
查看是不是socket中断
i = Read_W5500_1Byte(SIR); //读取端口中断标志寄存器
Write_W5500_SOCK_1Byte(0, SIR, i);//我觉的这写反了应该是 Write_W5500_1Byte( SIR, 0); 把SIR清零 而且这应该是写通用寄存器
后面才是查看socket寄存器
这里注意想把Sn_IR的某位清零就对这位写1 所以中断是谁就把谁写去
这里分了几种情况
1、连接成功中断
做一下处理 连接标识符写为1
断开时间归零
连接状态全局变量|上连接成功
网络状态指示灯关
2、断开连接中断
做一下处理 socket0断开连接标识符写为1
关闭socket0
初始化socket0
连接状态全局变量设置为连接失败
网络状态指示灯开
3、数据发送成功中断
复位接收与发送数据超时计数归零
socket0断开连接标识符写为0
断开连接时间归零
发送数据状态为发送数据完成
网络状态指示灯关
4、接收数据成功中断
复位接收与发送数据超时计数归零
socket0断开连接标识符写为0
断开连接时间归零
发送数据状态为接收数据完成
网络状态指示灯关
5、Socket连接或数据传输超时处理
超时标志位设置为
关闭socket0
网络连接状态为连接失败0
网络状态指示灯亮
void W5500_Interrupt_Process(void)
{
unsigned char i, j;
i = Read_W5500_1Byte(IR);//读取中断标志寄存器
Write_W5500_1Byte(IR, (i & 0xf0)); //回写清除中断标志
if((i & CONFLICT) == CONFLICT)//IP地址冲突异常处理
{
IP_Conflict = 1;//30秒后自动复位
}
if((i & UNREACH) == UNREACH)//UDP模式下地址无法到达异常处理
{
//自己添加代码
// SaveEventLog(3, 0, "UDP UNREACH");
}
i = Read_W5500_1Byte(SIR); //读取端口中断标志寄存器
Write_W5500_SOCK_1Byte(0, SIR, i);
if((i & S0_INT) == S0_INT)//Socket0事件处理
{
j = Read_W5500_SOCK_1Byte(0, Sn_IR); //读取Socket0中断标志寄存器
Write_W5500_SOCK_1Byte(0, Sn_IR, j);
if((j & IR_CON) == IR_CON) //在TCP模式下,Socket0成功连接
{
Socket0_Connect_Flag = 1;//连接标识
S0_Con_FailTime = 0;//断开时间归零
S0_State |= S_CONN; //网络连接状态0x02,端口完成连接,可以正常传输数据
DBG_LED3_OFF();//网络连接状态指示灯 连接
// DBG_LED4_OFF();//网络连接超时 灭
// DEBUG_OUT("Socket0 Success Connect\r\n");
//断开超时归零
}
if((j & IR_DISCON) == IR_DISCON) //在TCP模式下Socket断开连接处理
{
Socket0_Disconnect_Flag = 1;//断开连接标识
Write_W5500_SOCK_1Byte(0, Sn_CR, CLOSE); //关闭端口,等待重新打开连接
Socket_Init(0); //指定Socket(0~7)初始化,初始化端口0
S0_State = 0; //网络连接状态0x00,端口连接失败
DBG_LED3_ON();//网络连接状态指示灯 断开
// DEBUG_OUT("Socket0 Disconnect\r\n");
}
if((j & IR_SEND_OK) == IR_SEND_OK) //Socket0数据发送完成,可以再次启动S_tx_process()函数发送数据
{
Net_Contect = 0;//复位接收与发送数据超时计数
Socket0_Disconnect_Flag = 0;
S0_Con_FailTime = 0;//断开时间归零
S0_Data |= S_TRANSMITOK; //端口发送一个数据包完成
DBG_LED3_OFF();//W5500发送数据完成指示灯
// DBG_LED4_OFF();//网络连接超时 灭
}
if((j & IR_RECV) == IR_RECV) //Socket接收到数据,可以启动S_rx_process()函数
{
Net_Contect = 0;//复位接收与发送数据超时计数
Socket0_Disconnect_Flag = 0;
S0_Con_FailTime = 0;//断开时间归零
S0_Data |= S_RECEIVE; //端口接收到一个数据包
DBG_LED3_OFF();//W5500接收数据完成指示灯
// DBG_LED4_OFF();//网络连接超时 灭
}
if((j & IR_TIMEOUT) == IR_TIMEOUT) //Socket连接或数据传输超时处理
{
Socket0_Timeout_Flag = 1;
Write_W5500_SOCK_1Byte(0, Sn_CR, CLOSE); // 关闭端口,等待重新打开连接
S0_State = 0; //网络连接状态0x00,端口连接失败
DBG_LED3_ON();//网络连接超时 亮
// DEBUG_OUT("Socket0 Connect Timeout\r\n");
}
}
}
然后判断读取状态读到了就使用吧
if((S0_Data & S_RECEIVE) == S_RECEIVE)//如果Socket0接收到数据
S0_Data &= ~S_RECEIVE;//将状态位清零
Read_SOCK_Data_Buffer(0, Rx_Buffer);
先写到这吧后面可能会补充一些网络的知识
参考文件《W5500 数据手册Version 1.0》官网上可以找到