发新帖本帖赏金 80.00元(功能说明)我要提问
返回列表
打印
[MM32生态]

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

[复制链接]
1296|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 芯领域电子科技 于 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_ClocksTypeDef  RCC_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\nPCLK1  Frequency : %7.3f MHz", (double)RCC_Clocks.PCLK1_Frequency  / (double)1000000.0);
    printf("\r\nPCLK2  Frequency : %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_InitTypeDef  SPI_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[i] = 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[i]);

        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[6] = {0x00, 0xf1, 0xbe, 0xc4, 0xa1, 0x05};
    uint8_t ip[4]  = {192, 168, 103, 136};
    uint8_t sn[4]  = {255, 255, 255, 0};
    uint8_t gw[4]  = {192, 168, 103, 1};
    uint8_t dns[4] = {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[0], info.mac[1], info.mac[2], info.mac[3], info.mac[4], info.mac[5]);
    printf("  -ip  : %d.%d.%d.%d\r\n", info.ip[0],  info.ip[1],  info.ip[2],  info.ip[3]);
    printf("  -sn  : %d.%d.%d.%d\r\n", info.sn[0],  info.sn[1],  info.sn[2],  info.sn[3]);
    printf("  -gw  : %d.%d.%d.%d\r\n", info.gw[0],  info.gw[1],  info.gw[2],  info.gw[3]);
    printf("  -dns : %d.%d.%d.%d\r\n", info.dns[0], info.dns[1], info.dns[2], info.dns[3]);

    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、附件
原理图: W5500开发板.PDF (374.67 KB)

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 80.00 元 2024-07-30
理由:恭喜通过原创审核!期待您更多的原创作品~

21小跑堂 打赏了 80.00 元 2024-07-30
理由:恭喜通过原创审核!期待您更多的原创作品~

评论
21小跑堂 2024-7-30 10:36 回复TA
基于MM32F3273单片机,使用W5500以太网控制器,实现ping测试案例,硬件和软件资料详细,实现效果较佳。 
21小跑堂 2024-7-29 17:15 回复TA
hi 大佬 以后申请原创@21小跑堂 哦~ 
沙发
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协议,基于这些协议,可以实现各种应用程序开发;你所提到的服务器端、客户端都属于应用开发,都可以基于这些协议来实现,后面会依次来给大家分享

使用特权

评论回复
5
芯领域电子科技|  楼主 | 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缓存空间,但自行移植的也更加灵活;两者在成本上也有明显的对比,具体看项目方案。

使用特权

评论回复
6
观海| | 2024-7-18 19:33 | 只看该作者
可以在运行的过程中更改ip地址吗

使用特权

评论回复
7
八层楼| | 2024-7-18 20:05 | 只看该作者
单片机和w5500是什么种类的通讯方式呢

使用特权

评论回复
8
磨砂| | 2024-7-18 20:37 | 只看该作者
请问iolibrabrary具体是指的什么呢

使用特权

评论回复
9
木木guainv| | 2024-7-18 21:09 | 只看该作者
做以太网通讯的时候pcb布线需要收发数据线相同长度吗

使用特权

评论回复
10
地瓜patch| | 2024-7-30 18:22 | 只看该作者
八层楼 发表于 2024-7-18 20:05
单片机和w5500是什么种类的通讯方式呢

从程序里来看是spi接口

使用特权

评论回复
11
地瓜patch| | 2024-7-30 18:23 | 只看该作者
木木guainv 发表于 2024-7-18 21:09
做以太网通讯的时候pcb布线需要收发数据线相同长度吗

尽量走等长线,避免出现莫名的问题

使用特权

评论回复
发新帖 本帖赏金 80.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

8

帖子

0

粉丝