知识:

1. EtherCAT的运行需要硬件支持;

2. 上位机作为主机通过网卡直接向各个从机(板子)发送命令,从机中有芯片处理命令转发给各驱动。

一、配置 Npcap

1. 下载Npcap(不要用WinPcap太老了已经不维护了):https://npcap.com/#download 

2. 配置lib和include目录:

2.1 在visual studio中新建一个控制台程序PcapTest(也可以是其他名字)。

2.2 将压缩包中的lib和include拷贝出来放在控制台程序下:

2.3 配置附加目录、库目录文件(不懂的话问问AI):

↑注意,这里配置的是lib/x64(如果你是x86或者是其他arm之类的里面也有不同的文件类型)。

这四个:wpcap.lib;Packet.lib;ws2_32.lib;winmm.lib;

2.4 在main函数中运行这段代码:

#include <iostream>
#include <pcap.h>
#pragma comment(lib, "wpcap.lib")
#pragma comment(lib, "Packet.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "winmm.lib")

int main()
{
    /*std::cout << "Hello World!\n";*/

    pcap_if_t* alldevs;
    char errbuf[PCAP_ERRBUF_SIZE];
    if (pcap_findalldevs(&alldevs, errbuf) == 0)
        printf("Found network devices!\n");
    pcap_freealldevs(alldevs);
    return 0;
}

可得结果:

至此 Npcap配置结束。

参考文章:https://fanyfull.github.io/2022/05/28/Visual-Studio-2022-%E9%85%8D%E7%BD%AE-winpcap-%E7%8E%AF%E5%A2%83/

二、配置SOEM.

1. 下载SOEM:https://github.com/OpenEtherCATsociety/SOEM

2. 运行编译SOEM库:

阅读此文章并配置:https://wxflamy.github.io/2019/09/25/EtherCAT-SOEM/

↑这个文章比较老了,但是运行配置之后在install中出现的是sample_ng.exe,使用这个运行即可。(如果找不到install问问AI。

visual studio 2022中在最上方:生成-build install。

运行代码一闪而过,善用截图找到你的device:

3.在生成文件夹里找到sample_ng.exe,cmd打开后输入你的device地址:

4. 然后找到你的生成文件夹里的lib和include库,参照npcap同样在项目中新建文件引入:

5. main函数:

#include <iostream>
#include <iomanip>
#include <cstring>
#include "soem/soem.h"
#include <bitset>

// 全局 I/O 映射缓冲区
char IOmap[4096];

// 辅助:打印输入输出二进制
void print_bits(uint16_t val, int width = 8)
{
    for (int i = width - 1; i >= 0; --i)
        std::cout << ((val >> i) & 1);
}

// 主函数
int main()
{
    const char* ifname = "\\Device\\NPF_{7E6E850D-B177-4719-A409-3FF991DC1577}";
    std::cout << "Initializing EtherCAT on " << ifname << "...\n";

    // 创建上下文并清零
    ecx_contextt context;
    memset(&context, 0, sizeof(context));
    memset(IOmap, 0, sizeof(IOmap));

    // 初始化主站
    if (!ecx_init(&context, ifname))
    {
        std::cerr << "Failed to initialize SOEM on " << ifname << "\n";
        return -1;
    }

    // 扫描从站
    int slave_count = ecx_config_init(&context);
    if (slave_count <= 0)
    {
        std::cerr << "No slaves found!\n";
        ecx_close(&context);
        return -1;
    }

    std::cout << "there are " << slave_count << " slaves found.\n";
    std::cout << "Slave[1] name: " << context.slavelist[1].name << "\n";

    // 映射 PDO & DC
    ecx_config_map_group(&context, IOmap, 0); //在内存中分配一块地址给context用来更新input和output
    ecx_configdc(&context);

    std::cout << "Ibytes=" << context.slavelist[1].Ibytes
        << ", Obytes=" << context.slavelist[1].Obytes << "\n";

    // 进入 OP 状态
    context.slavelist[0].state = EC_STATE_OPERATIONAL;
    ecx_writestate(&context, 0);

    for (int i = 0; i < 50; ++i)
    {
        ecx_send_processdata(&context);
        ecx_receive_processdata(&context, EC_TIMEOUTRET);
        ecx_statecheck(&context, 0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE / 10);

        if (context.slavelist[0].state == EC_STATE_OPERATIONAL)
        {
            std::cout << "All slaves are now OPERATIONAL.\n";
            break;
        }
        osal_usleep(10000);
    }

    // 进入 PDO 循环
    std::cout << "Starting PDO loop...\n";


    //uint8_t* in = context.slavelist[1].inputs; //指向内存地址中的context,之后会动态更新
    //uint8_t* out = context.slavelist[1].outputs;

    // —— 主循环:读取输入、控制输出 ——
    for (int cycle = 0; cycle < 1000; ++cycle)
    {
        // 所有从站更新输出缓冲区(准备发出去的数据)
        for (int i = 1; i <= context.slavecount; ++i)
        {
            uint8_t* out = context.slavelist[i].outputs;
            if (!out) continue;

            // 控制输出-这里是输出逻辑,换成自己的,不懂就问AI
            //if ((cycle / 50) % 2 == 0)
                //out[0] = 0x01;   // LED1亮
            //else
                //out[0] = 0x02;   // LED2亮
        }

        // 发出帧(所有从站同步更新输出)
        ecx_send_processdata(&context);

        // 接收帧(更新所有从站输入缓冲区)
        int wkc = ecx_receive_processdata(&context, EC_TIMEOUTRET);

        // 现在 inputs 才是最新的,可以安全打印
        std::cout << "Cycle " << std::setw(4) << cycle << " | WKC=" << wkc << "\n";
        for (int i = 1; i <= context.slavecount; ++i)
        {
            uint8_t* in = context.slavelist[i].inputs;
            if (!in) continue;

            uint8_t inputs = in[0];
            std::cout << "  Slave " << i << " inputs: ";
            print_bits(inputs, 8);
            std::cout << "\n";
        }

        osal_usleep(5000); // 5ms周期
    }

    // 停止主站
    context.slavelist[0].state = EC_STATE_INIT;
    ecx_writestate(&context, 0);
    ecx_close(&context);

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

结束。

更多推荐