STM32+W5500实现UDP通信
一、前情提要
最近接触到了W5500,自己前后跑通也花了两天时间,之前做的硬件开发一直没有留痕,顾从这个项目开始决定写博客留痕
二、硬件配置
1.芯片型号
STM32L475VET6
2.开发软件
STM32CubeMx+keil5
3.STM32CubeMx配置
首先配置时钟源,我这里采用的是外部8M的高速时钟源。
选择HSE外部时钟源,锁相环倍频等参数如图所示
因为这款芯片的最大工作频率是80M,我直接给上最大了。
然后将对应的外部时钟引脚,UART串口引脚(方便调试)以及SPI通信用的引脚配置好。
我这里SPI片选用的是软件片选,所以对应的IO口设置成推挽输出就可以。
SPI配置的是8位数据传输,片选是低电平空闲,上升沿有效,下面是具体的工作模式。SPI的工作模式至少要时钟源二分频,所以最大是40M。
三、官方库移植
1.W5500官方库移植
https://github.com/Wiznet/ioLibrary_Driver
这是Wiznet官方给出的驱动库,里面已经定义好了相关的底层IO以及函数,我们只需要配置一些空的实现具体移植的函数即可。
下载好这个包后只需将Ethernet复制到你的对应工程文件夹下,然后在keil里面添加对应C文件和H文件路径。
只需要添加这三个文件即可。
四、软件代码
接下来进行具体代码的编写
首先打开wizchip_conf.c这个文件,包含对应的头文件
#include "wizchip_conf.h"
#include "main.h"
#include "stm32l4xx.h"
#include "stm32l4xx_hal.h"
#include "spi.h"
然后编写需要用到的几个函数
void wizchip_cris_enter(void) {__set_PRIMASK(1);}//关中断进入临界区
void wizchip_cris_exit(void) {__set_PRIMASK(0);}//开中断进入临界区
void wizchip_cs_select(void) {HAL_GPIO_WritePin(W5500_CS_GPIO_Port, W5500_CS_Pin, GPIO_PIN_RESET);}//片选
void wizchip_cs_deselect(void) {HAL_GPIO_WritePin(W5500_CS_GPIO_Port, W5500_CS_Pin, GPIO_PIN_SET);}//关片选
uint8_t wizchip_spi_readbyte(void)//SPI读取单字节
{
uint8_t data;
if(HAL_SPI_Receive(&hspi2,&data,1,1000) != HAL_OK)
{
data = 0;
}
return data;
}
void wizchip_spi_writebyte(uint8_t wb)//SPI写单字节
{
HAL_SPI_Transmit(&hspi2,&wb,1,1000);
}
void wizchip_spi_readburst(uint8_t* pBuf, uint16_t len)//SPI读多字节
{
if (!pBuf)
{
return;
}
HAL_SPI_Receive(&hspi2, pBuf, len, 1000);
}
void wizchip_spi_writeburst(uint8_t* pBuf, uint16_t len)//SPI写多字节
{
if (!pBuf)
{
return;
}
HAL_SPI_Transmit(&hspi2, pBuf, len, 1000);
}
这几个函数在wizchip_conf.c已经定义好了,只需要找到并且填入对应的内容就行。
//函数绑定
void w5500_regFunc(void)
{
reg_wizchip_cris_cbfunc(wizchip_cris_enter, wizchip_cris_exit);
reg_wizchip_cs_cbfunc(wizchip_cs_select, wizchip_cs_deselect);
reg_wizchip_spi_cbfunc(wizchip_spi_readbyte, wizchip_spi_writebyte);
reg_wizchip_spiburst_cbfunc(wizchip_spi_readburst, wizchip_spi_writeburst);
}
然后需要自己写一个函数绑定的函数,如果你写在wizchip_conf.c里,记得在wizchip_conf.h进行函数声明,写在主函数也可以。
函数配置完成以后就可以进行w5500配置了。
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "w5500.h"
#include "wizchip_conf.h"
#include "string.h"
#include "socket.h"
/* USER CODE END Includes */
老规矩,先记得引用头文件
/* USER CODE BEGIN 0 */
wiz_NetInfo gWIZNETINFO = { .mac = {0x78,0x83,0x68,0x88,0x56,0x72},
.ip = {192,168,3,77},
.sn = {255,255,255,0},
.gw = {192,168,3,1},
.dns = {8,8,8,8},
.dhcp = NETINFO_STATIC};
static void W5500_RESET(void)
{
HAL_GPIO_WritePin(W5500_RST_GPIO_Port, W5500_RST_Pin, GPIO_PIN_SET);
HAL_Delay(10);
HAL_GPIO_WritePin(W5500_RST_GPIO_Port, W5500_RST_Pin, GPIO_PIN_RESET);
HAL_Delay(50);
HAL_GPIO_WritePin(W5500_RST_GPIO_Port, W5500_RST_Pin, GPIO_PIN_SET);
HAL_Delay(10);
}
//初始化芯片参数
void ChipParametersConfiguration(void)
{
uint8_t tmp;
uint8_t memsize[2][8] = {{2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2}};
if(ctlwizchip(CW_INIT_WIZCHIP,(void*)memsize) == -1)
{
printf("WIZCHIP Initialized fail.\r\n");
while(1);
}
do{
if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -1)
{
printf("Unknown PHY Link stauts.\r\n");
while(1);
}
}while(tmp == PHY_LINK_OFF);
}
//初始化网络参数 mac ip等
void NetworkParameterConfiguration(void) //Intialize the network information to be used in WIZCHIP
{
uint8_t tmpstr[6];
ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO);
ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO);
ctlwizchip(CW_GET_ID,(void*)tmpstr);
printf("\r\n=== %s NET CONF ===\r\n",(char*)tmpstr);
printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n",gWIZNETINFO.mac[0],gWIZNETINFO.mac[1],gWIZNETINFO.mac[2],gWIZNETINFO.mac[3],gWIZNETINFO.mac[4],gWIZNETINFO.mac[5]);
printf("SIP: %d.%d.%d.%d\r\n", gWIZNETINFO.ip[0],gWIZNETINFO.ip[1],gWIZNETINFO.ip[2],gWIZNETINFO.ip[3]);
printf("GAR: %d.%d.%d.%d\r\n", gWIZNETINFO.gw[0],gWIZNETINFO.gw[1],gWIZNETINFO.gw[2],gWIZNETINFO.gw[3]);
printf("SUB: %d.%d.%d.%d\r\n", gWIZNETINFO.sn[0],gWIZNETINFO.sn[1],gWIZNETINFO.sn[2],gWIZNETINFO.sn[3]);
printf("DNS: %d.%d.%d.%d\r\n", gWIZNETINFO.dns[0],gWIZNETINFO.dns[1],gWIZNETINFO.dns[2],gWIZNETINFO.dns[3]);
printf("======================\r\n");
}
void W5500_ChipInit(void)
{
W5500_RESET();//硬件复位W5500
w5500_regFunc();//绑定SPI函数
ChipParametersConfiguration();
NetworkParameterConfiguration();
}
/* USER CODE END 0 */
通过以上函数就能对W5500实现初始化。
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LPUART1_UART_Init();
MX_SPI2_Init();
/* USER CODE BEGIN 2 */
W5500_ChipInit();//初始化W5500
uint8_t remote_ip[4]={192,168,3,76};
uint16_t remote_port=9090;
uint16_t local_port=6060;
uint16_t len=0;
uint8_t buf[]="Tx";
uint8_t buffer[128];
// buf[0] = 0x55;
// buf[1] = 0xAA;
// uint16_t leng = 2;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while(1) // Socket状态机
{
switch(getSn_SR(0)) // 获取socket0的状态
{
case SOCK_UDP: // Socket处于初始化完成(打开)状态
// printf("Open Socket sucessful.\r\n");
// sendto(0,buffer,sizeof(buffer), remote_ip, remote_port);
// printf("Send data sucessful.\r\n");
HAL_Delay(100);
if(getSn_IR(0) & Sn_IR_RECV)
{
setSn_IR(0, Sn_IR_RECV); // Sn_IR的RECV位置1
}
// 数据回环测试程序:数据从远程上位机发给W5500,W5500接收到数据后再回给远程上位机
if((len=getSn_RX_RSR(0))>0)
{
memset(buffer,0,len+1);
recvfrom(0,buffer, sizeof(buffer), remote_ip,&remote_port); // W5500接收来自远程上位机的数据,并通过SPI发送给MCU
printf("receive data sucessful.\r\n");
printf("%s\r\n",buffer); // 串口打印接收到的数据
sendto(0,buffer,sizeof(buffer), remote_ip, remote_port); // 接收到数据后再回给远程上位机,完成数据回传
}
break;
case SOCK_CLOSED: // Socket处于关闭状态
socket(0,Sn_MR_UDP,local_port,0); // 打开Socket0,并配置为UDP模式,打开一个本地端口
printf("Open Socket failed.\r\n");
break;
}
}
/* USER CODE END 3 */
}
主函数先进行发送测试,然后进行回调测试。
五、电脑配置
网口和电脑正确连线后,打开控制面板->网络和internet->网络和共享中心->更改适配器设置->对应的以太网
双击这个
输入你设置好的相应参数,不会找Ip可以输入cmd打开命令行面板输入ipconfig -all查看ip信息,查看端口是否被占用输入netstat -aon|findstr "端口"就可以。
注意!
1.这里输入的ip地址是你电脑的ip,也是你设置的remote_ip,即远程ip,千万别设置成w5500的本地ip,我当时在这踩了坑。
2.电脑ip和W5500的ip需要在同一网段,比如192.168.3.x,除了x,其它都要相同,mac地址符合规则就行,网关可以都用电脑的默认网关,DNS无所谓,直接填阿里云的8.8.8.8就行
3.关闭电脑防火墙,不然可能出现时不时断一下的情况
以上工作都完成后,对板子进行烧录复位,在命令行输入ping w5500ip
成功ping通后,打开网络调试助手,输入对应ip和端口
通信成功!
作者:我不南郭