上位机测试程序解析_20251225

dpdk_short.c

该程序是一个基于 UDPDK 的 UDP 数据包接收程序。

程序首先通过cpu_init(),直接将该程序的优先级设置为最高优先级99,保证当前程序会优先获得 CPU 资源,确保对 UDP 数据接收、模拟输出触发等时间敏感的操作能快速响应,避免因资源竞争导致的处理延迟。


  • 实时 FIFO 调度(SCHED_FIFO)是 Linux 系统中为实时任务设计的调度策略,采用该策略的进程一旦获得 CPU 时间,会一直运行直到主动放弃或被更高优先级的实时进程抢占。这确保了当前程序不会被系统中其他普通进程打断,减少了因抢占导致的任务切换延迟。

之后调用udpdk_api.h中函数udpdk_init(argc, argv),初始化 UDPDK 的运行环境,包括:

  1. 启动 DPDK 的环境抽象层(EAL)。
  2. 解析 DPDK 相关的命令行参数。
  3. 分配和初始化内存池(mempool)。
  4. 初始化网卡端口(port)和队列。
  5. 设置收包 / 发包的底层驱动。

实际运行该程序时,输入指令./dpdk_short -c config.ini,其中相关参数已经在config.ini文件中写入,修改其中参数,可以调整接收端mac地址和ip地址,或者控制只接收哪个mac地址的发送端数据。

该函数返回值>=0时,UDPDK环境初始化成功。之后再调用程序中编写的函数parse_app_args(argc, argv),解析命令行指令。该函数只处理-d的延时命令,根据-d后的数选择延时多少微秒。在我们的指令中没有延时需求,因此实际直接返回0即可。

之后调用函数dd_recv,通过 UDP 协议接收数据,并根据接收状态控制模拟输出(AO)设备的电压输出。

static void dd_recv(void)
{
    int sock, n;
    struct sockaddr_in servaddr, cliaddr;
    char buf[1936];

    // Create a socket
    if ((sock = udpdk_socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        fprintf(stderr, "Pong: socket creation failed\n");
        return;
    }
    // Bind it
    memset(&servaddr, 0, sizeof(servaddr));
    memset(&cliaddr, 0, sizeof(cliaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(PORT_PONG);
    if (udpdk_bind(sock, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        fprintf(stderr, "Pong: bind failed\n");
        return;
    }

    while (app_alive) {
        if(g_ao_triggered) {
            trigger_ao_0v_once();
        }
        int len = sizeof(cliaddr);
        n = udpdk_recvfrom(sock, (void *)buf, sizeof(buf), 0, (struct sockaddr *)&cliaddr, &len);
        if (n > 0) {
            trigger_ao_3v_once();
        }
    }
}

程序首先使用 udpdk_socke,创建一个 UDP 套接字,之后将套接字绑定到指定端口 PORT_PONG(8010)和所有网络接口(INADDR_ANY),用于监听来自该端口的 UDP 数据。再将程序丢进app_alive控制的循环中,当程序需要终止时跳出循环;循环中,通过 udpdk_recvfrom 阻塞等待接收 UDP 数据,接收到的数据将会写入共享内存dd.shm.5里面,共享内存的数据会由dd_count.c程序解析

循环终止后,调用udpdk_interrupt(0)和udpdk_cleanup()函数关闭释放udpdk。

另外,程序中还有函数trigger_ao_3v_once、trigger_ao_0v_once、ao_cleanup,这些函数通过调用comedi_data_write函数,改变PCI板卡输出通道的电平。

trigger_ao_3v_once、trigger_ao_0v_once,分别将输出通道的输出电平调整为3V/0V,用于根据设定规则输出高低电平;ao_cleanup则将所有通道输出变为低电平后,还关闭comedi停止输出,在程序终止时释放输出通道。

dd_count.c

该程序是基于共享内存的数据消费者程序,用于从共享内存中获取blob数据。

程序首先通过parse_opts解析命令行。执行程序时输入指令./dd_count -c config.yml会需要调用read_config_from_file,读取config.yml文件内的信息,该文件内存储了共享内存的标识 ID(job_id),启用的通道数量(channels_enabled),每个数据块的大小(blob_size)等内容。

之后,程序通过dd_init(MODE_CONSUMER, 0, job_id),轮询等待生产者创建共享内存,当未找到对应id号的共享内存时,输出未找到共享内存信息,并在1秒延时后继续循环。

找到共享内存后,pthread_create(&threads[0], NULL, cons_proc, NULL)创建消费者进程,启动消费线程cons_proc。该消费者程序,在共享内存里面存在blob时,通过dd_get_blob_pop读取blob,并通过计数器packet_count对blob计数,之后输出该blob相关信息,例如blob id,blob里面包含的采样数据数量,总共收到的包的数量。

调用该程序时,需要dpdk_short.c程序先接收到udp包,并将udp包内容写入到共享内存dd.shm.5里面,之后该程序检测到共享内存,读取共享内存里面数据,并输出共享内存里面相关的信息。通过观察输出的blob总共有多少个,即可判断出来上一次udp发送里面,上位机接收到了多少udp包,根据该数据即可计算得出udp发送的丢包率。


本文章使用limfx的vscode插件快速发布