芯领域电子科技 发表于 2024-7-16 10:23

基于MM32F3273移植W5500的ioLibrary实现PING测试

本帖最后由 芯领域电子科技 于 2024-7-16 10:25 编辑

#申请原创#   @小跑堂

1、MM32F3270简介
MM32F3270系列MCU使用高性能的ARM Cortex-M3为内核的32位微控制器,最高工作频率可达120MHz,内置高速存储器,256KB FLASH & 128KB SRAM,丰富的 I/O 端口和外设连接到外部总线。本产品包含多达3个12位的ADC、2个比较器、 2个16 位通用定时器、2个32 位通用定时器、2个16 位基本定时器和2个16位高级定时器。还包含标准的通信接口:2 个I2C接口、3个I2S接口、3个SPI 接口、1个USB OTG全速接口、1个CAN接口、1个SDIO接口和8个UART接口。


2、W5500简介
W5500是一款全硬件TCP/IP嵌入式以太网控制器,为嵌入式系统提供了更加简易的互联网连接方案。 W5500集成了TCP/IP协议栈,10/100M以太网数据链路层(MAC)及物理层(PHY),使得用户使用单芯片就能够在他们的应用中拓展网络连接。W5500内置的全硬件TCP/IP协议栈支持TCP,UDP,IPv4,ICMP,ARP,IGMP以及 PPPoE协议。W5500还内嵌了32K字节片上缓存以供以太网包处理。如果使用W5500,只需要一些简单的Socket编程就轻松能实现以太网应用。这将会比其他嵌入式以太网方案更加快捷、简便。



3、硬件原理图
我们直接购买了一块使用STM32F103C8T6作为主控MCU的W5500开发板。对于MM32F3270系列MCU支持LQFP48/64/100/144四种封装形式,与STM32F103系列在硬件上能够做到完全的PIN TO PIN兼容,所以我们将板载的STM32F103C8T6直接替换成MM32F3270G6P,原理图如下所示:


通过如上原理图获知,PC13控制LED发光二极管、PA0作为输入按键检测端口引脚、PA9/PA10作为UART串口功能,连接到了RS232芯片上,与DB9相连接、最后就是MM32F3273与W5500连接的部分了,使用PA2作为W5500的硬件复位控制引脚、使用PA3作为W5500的中断输入检测引脚、使用SPI1(PA4/PA5/PA6/PA7)与W5500的SPI通讯接口相连接,实现数据交互。

4、程序设计
4.1.下载MM32F3270系列MCU的官方库函数与例程:
https://www.mindmotion.com.cn/products/mm32mcu/performance_line/mm32f_performance/mm32f3270/

4.2.下载W5500官方的库函数与例程:
https://github.com/Wiznet/ioLibrary_Driver

4.3.初始化板载资源配置(KEY、LED、UART、SysTick等)
#define __PLATFORM_C

#include "platform.h"

volatile uint32_t SysTick_DelayTick = 0;

void PLATFORM_InitSysTick(void)
{
    RCC_ClocksTypeDefRCC_Clocks;
    RCC_GetClocksFreq(&RCC_Clocks);

    if (SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000))
    {
      while (1)
      {
      }
    }

    NVIC_SetPriority(SysTick_IRQn, 0x0);
}

void SysTick_DelayMS(uint32_t Tick)
{
    SysTick_DelayTick = Tick;

    while (SysTick_DelayTick)
    {
    }
}

void PLATFORM_InitConsole(uint32_t Baudrate)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    UART_InitTypeDef UART_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_UART1, ENABLE);

    UART_StructInit(&UART_InitStruct);
    UART_InitStruct.BaudRate      = Baudrate;
    UART_InitStruct.WordLength    = UART_WordLength_8b;
    UART_InitStruct.StopBits      = UART_StopBits_1;
    UART_InitStruct.Parity      = UART_Parity_No;
    UART_InitStruct.HWFlowControl = UART_HWFlowControl_None;
    UART_InitStruct.Mode          = UART_Mode_Tx;
    UART_Init(UART1, &UART_InitStruct);

    UART_Cmd(UART1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_7);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

int fputc(int ch, FILE *f)
{
    UART_SendData(UART1, (uint8_t)ch);

    while (RESET == UART_GetFlagStatus(UART1, UART_FLAG_TXEPT))
    {
    }

    return (ch);
}

void PLATFORM_InitKEY(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin= GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void PLATFORM_InitLED(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOC, ENABLE);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_13;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_PP;
    GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
}

void PLATFORM_PrintInfo(void)
{
    RCC_ClocksTypeDef RCC_Clocks;

    printf("\r\n");
    printf("\r\nMCU : MM32F3273G6P");
    printf("\r\n");

    switch (RCC->CFGR & RCC_CFGR_SWS)
    {
      case 0x00:
            printf("\r\nHSI used as system clock source");
            break;

      case 0x04:
            printf("\r\nHSE used as system clock source");
            break;

      case 0x08:
            if (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC)
            {
                printf("\r\nPLL (clocked by HSE) used as system clock source");
            }
            else
            {
                printf("\r\nPLL (clocked by HSI) used as system clock source");
            }

            break;

      case 0x0C:
            printf("\r\nLSI used as system clock source");
            break;

      default:
            break;
    }

    RCC_GetClocksFreq(&RCC_Clocks);

    printf("\r\n");
    printf("\r\nSYSCLK Frequency : %7.3f MHz", (double)RCC_Clocks.SYSCLK_Frequency / (double)1000000.0);
    printf("\r\nHCLK   Frequency : %7.3f MHz", (double)RCC_Clocks.HCLK_Frequency   / (double)1000000.0);
    printf("\r\nPCLK1Frequency : %7.3f MHz", (double)RCC_Clocks.PCLK1_Frequency/ (double)1000000.0);
    printf("\r\nPCLK2Frequency : %7.3f MHz", (double)RCC_Clocks.PCLK2_Frequency/ (double)1000000.0);
    printf("\r\n");
}

void PLATFORM_Init(void)
{
    PLATFORM_InitSysTick();

    PLATFORM_InitConsole(115200);

    PLATFORM_InitKEY();

    PLATFORM_InitLED();

    PLATFORM_PrintInfo();
}

4.4.初始化MM32F3273与W5500的控制IO(RST、INT)
对W5500硬件RST的控制,低电平的时候对W5500进行硬件复位,低电平需要持续500us以上;对于INT引脚,如果没有使用到,可以忽略不配置,如果使用到了,需要配置为EXTI,通过EXTI中断来及时的响应W5500的反馈,具体配置如下所示:
void W5500_InitGPIO(void)
{
    EXTI_InitTypeDef EXTI_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    /* RST */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_2;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* W5500 Hardware Reset */
    GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_RESET);
    SysTick_DelayMS(5);
    GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_SET);
    SysTick_DelayMS(5);

    /* INT */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin= GPIO_Pin_3;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource3);

    EXTI_StructInit(&EXTI_InitStruct);
    EXTI_InitStruct.EXTI_Line    = EXTI_Line3;
    EXTI_InitStruct.EXTI_Mode    = EXTI_Mode_Interrupt;
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStruct);

    NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}
4.5.初始化MM32F3273与W5500之间的SPI通讯配置
void W5500_InitSPI1(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDefSPI_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_SPI1, ENABLE);

    SPI_StructInit(&SPI_InitStruct);
    SPI_InitStruct.SPI_Mode      = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize= SPI_DataSize_8b;
    SPI_InitStruct.SPI_DataWidth = 8;
    SPI_InitStruct.SPI_CPOL      = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA      = SPI_CPHA_1Edge;
    SPI_InitStruct.SPI_NSS       = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    SPI_InitStruct.SPI_FirstBit= SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStruct);

    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx);
    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode= GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    SPI_Cmd(SPI1, ENABLE);
}
4.6.实现MM32F3273的SPI数据读写函数,并注册为W5500的接口函数
W5500的接口注册函数有如下几个:
void reg_wizchip_cris_cbfunc(void(*cris_en)(void), void(*cris_ex)(void));
void reg_wizchip_cs_cbfunc(void(*cs_sel)(void), void(*cs_desel)(void));
void reg_wizchip_bus_cbfunc(iodata_t (*bus_rb)(uint32_t addr), void (*bus_wb)(uint32_t addr, iodata_t wb));
void reg_wizchip_spi_cbfunc(uint8_t (*spi_rb)(void), void (*spi_wb)(uint8_t wb));
void reg_wizchip_spiburst_cbfunc(void (*spi_rb)(uint8_t* pBuf, uint16_t len), void (*spi_wb)(uint8_t* pBuf, uint16_t len));因为我们使用的是SPI接口与W5500进行通讯,所以可以仅实现SPI部分的注册函数即可,如下:
void reg_wizchip_cs_cbfunc(void(*cs_sel)(void), void(*cs_desel)(void));
void reg_wizchip_spi_cbfunc(uint8_t (*spi_rb)(void), void (*spi_wb)(uint8_t wb));
void reg_wizchip_spiburst_cbfunc(void (*spi_rb)(uint8_t* pBuf, uint16_t len), void (*spi_wb)(uint8_t* pBuf, uint16_t len));其中reg_wizchip_cs_cbfunc函数是对SPI CS的操作,reg_wizchip_spi_cbfunc函数是注册单字节进行SPI读写操作,reg_wizchip_spiburst_cbfunc函数是注册多字节进行SPI读写操作,reg_wizchip_spi_cbfunc和reg_wizchip_spiburst_cbfunc只需要选其一进行进行函数注册即可。具体实现如下所示:
void W5500_SPI_CS_Select(void)
{
    SPI_CSInternalSelected(SPI1, ENABLE);
}

void W5500_SPI_CS_Deselect(void)
{
    SPI_CSInternalSelected(SPI1, DISABLE);
}

uint8_t W5500_SPI_ReadByte(void)
{
    SPI_SendData(SPI1, 0x00);

    while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
    {
    }

    while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL))
    {
    }

    return (SPI_ReceiveData(SPI1));
}

void W5500_SPI_WriteByte(uint8_t Data)
{
    SPI_SendData(SPI1, Data);

    while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
    {
    }

    while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL))
    {
    }

    SPI_ReceiveData(SPI1);
}

void W5500_SPI_ReadBurst(uint8_t *Buffer, uint16_t Length)
{
    for (uint16_t i = 0; i < Length; i++)
    {
      SPI_SendData(SPI1, 0x00);

      while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
      {
      }

      while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL))
      {
      }

      Buffer = SPI_ReceiveData(SPI1);
    }
}

void W5500_SPI_WriteBurst(uint8_t *Buffer, uint16_t Length)
{
    for (uint16_t i = 0; i < Length; i++)
    {
      SPI_SendData(SPI1, Buffer);

      while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
      {
      }

      while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL))
      {
      }

      SPI_ReceiveData(SPI1);
    }
}
4.7.对W5500进行初始化配置
#include "socket.h"
#include "wizchip_conf.h"

int main(void)
{
    PLATFORM_Init();

    W5500_InitGPIO();
    W5500_InitSPI1();

    reg_wizchip_cris_cbfunc(NULL, NULL);
    reg_wizchip_cs_cbfunc(W5500_SPI_CS_Select, W5500_SPI_CS_Deselect);
    reg_wizchip_spiburst_cbfunc(W5500_SPI_ReadBurst, W5500_SPI_WriteBurst);

    printf("\r\nW5500 VERSIONR : 0x%02X", WIZCHIP_READ(VERSIONR));

    if (ctlwizchip(CW_INIT_WIZCHIP, NULL) == -1)
    {
      printf("\r\nW5500 Initialized Fail.");
    }
    else
    {
      printf("\r\nW5500 Initialized Success.");
    }

    wiz_PhyConf PhyConf;

    PhyConf.by   = PHY_CONFBY_SW;
    PhyConf.mode   = PHY_MODE_MANUAL;
    PhyConf.speed= PHY_SPEED_100;
    PhyConf.duplex = PHY_DUPLEX_FULL;

    ctlwizchip(CW_SET_PHYCONF, &PhyConf);

    wiz_NetInfo NetInfo;

    uint8_t mac = {0x00, 0xf1, 0xbe, 0xc4, 0xa1, 0x05};
    uint8_t ip= {192, 168, 103, 136};
    uint8_t sn= {255, 255, 255, 0};
    uint8_t gw= {192, 168, 103, 1};
    uint8_t dns = {8, 8, 8, 8};

    memcpy(NetInfo.mac, mac, sizeof(mac));
    memcpy(NetInfo.ip,ip,sizeof(ip));
    memcpy(NetInfo.sn,sn,sizeof(sn));
    memcpy(NetInfo.gw,gw,sizeof(gw));
    memcpy(NetInfo.dns, dns, sizeof(dns));

    NetInfo.dhcp = NETINFO_STATIC;

    ctlnetwork(CN_SET_NETINFO, &NetInfo);

    wiz_NetInfo info;

    ctlnetwork(CN_GET_NETINFO, &info);

    printf("\r\n");
    printf("W5500 Network Infomation :\r\n");
    printf("-mac : %d:%d:%d:%d:%d:%d\r\n", info.mac, info.mac, info.mac, info.mac, info.mac, info.mac);
    printf("-ip: %d.%d.%d.%d\r\n", info.ip,info.ip,info.ip,info.ip);
    printf("-sn: %d.%d.%d.%d\r\n", info.sn,info.sn,info.sn,info.sn);
    printf("-gw: %d.%d.%d.%d\r\n", info.gw,info.gw,info.gw,info.gw);
    printf("-dns : %d.%d.%d.%d\r\n", info.dns, info.dns, info.dns, info.dns);

    if (info.dhcp == NETINFO_DHCP)
    {
      printf("-dhcp_mode : dhcp\r\n");
    }
    else
    {
      printf("-dhcp_mode : static\r\n");
    }

    printf("\r\n");

    while (1)
    {
      GPIO_WriteBit(GPIOC, GPIO_Pin_13, GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0));
    }
}
5、搭建环境
搭建硬件环境我们除了需要必备的W5500开发板之后,还需要准备一个网络交换机,以及3根网线;硬件连接示意图如下所示:

网络交换机的其中一个端口连接以太网、第二个接口连接W5500开发板,第三个接口连接PC电脑。

6、PING测试
打开串口监测软件,监控RS232输入的调试打印信息:


通过WIN+R快捷键,打开运行窗口,输入CMD进入终端,在窗口中输入ping 192.168.103.136并回车,查看PING的测试结果:


7、附件
原理图:

tpgf 发表于 2024-7-18 14:34

是否可以配置为服务器端还是客户端是由这个芯片来决定的吗

晓伍 发表于 2024-7-18 15:24

使用其他以太网芯片进行移植的原理都是一样的吧

芯领域电子科技 发表于 2024-7-18 15:29

tpgf 发表于 2024-7-18 14:34
是否可以配置为服务器端还是客户端是由这个芯片来决定的吗

W5500是全硬件TCP/IP嵌入式以太网控制器,支持TCP、UDP、IPv4、ICMP、ARP、IGMP、以及PPPoE协议,基于这些协议,可以实现各种应用程序开发;你所提到的服务器端、客户端都属于应用开发,都可以基于这些协议来实现,后面会依次来给大家分享{:handshake:}

芯领域电子科技 发表于 2024-7-18 15:37

本帖最后由 芯领域电子科技 于 2024-7-18 15:40 编辑

晓伍 发表于 2024-7-18 15:24
使用其他以太网芯片进行移植的原理都是一样的吧
是的,一个是芯片硬件层面带了TCP/IP协议,另外一个是需要自行去移植TCP/IP的协议;硬件的TCP/IP不易出错、对于开发来说可以节省TCP/IP协议所占用的代码空间;而自行移植的TCP/IP协议,需要芯片有足够的FLASH代码空间和SRAM缓存空间,但自行移植的也更加灵活;两者在成本上也有明显的对比,具体看项目方案。

观海 发表于 2024-7-18 19:33

可以在运行的过程中更改ip地址吗

八层楼 发表于 2024-7-18 20:05

单片机和w5500是什么种类的通讯方式呢

磨砂 发表于 2024-7-18 20:37

请问iolibrabrary具体是指的什么呢

木木guainv 发表于 2024-7-18 21:09

做以太网通讯的时候pcb布线需要收发数据线相同长度吗

地瓜patch 发表于 2024-7-30 18:22

八层楼 发表于 2024-7-18 20:05
单片机和w5500是什么种类的通讯方式呢

从程序里来看是spi接口

地瓜patch 发表于 2024-7-30 18:23

木木guainv 发表于 2024-7-18 21:09
做以太网通讯的时候pcb布线需要收发数据线相同长度吗

尽量走等长线,避免出现莫名的问题
页: [1]
查看完整版本: 基于MM32F3273移植W5500的ioLibrary实现PING测试