STM32_HAL_W5500_以太网通讯

STM32_HAL_W5500_以太网通讯

本文代码地址: github
本文使用STM32F103通过W5500与PC建立UDP通讯。
W5500与MCU连接如下图所示。
W5500示例图
STM32与W5500连接

1.使用CubeMX建立工程

直接参考我GitHub仓库里的CubeMX工程即可。
注意SPI的配置,配置好SPI、CS、INT、RST即可。
SPI

2.下载W5500官方库

下载地址:github
将socket.c、wizchip_conf.c、w5500.c加入工程即可。
MDK工程配置

3.初始化W5500

初始化代码写在MyUDP.c中,具体如下:
初始化所需函数:

//片选
void W5500_Select(void) {
    HAL_GPIO_WritePin(W5500_CS_GPIO_Port, W5500_CS_Pin, GPIO_PIN_RESET);
}
void W5500_Unselect(void) {
    HAL_GPIO_WritePin(W5500_CS_GPIO_Port, W5500_CS_Pin, GPIO_PIN_SET);
}
void W5500_Restart(void) {
    HAL_GPIO_WritePin(W5500_RST_GPIO_Port, W5500_RST_Pin, GPIO_PIN_RESET);
    HAL_Delay(1);  // delay 1ms
    HAL_GPIO_WritePin(W5500_RST_GPIO_Port, W5500_RST_Pin, GPIO_PIN_SET);
    HAL_Delay(1600);  // delay 1600ms
}

void W5500_ReadBuff(uint8_t* buff, uint16_t len) {
    HAL_SPI_Receive(&hspi1, buff, len, HAL_MAX_DELAY);
}

void W5500_WriteBuff(uint8_t* buff, uint16_t len) {
    HAL_SPI_Transmit(&hspi1, buff, len, HAL_MAX_DELAY);
}

uint8_t W5500_ReadByte(void) {
    uint8_t byte;
    W5500_ReadBuff(&byte, sizeof(byte));
    return byte;
}

void W5500_WriteByte(uint8_t byte) {
    W5500_WriteBuff(&byte, sizeof(byte));
}

//配置W5500网络信息
wiz_NetInfo gSetNetInfo = {
  .mac  = {0x00, 0x08, 0xdc, 0x11, 0x11, 0x11},
  .ip   = {192, 168, 1, 1},
  .sn   = {255, 255, 255, 0},
  .gw   = {192, 168, 3, 1},
  .dns  = {144, 144, 144, 144},
  .dhcp = NETINFO_STATIC};

wiz_NetInfo gGetNetInfo;

enum Status
{
  Failed = 0,
  Success = 1
};

/**
 * @brief valid the result of set net info
 * @return 1: Success
 *         0: Failed
*/
uint8_t validSetNetInfoResult(wiz_NetInfo* _set, wiz_NetInfo* _get)
{
  return (!memcmp(_set, _get, sizeof(wiz_NetInfo)));  // if same, memcmp return 0
}

接着在主函数中调用Init函数:

void UDPinit(void)
{
  reg_wizchip_cs_cbfunc(W5500_Select, W5500_Unselect);
  reg_wizchip_spi_cbfunc(W5500_ReadByte, W5500_WriteByte);

  W5500_Restart();  // hardware restart through RESET pin

  ctlnetwork(CN_SET_NETINFO, (void*)&gSetNetInfo);  // set net info
  // maybe need delay
  ctlnetwork(CN_GET_NETINFO, (void*)&gGetNetInfo);  // get net info

  // W5500 has 8 channel, 32k buffer, 2 means 2KBytes
  uint8_t buffer_size_8channel_tx_rx[16] = {2, 2, 2, 2, 2, 2, 2, 2,  // 8 channel tx
                                            2, 2, 2, 2, 2, 2, 2, 2}; // 8 channel rx
  if(ctlwizchip(CW_INIT_WIZCHIP,(void*)buffer_size_8channel_tx_rx))
  {
    // failed
    
  }

  uint8_t sta = getSn_SR(SOCK_UDPS);
  if(sta == SOCK_CLOSED)
  {
    socket(SOCK_UDPS, Sn_MR_UDP, 5001, 0);
  }
  HAL_Delay(100);

}

回环测试函数与Send函数:

void do_udp(void)
{                                                              
	uint16_t len=0;	
	switch(getSn_SR(SOCK_UDPS))       /*获取socket的状态*/
	{
		case SOCK_CLOSED:                 /*socket处于关闭状态*/
			socket(SOCK_UDPS,Sn_MR_UDP,5001,0);    /*初始化socket*/
		  break;
		
		case SOCK_UDP:      /*socket初始化完成*/
			HAL_Delay(10);  
			if(getSn_IR(SOCK_UDPS) & Sn_IR_RECV) //检查是否有接收中断
			{
				setSn_IR(SOCK_UDPS, Sn_IR_RECV);  /*清接收中断*/
			}
			if((len=getSn_RX_RSR(SOCK_UDPS))>0)  /*接收到数据*/
			{
				recvfrom(SOCK_UDPS,buff, len, remote_ip,&remote_port);               /*W5500接收计算机发送来的数据*/
				sendto(SOCK_UDPS,buff,len-8, remote_ip, remote_port);                /*W5500把接收到的数据发送*/
        memset(buff, 0, sizeof(buff));
			}
			break;
	}

}

void UDP_send(uint8_t* data,uint8_t len)
{
  sendto(SOCK_UDPS, data, len, remote_ip, remote_port);
  memset(data, 0, len);
}

利用定时器每隔1s向PC发送信息:

void TIM2_IRQHandler(void)
{
  /* USER CODE BEGIN TIM2_IRQn 0 */
  HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);

  if(a == 0)
  {
    UDP_send_buff[0] = 0x01;
    UDP_send_buff[1] = 0x02;
    UDP_send_buff[2] = 0x03;
    UDP_send_buff[3] = 0x04;

    UDP_send(UDP_send_buff,4);
    a = 1;
  }
  else
  {
    UDP_send_buff[0] = 0x05;
    UDP_send_buff[1] = 0x06;


    UDP_send(UDP_send_buff,2);
    a = 0;
  }

  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */

  /* USER CODE END TIM2_IRQn 1 */
}

4.测试

可使用Github仓库中C#写的上位机进行测试,上位机每4S向STM32发送一次数据
(值得注意的是,实际上PC发送时的IP是由操作系统自动分配的,也就是您必须先给STM32发送一次数据,由recvfrom函数获取PC的IP,传值给remote_ip,您才能顺利发送数据给PC)

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;

class Program
{
    static UdpClient receiver;
    static IPEndPoint RemoteEndPoint = new IPEndPoint(IPAddress.Parse("192.168.1.1"), 5001); // STM32的IP和端口
    static ManualResetEventSlim stopReceiver = new ManualResetEventSlim(false);

    static void Main(string[] args)
    {
        receiver = new UdpClient(5002);

        // 创建并启动接收数据的线程
        Thread receiveThread = new Thread(ReceiveData);
        receiveThread.Start();

        // 创建并启动定时发送数据的线程
        Thread sendThread = new Thread(SendDataPeriodically);
        sendThread.Start();

        Console.WriteLine("按任意键退出...");
        Console.ReadKey();

        // 请求线程停止
        stopReceiver.Set();

        // 等待线程结束
        receiveThread.Join();
        sendThread.Join();

        receiver.Close();
    }

    static void ReceiveData()
    {
        while (!stopReceiver.IsSet)
        {
            try
            {
                byte[] data = receiver.Receive(ref RemoteEndPoint);
                //Console.WriteLine($"接收到数据的来源: {RemoteEndPoint.Address}:{RemoteEndPoint.Port}");

                Console.Write($"STM32 -> PC:({RemoteEndPoint.Address}:{RemoteEndPoint.Port})  ");
                for (int i = 0; i < data.Length; i++)
                {
                    Console.Write("{0:x2} ", data[i]);
                }
                Console.WriteLine(); // 换行
            }
            catch (ObjectDisposedException) // 可能在关闭receiver时抛出
            {
                return;
            }
            catch (SocketException ex)
            {
                Console.WriteLine($"Socket异常: {ex.Message}");
            }
        }
    }

    static void SendDataPeriodically()
    {
        //string messageToSend = "Hello from PC!";
        //byte[] sendData = Encoding.UTF8.GetBytes(messageToSend);
        string sourceIp = "192.168.1.2"; // 示例IP,根据实际情况替换
        int sourcePort = 5002; // 假定的发送端口,如果是从特定端口发送则应获取该端口
        byte[] sendData = {0xFF,0xFF, 0xFF, 0xFF };
        string messageToSend = ByteArrayToHexadecimalString(sendData);
        while (!stopReceiver.IsSet)
        {
            try
            {
                receiver.Send(sendData, sendData.Length, RemoteEndPoint);
                //Console.WriteLine($"PC -> STM32:({sourceIp}:{sourcePort})  {messageToSend}");
                Console.WriteLine($"\u001b[33mPC -> STM32:({sourceIp}:{sourcePort})  {messageToSend}\u001b[0m");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"发送失败: {ex.Message}");
            }

            Thread.Sleep(4000); // 每隔2秒发送一次
        }
    }

    public static string ByteArrayToHexadecimalString(byte[] byteArray)
    {
        StringBuilder hexString = new StringBuilder(byteArray.Length * 2);
        foreach (byte b in byteArray)
        {
            hexString.Append(b.ToString("X2"));
            hexString.Append(" ");
        }
        return hexString.ToString();
    }
}
  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值