zynq的网口和串口透传

参考

pysoem

zynq以太网透传到串口

为了方便学习以太网的帧格式

有很多好用的串口助手, 但很难找到一个好用的以太网调试助手

所以有必要做这个工具

网口数据太多了, 用 1 对 1 的 方式接线

目标

向zynq板的串口发送1个字节流(超时封包),

zynq板通过网口发出( )

zynq板从网口接收一个字节流,从它的串口输出

可过滤非ecat的帧

例子

json 复制代码
{
  "remark": "ecat_translate.j2b.json",
  "读取AL状态寄存器0x0130": {
      "Call": "FF FF FF FF FF FF 02 11 22 33 44 55 88 A4 0E 10 01 00 00 00 30 01 02 00 00 00 00 00 00 00",
      "Reply": "FF FF FF FF FF FF 02 11 22 33 44 55 88 A4 0E 10 01 00 01 00 30 01 02 00 00 00 01 00 01 00"
  },
  "写LED寄存器_点亮LED1": {
      "Call": "FF FF FF FF FF FF 02 11 22 33 44 55 88 A4 0E 10 01 00 00 00 00 10 02 00 01 00 00 00 00",
      "Reply": "FF FF FF FF FF FF 02 11 22 33 44 55 88 A4 0E 10 01 00 01 00 00 10 02 00 00 00 00 00 00 00"
  },
  "读取AL事件屏蔽寄存器0x0204": {
    "Call": "FF FF FF FF FF FF 02 11 22 33 44 55 88 A4 0E 10 01 00 00 00 04 02 04 00 00 00 00 00 00 00",
    "Reply": "FF FF FF FF FF FF 02 11 22 33 44 55 88 A4 0E 10 01 00 01 00 04 02 04 00 XX XX XX XX 01 00"
  }
}

连接

bash 复制代码
/dev/ttyPS1 和  eth1 相互透传
eth1  直连 ecat LAN9252
eth0  连交换机联网
/dev/ttyPS0 串口终端

eth0 配置

basjh 复制代码
# 清空 eth0 上的所有 IPv4/IPv6 地址
ip addr flush dev eth0
# 清空默认路由(有多个默认路由就多执行几次,或用 replace 见下)
ip route del default 2>/dev/null
# 重新配置
ip addr add 192.168.3.50/24 dev eth0
ip link set eth0 up
# 默认路由建议用 replace(可重复执行)
ip route replace default via 192.168.3.1 dev eth0

eth1配置

bash 复制代码
ip link set eth1 up
ip link set eth1 promisc on

ttyPS1测试

bash 复制代码
stty -F /dev/ttyPS1 115200 raw -echo
cat /dev/ttyPS1

源码

main.cpp

c 复制代码
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

#include "utils/raw_socket.h"
#include "utils/serial_port.h"

#include <chrono>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/if_packet.h>

struct BridgeOptions
{
    std::string ifname = "eth1";
    std::string serial = "/dev/ttyPS1";
    int baud = 115200;

    bool ethercatOnly = true;      // filter ethertype 0x88A4
    bool padTo60 = true;           // pad short frames (without FCS) to 60 bytes
    bool ignoreOutgoing = true;    // avoid serial echo from our own TX
    bool trimEthercatPadding = true;
    bool debug = false;
    int maxFrame = 2048;
    int serialTimeoutMs = 100;     // serial idle timeout means end of one ethernet frame
};

static void PrintHelp(const char* argv0)
{
    std::cout
        << "zynq eth<->serial bridge (for EtherCAT learning)\n\n"
        << "Usage:\n"
        << "  " << argv0 << " [--if eth1] [--serial /dev/ttyPS1] [--baud 115200]\n"
        << "            [--all] [--no-pad] [--no-trim] [--no-ignore-outgoing] [--max 2048]\n"
        << "            [--timeout-ms 100] [--debug]\n\n"
        << "Serial framing:\n"
        << "  Send raw ethernet frame bytes directly. 100ms serial idle means frame end.\n\n"
        << "Defaults:\n"
        << "  - Only forwards EtherCAT (ethertype 0x88A4). Use --all to forward all Ethernet frames.\n"
        << "  - Pads short TX frames to 60 bytes (without FCS).\n"
        << "  - Needs root privileges on Linux (raw socket).\n";
}

static bool ParseArgs(int argc, char** argv, BridgeOptions& opt)
{
    for (int i = 1; i < argc; ++i)
    {
        std::string a = argv[i];
        if (a == "--help" || a == "-h")
        {
            PrintHelp(argv[0]);
            return false;
        }
        if (a == "--if" && i + 1 < argc)
        {
            opt.ifname = argv[++i];
            continue;
        }
        if (a == "--serial" && i + 1 < argc)
        {
            opt.serial = argv[++i];
            continue;
        }
        if (a == "--baud" && i + 1 < argc)
        {
            opt.baud = std::stoi(argv[++i]);
            continue;
        }
        if (a == "--all")
        {
            opt.ethercatOnly = false;
            continue;
        }
        if (a == "--no-pad")
        {
            opt.padTo60 = false;
            continue;
        }
        if (a == "--no-trim")
        {
            opt.trimEthercatPadding = false;
            continue;
        }
        if (a == "--no-ignore-outgoing")
        {
            opt.ignoreOutgoing = false;
            continue;
        }
        if (a == "--debug")
        {
            opt.debug = true;
            continue;
        }
        if (a == "--max" && i + 1 < argc)
        {
            opt.maxFrame = std::stoi(argv[++i]);
            continue;
        }
        if (a == "--timeout-ms" && i + 1 < argc)
        {
            opt.serialTimeoutMs = std::stoi(argv[++i]);
            continue;
        }

        std::cout << "Unknown arg: " << a << "\n";
        PrintHelp(argv[0]);
        return false;
    }
    return true;
}

static volatile sig_atomic_t g_stop = 0;
static void OnSignal(int) { g_stop = 1; }

static uint16_t GetEtherType(const uint8_t* frame, int len)
{
    if (len < 14)
        return 0;
    return ((uint16_t)frame[12] << 8) | (uint16_t)frame[13];
}

static uint16_t GetEtherType(const std::vector<uint8_t>& frame)
{
    if (frame.size() < 14)
        return 0;
    return ((uint16_t)frame[12] << 8) | (uint16_t)frame[13];
}

static void PrintHexPrefix(const uint8_t* data, int len, int maxLen = 64)
{
    int n = len < maxLen ? len : maxLen;
    for (int i = 0; i < n; ++i)
    {
        std::cout << std::hex << std::uppercase << std::setw(2) << std::setfill('0')
                  << (int)data[i] << " ";
    }
    if (len > maxLen)
        std::cout << "...";
    std::cout << std::dec << std::nouppercase << std::setfill(' ') << "\n";
}

static int GetEthercatPayloadLength(const uint8_t* frame, int len)
{
    if (len < 16 || GetEtherType(frame, len) != 0x88A4)
        return -1;

    uint16_t header = (uint16_t)frame[14] | ((uint16_t)frame[15] << 8);
    return header & 0x07FF;
}

static int GetEthernetUsefulLength(const uint8_t* frame, int len, const BridgeOptions& opt)
{
    if (!opt.trimEthercatPadding)
        return len;

    int ethercatPayloadLen = GetEthercatPayloadLength(frame, len);
    if (ethercatPayloadLen < 0)
        return len;

    int usefulLen = 14 + 2 + ethercatPayloadLen;
    if (usefulLen < 16 || usefulLen > len)
        return len;

    return usefulLen;
}

static bool IsAllowedEtherType(const std::vector<uint8_t>& frame, bool ethercatOnly)
{
    if (frame.size() < 14)
        return false;

    if (!ethercatOnly)
        return true;

    return GetEtherType(frame) == 0x88A4;
}

static bool IsAllowedEtherType(const uint8_t* frame, int len, bool ethercatOnly)
{
    if (len < 14)
        return false;

    if (!ethercatOnly)
        return true;

    return GetEtherType(frame, len) == 0x88A4;
}

static void SendSerialFrameToEth(RawSocket& eth, std::vector<uint8_t>& frame, const BridgeOptions& opt)
{
    if (frame.empty())
        return;

    if (!IsAllowedEtherType(frame, opt.ethercatOnly))
    {
        if (opt.debug)
        {
            std::cout << "SER->ETH drop len=" << frame.size();
            if (frame.size() >= 14)
                std::cout << " ethertype=0x" << std::hex << std::uppercase << GetEtherType(frame) << std::dec << std::nouppercase;
            std::cout << "\n";
        }
        frame.clear();
        return;
    }

    int originalLen = (int)frame.size();
    if (opt.padTo60 && frame.size() < 60)
        frame.resize(60, 0x00);

    bool ok = eth.Send(frame);
    if (opt.debug)
    {
        std::cout << "SER->ETH " << (ok ? "send" : "send_failed")
                  << " len=" << originalLen
                  << " tx_len=" << frame.size()
                  << " ethertype=0x" << std::hex << std::uppercase << GetEtherType(frame)
                  << std::dec << std::nouppercase << "\n";
        PrintHexPrefix(frame.data(), (int)frame.size());
    }
    frame.clear();
}

int main(int argc, char** argv)
{
    BridgeOptions opt;
    if (!ParseArgs(argc, argv, opt))
        return 0;

    signal(SIGINT, OnSignal);
    signal(SIGTERM, OnSignal);

    RawSocket eth;
    if (!eth.Open(opt.ifname))
    {
        std::cout << "Open ethernet failed: " << opt.ifname << "\n";
        return 1;
    }

    if (opt.ignoreOutgoing)
    {
        int one = 1;
        setsockopt(eth.GetFd(), SOL_PACKET, PACKET_IGNORE_OUTGOING, &one, sizeof(one));
    }

    SerialPort ser;
    if (!ser.Open(opt.serial, opt.baud))
    {
        std::cout << "Open serial failed: " << opt.serial << " baud=" << opt.baud << "\n";
        return 1;
    }

    std::cout
        << "Bridge running:\n"
        << "  serial: " << opt.serial << " @" << opt.baud << "\n"
        << "  eth   : " << opt.ifname << (opt.ethercatOnly ? " (EtherCAT only)" : " (ALL)") << "\n"
        << "  frame : raw ethernet bytes, " << opt.serialTimeoutMs << "ms idle timeout\n"
        << "  max   : " << opt.maxFrame << " bytes, pad_to_60=" << (opt.padTo60 ? "yes" : "no")
        << ", trim_ecat_padding=" << (opt.trimEthercatPadding ? "yes" : "no") << "\n"
        << "  debug : " << (opt.debug ? "on" : "off") << "\n";

    std::vector<uint8_t> serialFrame;
    uint8_t serialIn[4096];
    uint8_t ethIn[4096];
    auto lastSerialRx = std::chrono::steady_clock::now();

    while (!g_stop)
    {
        int pollTimeoutMs = 200;
        if (!serialFrame.empty())
        {
            auto now = std::chrono::steady_clock::now();
            int idleMs = (int)std::chrono::duration_cast<std::chrono::milliseconds>(now - lastSerialRx).count();
            pollTimeoutMs = idleMs >= opt.serialTimeoutMs ? 0 : opt.serialTimeoutMs - idleMs;
        }

        pollfd fds[2];
        memset(fds, 0, sizeof(fds));
        fds[0].fd = ser.GetFd();
        fds[0].events = POLLIN;
        fds[1].fd = eth.GetFd();
        fds[1].events = POLLIN;

        int pr = poll(fds, 2, pollTimeoutMs);
        if (pr < 0)
        {
            if (errno == EINTR)
                continue;
            std::cout << "poll error: " << strerror(errno) << "\n";
            break;
        }

        // serial -> eth
        if (fds[0].revents & POLLIN)
        {
            int n = ser.Read(serialIn, (int)sizeof(serialIn));
            if (n > 0)
            {
                serialFrame.insert(serialFrame.end(), serialIn, serialIn + n);
                lastSerialRx = std::chrono::steady_clock::now();
                if (opt.debug)
                    std::cout << "SER rx chunk len=" << n << " buffered=" << serialFrame.size() << "\n";

                if ((int)serialFrame.size() > opt.maxFrame)
                {
                    std::cout << "serial frame too large, dropped: " << serialFrame.size() << "\n";
                    serialFrame.clear();
                }
            }
        }

        if (!serialFrame.empty())
        {
            auto now = std::chrono::steady_clock::now();
            int idleMs = (int)std::chrono::duration_cast<std::chrono::milliseconds>(now - lastSerialRx).count();
            if (idleMs >= opt.serialTimeoutMs)
                SendSerialFrameToEth(eth, serialFrame, opt);
        }

        // eth -> serial
        if (fds[1].revents & POLLIN)
        {
            int len = eth.Receive(ethIn, (int)sizeof(ethIn));
            if (len > 0)
            {
                if (!IsAllowedEtherType(ethIn, len, opt.ethercatOnly))
                {
                    if (opt.debug)
                    {
                        std::cout << "ETH->SER drop len=" << len;
                        if (len >= 14)
                            std::cout << " ethertype=0x" << std::hex << std::uppercase << GetEtherType(ethIn, len) << std::dec << std::nouppercase;
                        std::cout << "\n";
                    }
                    continue;
                }

                int usefulLen = GetEthernetUsefulLength(ethIn, len, opt);
                ser.WriteAll(ethIn, usefulLen);
                if (opt.debug)
                {
                    std::cout << "ETH->SER recv len=" << len
                              << " serial_len=" << usefulLen
                              << " ethertype=0x" << std::hex << std::uppercase << GetEtherType(ethIn, len)
                              << std::dec << std::nouppercase << "\n";
                    PrintHexPrefix(ethIn, usefulLen);
                }
            }
        }
    }

    std::cout << "Bridge stopped.\n";
    return 0;
}

utils/raw_socket.h

c 复制代码
#ifndef RAW_SOCKET_H
#define RAW_SOCKET_H

#include <stdint.h>
#include <string>
#include <vector>

class RawSocket
{
public:
    RawSocket();
    ~RawSocket();
    bool Open(const std::string& ifname);
    bool Send(const std::vector<uint8_t>& frame);
    int  Receive(uint8_t* buffer,int maxLen);
    void Close();
    int GetFd() const { return m_fd; }
private:
    //套接字句柄
    int m_fd;
};

#endif

utils/raw_socket.cpp

c 复制代码
#include "raw_socket.h"
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <netinet/ether.h>


RawSocket::RawSocket()
{
    m_fd = -1;
}

RawSocket::~RawSocket()
{
    if (m_fd >= 0)
    {
        close(m_fd);
    }
}

bool RawSocket::Open(
        const std::string& ifname)
{
    m_fd =
            socket(
                    AF_PACKET,
                    SOCK_RAW,
                    htons(ETH_P_ALL));

    if (m_fd < 0)
    {
        return false;
    }

    struct ifreq ifr;

    memset(&ifr,0,sizeof(ifr));

    strncpy(
            ifr.ifr_name,
            ifname.c_str(),
            IFNAMSIZ - 1);

    if (ioctl(
            m_fd,
            SIOCGIFINDEX,
            &ifr) < 0)
    {
        return false;
    }

    sockaddr_ll addr;

    memset(&addr,0,sizeof(addr));

    addr.sll_family = AF_PACKET;
    addr.sll_protocol = htons(ETH_P_ALL);
    addr.sll_ifindex = ifr.ifr_ifindex;

    if (bind(
            m_fd,
            (sockaddr*)&addr,
            sizeof(addr)) < 0)
    {
        return false;
    }

    return true;
}

bool RawSocket::Send(
        const std::vector<uint8_t>& frame)
{
    return send(
            m_fd,
            frame.data(),
            frame.size(),
            0) > 0;
}

int RawSocket::Receive(
        uint8_t* buffer,
        int maxLen)
{
    return recv(
            m_fd,
            buffer,
            maxLen,
            0);
}


void RawSocket::Close()
{
    if (m_fd >= 0)
    {
        close(m_fd);// 关闭socket
        m_fd = -1;     //防止重复close
    }
}

utils/serial_port.h

c 复制代码
#ifndef SERIAL_PORT_H
#define SERIAL_PORT_H

#include <stdint.h>
#include <string>
#include <vector>

class SerialPort
{
public:
    SerialPort();
    ~SerialPort();

    bool Open(const std::string& path, int baud);
    void Close();

    int GetFd() const { return m_fd; }
    bool IsOpen() const { return m_fd >= 0; }

    // Non-blocking read; returns bytes read, or -1 on EAGAIN/error.
    int Read(uint8_t* buffer, int maxLen);

    // Write all bytes (busy-waits on EAGAIN).
    bool WriteAll(const uint8_t* data, int len);
    bool WriteAll(const std::vector<uint8_t>& data);

private:
    int m_fd;
};

#endif

utils/serial_port.cpp

c 复制代码
#include "serial_port.h"

#include <string.h>

#include <errno.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

static speed_t BaudToSpeed(int baud)
{
    switch (baud)
    {
    case 9600: return B9600;
    case 19200: return B19200;
    case 38400: return B38400;
    case 57600: return B57600;
    case 115200: return B115200;
    case 230400: return B230400;
    case 460800: return B460800;
    case 921600: return B921600;
    default: return 0;
    }
}

SerialPort::SerialPort() : m_fd(-1) {}

SerialPort::~SerialPort()
{
    Close();
}

bool SerialPort::Open(const std::string& path, int baud)
{
    Close();

    speed_t speed = BaudToSpeed(baud);
    if (speed == 0)
        return false;

    m_fd = open(path.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (m_fd < 0)
        return false;

    termios tio;
    if (tcgetattr(m_fd, &tio) != 0)
    {
        Close();
        return false;
    }

    cfmakeraw(&tio);

    tio.c_cflag &= ~PARENB;
    tio.c_cflag &= ~CSTOPB;
    tio.c_cflag &= ~CSIZE;
    tio.c_cflag |= CS8;

    tio.c_cflag &= ~CRTSCTS;
    tio.c_iflag &= ~(IXON | IXOFF | IXANY);

    tio.c_cflag |= (CLOCAL | CREAD);

    tio.c_cc[VMIN] = 0;
    tio.c_cc[VTIME] = 0;

    if (cfsetispeed(&tio, speed) != 0 || cfsetospeed(&tio, speed) != 0)
    {
        Close();
        return false;
    }

    if (tcsetattr(m_fd, TCSANOW, &tio) != 0)
    {
        Close();
        return false;
    }

    tcflush(m_fd, TCIOFLUSH);
    return true;
}

void SerialPort::Close()
{
    if (m_fd >= 0)
    {
        close(m_fd);
        m_fd = -1;
    }
}

int SerialPort::Read(uint8_t* buffer, int maxLen)
{
    if (m_fd < 0)
        return -1;

    int n = (int)read(m_fd, buffer, (size_t)maxLen);
    if (n < 0)
    {
        if (errno == EAGAIN || errno == EWOULDBLOCK)
            return -1;
        return -1;
    }
    return n;
}

bool SerialPort::WriteAll(const uint8_t* data, int len)
{
    if (m_fd < 0)
        return false;

    int written = 0;
    while (written < len)
    {
        int n = (int)write(m_fd, data + written, (size_t)(len - written));
        if (n < 0)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                continue;
            return false;
        }
        if (n == 0)
            continue;
        written += n;
    }
    return true;
}

bool SerialPort::WriteAll(const std::vector<uint8_t>& data)
{
    if (data.empty())
        return true;
    return WriteAll(data.data(), (int)data.size());
}

启动

网口 eth1 和 串口 /dev/ttyPS1 相互透传

bash 复制代码
./ming_ethercat_petalinux --if eth1 --serial /dev/ttyPS1 --baud 115200 --debug
相关推荐
神奇的小猴程序员1 小时前
学习查理・芒格思维模型,整理自用资料查阅渠道
学习
xian_wwq1 小时前
【学习笔记】提示词注入完全指南:五种变体,一套防御体系
笔记·学习·ai安全
做cv的小昊1 小时前
计算机图形学:【Games101】学习笔记06——几何(曲线和曲面、网格处理)、阴影图
c++·笔记·学习·游戏·图形渲染·几何学·光照贴图
AOwhisky2 小时前
MySQL 学习笔记(第二期):SQL 语言之库表操作与数据类型
linux·运维·数据库·笔记·sql·学习·mysql
段一凡-华北理工大学2 小时前
工业领域的Hadoop架构学习~系列文章11:Kerberos安全认证
数据仓库·hadoop·学习·架构·高炉炼铁·工业智能体·高炉炼铁智能化
一锅炖出任易仙2 小时前
创梦汤锅学习日记day23
学习·ai·ue5
花落yu2 小时前
AI学习:第2天
人工智能·python·学习
蒟蒻的贤2 小时前
为什么加入 ReLU 后,神经网络可以学习线性可分的特征?
人工智能·神经网络·学习