1.介绍        

以太网是一种计算机局域网技术,主要用于在相对较小的地理范围内连接多台计算机和设备,如在一个办公室、一栋建筑物或一个校园内。以太网通常由以太网交换机、网络适配器(网卡)、网线等设备组成,通过特定的网络协议实现设备之间的数据传输。

以太网是一种 有线局域网(LAN)技术,由 IEEE 802.3 标准定义,用于在本地网络(如家庭、办公室、校园)中通过有线介质(双绞线、光纤等)连接设备。

以太网的主要特点是传输速度快、可靠性高、成本相对较低,并且易于安装和管理。它主要用于实现局部范围内的设备互联和资源共享,如文件共享、打印机共享、内部通信等。

以太网的层次:

以太网主要涉及 OSI 模型 的以下两层:

(1) 物理层(Physical Layer)

  • 功能:定义硬件接口、电气特性(如电压)、物理介质(如网线、光纤)和比特流传输。

  • 传输单位:比特(bit)。

(2) 数据链路层(Data Link Layer)

  • 功能:提供相邻节点间的可靠数据传输,包括帧封装、错误检测(CRC)、MAC 寻址等。

  • 子层

    • MAC 子层:控制介质访问(如 CSMA/CD)、帧格式(源/目的 MAC 地址)。

    • LLC 子层(可选):为网络层提供统一接口(如 IEEE 802.2)。

  • 传输单位:帧(Frame)。

2.常见的网络协议

IP协议

        IP 协议是TCP/IP协议族中最为核心的协议,位于网络层。IP层接收由更低层(网络接口层例如以太网设备驱动程序)发来的数据包,并把该数据包发送到更高层——TCP或UDP层;相反,IP层也把从TCP或UDP层接收来的数据包传送到更低层。IP数据包是不可靠的

IP 协议的主要功能包括:

  • 寻址(Addressing):使用 IP 地址(如 192.168.1.1)唯一标识网络中的设备。

  • 路由(Routing):决定数据包从源主机到目标主机的传输路径。

  • 分片与重组(Fragmentation & Reassembly):如果数据包太大,IP 层可以将其分片传输,并在目标端重组。

  • 跨网络传输:在不同类型的网络(如以太网、Wi-Fi、光纤)之间传递数据。

TCP协议(传输层:很安全,但是资源消耗大。

        TCP是面向连接的通信协议,通过三次握手建立连接,通讯完成时要拆除连接,由于TCP是面向连接的所以只能用于端到端的通讯。4次挥手断开连接。因为这种“握手”机制,所以资源消耗大。

        TCP提供的是一种可靠的数据流服务,采用“带重传的肯定确认”技术来实现传输的可靠性。TCP还采用一种称为“滑动窗口”的方式进行流量控制,所谓窗口实际表示接收能力,用以限制发送方的发送速度。

IP 负责数据包的寻址和路由(送到哪),而 TCP 负责数据的可靠传输(怎么送)。

UDP协议(传输层:速度快,不安全,传输视频用

UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。

UDP通讯时不需要接收方确认,属于不可靠的传输,可能会出现丢包现象,实际应用中要求程序员编程验证,可用于视频。

UDP与TCP位于同一层,但UDP不管数据包的顺序、错误或重发。

2.W5500简介

        W5500 是韩国 WIZnet 公司推出的一款 硬件 TCP/IP 嵌入式以太网控制器,集成了 MAC(媒体访问控制)和 PHY(物理层),支持 SPI 接口 与主控芯片(如 STM32、Arduino、ESP32)通信。它专为嵌入式设备设计,能够 无需操作系统 实现稳定的网络连接,适用于 IoT、工业控制、智能家居等场景。

 3.库的移植

首先我们要移植以太网的库:

wizchip.conf.c 是 WIZnet 官方提供的 硬件 TCP/IP 芯片(如 W5500、W5100S)的底层驱动配置文件,用于适配不同 MCU 的硬件接口(如 SPI、GPIO)。以下是该文件的详细解析和配置方法:

1. 文件功能

  • 实现硬件抽象层(HAL):定义 SPI 读写、复位、中断等底层操作函数。

  • 配置芯片参数:设置 MAC 地址、IP 地址、子网掩码等网络信息。

  • 提供基础 API:供上层协议栈(如 Socket 层)调用。

1.wizchip.conf.c的重写

1.

这个相当于Freertos保护临界区作用的代码。进入临界区和退出临界区,保护代码不被打断。

2.片选信号

3.利用SPI的数据交换进行读写。

 4.并且添加一个封装注册功能的函数。

在wizchip.cnof.c上的操作完成。

2.对SPI进行配置并且ping成功。

#ifndef _SPI_H
#define _SPI_H

#include"stm32f10x.h"
#include"delay.h"

#define SPI_CS_HIGH (GPIOD->ODR|=GPIO_ODR_ODR3)
#define SPI_CS_LOW  (GPIOD->ODR&=~GPIO_ODR_ODR3)

void spi_init(void);

void spi_start(void);

void spi_stop(void);

uint8_t spi_swapdata(uint8_t data);


#endif
#include"spi.h"

void spi_init(void)
{
    //开启时钟 
    RCC->APB2ENR|=RCC_APB2ENR_IOPAEN;
    RCC->APB2ENR|=RCC_APB2ENR_IOPDEN;
    RCC->APB1ENR|=RCC_APB1ENR_SPI2EN;
    RCC->APB2ENR|=RCC_APB2ENR_IOPBEN;
    RCC->APB2ENR|=RCC_APB2ENR_IOPGEN;

    //配置GPIO PB15   复用推挽输出 MODE 11 CNF 10      PB14 浮空输入 MODE 00 CNF 01   片选PD3 通用推挽输出 mode 11 cnf 00
    GPIOB->CRH|=(GPIO_CRH_MODE15|GPIO_CRH_MODE13);
    GPIOB->CRH|=(GPIO_CRH_CNF15_1|GPIO_CRH_CNF13_1);
    GPIOB->CRH&=~(GPIO_CRH_CNF15_0|GPIO_CRH_CNF13_0);

    GPIOD->CRL|=GPIO_CRL_MODE3;
    GPIOD->CRL&=~GPIO_CRL_CNF3;

    GPIOB->CRH&=~GPIO_CRH_MODE14;
    GPIOB->CRH&=~GPIO_CRH_CNF14;
    GPIOB->CRH|=GPIO_CRH_CNF14_0;

    //配置SPI
    //配置为主模式
    SPI2->CR1|=SPI_CR1_MSTR;

    //配置软件片选
    SPI2->CR1|=SPI_CR1_SSM;
    SPI2->CR1|=SPI_CR1_SSI;

    //分频
    SPI2->CR1&=~SPI_CR1_BR;
    //模式0
    SPI2->CR1&=~SPI_CR1_CPHA;
    SPI2->CR1&=~SPI_CR1_CPOL;

    //高位先行
    SPI2->CR1&=~SPI_CR1_LSBFIRST;

    //8位数据帧格式
    SPI2->CR1&=~SPI_CR1_DFF;

    //使能
    SPI2->CR1|=SPI_CR1_SPE;

}

void spi_start(void)
{
    SPI_CS_LOW;

}

void spi_stop(void)
{
    SPI_CS_HIGH;

}

uint8_t spi_swapdata(uint8_t data)
{
    while ((SPI2->SR & SPI_SR_TXE)==0)
    {
    }
    SPI2->DR=data;

    while ((SPI2->SR & SPI_SR_RXNE)==0)
    {
    }
    
    return (uint8_t)(SPI2->DR);
    
}

3.Ethernet的配置

#include "eth.h"
#include "spi.h"
#include "USART.h"
#include "w5500.h"
static void eth_rst(void);
uint8_t ip[4]={192,168,1,152};
uint8_t MAC[6]={110,120,130,140,150,160};
uint8_t getway[4]={192,168,1,1};
uint8_t mask[4]={255,255,255,0};

void eth_init(void)
{
    spi_init();
    ethernet_callback();
    eth_rst();
    
    setSHAR(MAC);
    setSIPR(ip);
    setSUBR(mask);
    setGAR(getway);
}

static void eth_rst(void)
{
    //PG7 通用推挽输出 MODE11 CNF 00
    RCC->APB2ENR|=RCC_APB2ENR_IOPGEN;

    GPIOG->CRL|=GPIO_CRL_MODE7;
    GPIOG->CRL&=~GPIO_CRL_CNF7;

    GPIOG->ODR&=~GPIO_ODR_ODR7;
    Delay_ms(1);
    GPIOG->ODR|=GPIO_ODR_ODR7;
    Delay_ms(100);
}
  • 初始化 W5500 硬件和网络参数,为 TCP/IP 通信奠定基础。

  • 用 ping 测试 IP 连通性,再用逻辑分析仪检查 SPI 通信波形。

  • 后续通过 Socket API 实现具体的 TCP 连接和数据传输。

4.TCP通信

既然IP已经联通了,后续通过 Socket API 实现具体的 TCP 连接和数据传输。

4.1 TCP的服务端的配置

#include "TCP.h"
uint8_t self=serve;
uint16_t pport;
uint8_t key=0;
uint8_t ipp[4]={0};
void tcp_serve(void)
{
    uint8_t flag;
    uint8_t state=getSn_SR(SN);
    if(state==SOCK_CLOSED)
    {
        flag=socket(SN,Sn_MR_TCP,PORT,SF_TCP_NODELAY);
        if(flag==SN)
        {
            printf("成功连接TCP\n");
            key=0;
        }
        else
        {
            printf("连接失败\n");
        }
    }
    else if(state==SOCK_INIT)
    {
        flag=listen(SN);
        if(flag==SOCK_OK)
        printf("侦听状态\n");
    }
    else if(state==SOCK_LISTEN)
    {
        
    }
    else if(state==SOCK_ESTABLISHED)
    {
        if(key==0)
        {
            printf("正在连接\n");
            pport=getSn_PORT(SN);
            getSn_DIPR(SN,ipp);
            printf("port=%d,ip=%d.%d.%d.%d",pport,ipp[0],ipp[1],ipp[2],ipp[3]);
            key=1;
        }
    }
    else if(state==SOCK_CLOSE_WAIT)
    {
        close(SN);
        printf("关闭连接\n");
        key=0;
    }
}

void senddata(uint8_t *buffer,uint16_t len)
{
    uint8_t state=getSn_SR(SN);
    if(state==SOCK_ESTABLISHED)
    {
        send(SN,buffer,len);
    }
}

void redata(uint8_t *buffer,uint16_t *leng)
{
    uint8_t state=getSn_SR(SN);
    *leng=0;
    if(state==SOCK_ESTABLISHED)
    {
        
        if((getSn_IR(SN) & Sn_IR_RECV))    //先看看是否接收到消息
        {
            setSn_IR(SN,Sn_IR_RECV);  //Sn_IR_RECV状态位置0
            *leng=getSn_RX_RSR(SN);        //查看接收的消息个数,并且传回去
            if(*leng>0)
            {
                recv(SN,buffer,*leng);     //接收消息
            }
        }
    }
}

#ifndef __TCP_H
#define __TCP_H


#include "stm32f10x.h"
#include "USART.h"
#include "wizchip_conf.h"
#include "w5500.h"
#include "socket.h"

#define SN 0
#define client 0
#define serve 1
#define PORT 8088

void tcp_serve(void);

void senddata(uint8_t *buffer,uint16_t len);

void redata(uint8_t *buffer,uint16_t *leng);

#endif

5.UTP通信

1. TCP 的客户端 - 服务器模式(明确区分)

TCP 是面向连接的协议,通信前需要通过 “三次握手” 建立可靠连接,其客户端与服务器的角色在连接建立时就已明确:

  • 服务器:长期监听特定端口(如 Web 服务的 80 端口),等待客户端连接请求。
  • 客户端:主动发起连接请求,连接建立后才能传输数据,且通信过程中始终依赖连接状态。

2. UDP 的无连接特性(角色区分相对模糊)

UDP 是无连接协议,数据传输无需建立连接,直接通过 IP 地址和端口发送数据包,因此:

  • 表面上看:UDP 通信中双方似乎可以 “双向任意发送数据”,没有严格的 “必须先由一方发起” 的限制,导致部分人认为其不区分客户端与服务器。
  • 本质上:UDP 仍存在客户端与服务器的逻辑区分,只是角色定义更灵活,依赖应用层实现。
#include "UDP.h"
uint8_t key=0;

void udp_init(void)
{
    uint8_t flag;
    uint8_t state=getSn_SR(SN);
    if(state==SOCK_CLOSED)
    {
        flag=socket(SN,Sn_MR_UDP,8888,0);
        if(flag==SN)
        {
            printf("成功连接\n");
        }
        else
        {
            printf("连接失败\n");
        }
    }
}


void redata(uint8_t *buffer,uint16_t *leng,uint8_t *scradd,uint16_t *scrport)
{
    uint8_t state=getSn_SR(SN);
    memset(buffer,0,sizeof(uint8_t)*(*leng));
    *leng=0;
    if(state==SOCK_UDP)
    {
        if((getSn_IR(SN) & Sn_IR_RECV))    //先看看是否接收到消息
        {
            setSn_IR(SN,Sn_IR_RECV);       //Sn_IR_RECV状态位置0
            uint8_t temp=getSn_RX_RSR(SN);        //查看接收的消息个数,并且传回去
            if(temp>8)                    //UDP前8位是端口和ip
            {
                *leng=temp-8;
                recvfrom(SN,buffer,*leng,scradd,scrport);     //接收消息
            }
        }
    }
}

void senddata(uint8_t *buffer,uint16_t len,uint8_t *desip,uint16_t desport)
{
    uint8_t state=getSn_SR(SN);
    if(state==SOCK_UDP)
    {
        sendto(SN, buffer, len, desip, desport);
    }
}



Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐