Российский производитель и разработчик сертифицированного измерительного оборудования с 1987 года


Задержка при получении измерений АЦП

Вы не вошли.

 Поиск | Регистрация | Вход 

11.07.2025 15:39:28
#1

Участник
Здесь с 11.07.2025
Сообщений: 2

Задержка при получении измерений АЦП

Приветствую!

Разрабатываю модуль на C для получения измерений с аналоговых входов L-502. Столкнулся со следующей проблемой: метод X502_Recv всегда выполняется около 4х секунд, вне зависимости от частоты АЦП и количества запрашиваемых отсчетов. Если передать таймаут меньше 4х секунд, то стабильно ничего не возвращается.
В целях дебага написал простой код, который с интервалом в 1 секунду вызывает X502_GetRecvReadyCount, и также получил весьма странные результаты.

Код:

#include <locale.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>

#include <x502api.h>
#include <l502api.h>


uint32_t calc_delay(struct timeval *start, struct timeval *stop) {
    return (stop->tv_sec - start->tv_sec) * 1000000 + stop->tv_usec - start->tv_usec;
}


int main(int argc, char** argv) {
    setlocale(LC_NUMERIC, "");
    struct timeval start, stop;
    uint64_t delta_us;
    int32_t err;

    // Получаем версию библиотеки
    uint32_t ver = X502_GetLibraryVersion();
    printf("Library version: %d.%d.%d\n", (ver >> 24)&0xFF, (ver>>16)&0xFF, (ver>>8)&0xFF);

    // Получаем количество подключенных устройств по интерфейсу PCI
    uint32_t devcnt = 0;
    L502_GetDevRecordsList(NULL, 0, 0, &devcnt);
    printf("Found %d PCI device(s)\n", devcnt);

    // Выводим список найденных устройств
    if (devcnt == 0) {
        return 0;
    }

    // Выделяем память для массива для сохранения найденного количества записей
    t_x502_devrec *devices = NULL;
    devices = malloc(devcnt * sizeof(t_x502_devrec));

    // Получаем записи о модулях L502
    L502_GetDevRecordsList(&devices[0], devcnt, 0, NULL);
    for (int i=0; i<devcnt; ++i) {
        printf("Device %d: %s (SN: %s, Flags: %x)\n", i, devices[i].devname, devices[i].serial, devices[i].flags);
    }

    // Устанавливаем соединение
    t_x502_hnd dev_hnd = X502_Create();
    gettimeofday(&start, NULL);
    err = X502_OpenByDevRecord(dev_hnd, &devices[0]);
    gettimeofday(&stop, NULL);
    printf("X502_OpenByDevRecord took %'d us\n", calc_delay(&start, &stop));

    if (err != X502_ERR_OK) {
        fprintf(stderr, "Error connecting to device: %s\n", X502_GetErrorString(err));
        return 1;
    }

    printf("Successfully connected to device!\n");

    // Освобождение ресурсов действительных записей из списка
    X502_FreeDevRecordList(devices, devcnt);

    // Очистка памяти самого массива
    free(devices);

    // Устанавливаем параметры логической таблицы АЦП
    err = X502_SetLChannelCount(dev_hnd, 1);
    if (err != X502_ERR_OK) {
        fprintf(stderr, "Error configuring ADC inputs: %s\n", X502_GetErrorString(err));
        return 1;
    }

    err = X502_SetLChannel(dev_hnd, 0, 0, X502_LCH_MODE_COMM, X502_ADC_RANGE_10, 0);
    if (err != X502_ERR_OK) {
        fprintf(stderr, "Error configuring ADC inputs: %s\n", X502_GetErrorString(err));
        return 1;
    }

    err = X502_SetMode(dev_hnd, X502_MODE_FPGA);
    if (err != X502_ERR_OK) {
        fprintf(stderr, "Error setting mode: %s\n", X502_GetErrorString(err));
        return 1;
    }

    // Устанавливаем частоту АЦП
    double f_adc = 2000.0;
    err = X502_SetAdcFreq(dev_hnd, &f_adc, NULL);
    if (err != X502_ERR_OK) {
        fprintf(stderr, "Error setting ADC frequency: %s\n", X502_GetErrorString(err));
        return 1;
    }

    // Записываем настройки
    err = X502_Configure(dev_hnd, 0);
    if (err != X502_ERR_OK) {
        fprintf(stderr, "Error configuring device: %s\n", X502_GetErrorString(err));
        return 1;
    }

    // Разрешаем синхронные потоки
    int streams = X502_STREAM_ADC;
    err = X502_StreamsEnable(dev_hnd, streams);
    if (err != X502_ERR_OK) {
        fprintf(stderr, "Error configuring input streams: %s\n", X502_GetErrorString(err));
        return 1;
    }

    // Начинаем чтение данных
    gettimeofday(&start, NULL);
    err = X502_StreamsStart(dev_hnd);
    gettimeofday(&stop, NULL);
    printf("X502_StreamsStart took %'d us\n", calc_delay(&start, &stop));
    if (err != X502_ERR_OK) {
        fprintf(stderr, "Error during startup: %s\n", X502_GetErrorString(err));
        return 1;
    }

    printf("Measurement started\n");

    uint32_t rdy_cnt = 0;
    for (int i=0; i < 10; ++i) {

        err = X502_GetRecvReadyCount(dev_hnd, &rdy_cnt);
        if (err != X502_ERR_OK) {
            fprintf(stderr, "Error during GetRecvReadyCount: %s\n", X502_GetErrorString(err));
            return 1;
        }
        printf("ReadyCount: %d\n", rdy_cnt);
        sleep(1);
    }

    gettimeofday(&start, NULL);
    err = X502_StreamsStop(dev_hnd);
    gettimeofday(&stop, NULL);
    printf("X502_StreamsStop took %'d us\n", calc_delay(&start, &stop));
    if (err != X502_ERR_OK) {
        fprintf(stderr, "Error stopping measurement: %s\n", X502_GetErrorString(err));
        return 1;
    }

    gettimeofday(&start, NULL);
    err = X502_Close(dev_hnd);
    if (err != X502_ERR_OK) {
        fprintf(stderr, "Error closing connection: %s\n", X502_GetErrorString(err));
    }
    gettimeofday(&stop, NULL);
    printf("X502_Close took %'d us\n", calc_delay(&start, &stop));

    X502_Free(dev_hnd);

    return 0;
}

Возвращает этот код следующее:

Library version: 1.1.34
Found 1 PCI device(s)
Device 0: L502 (SN: , Flags: 800400)
X502_OpenByDevRecord took 5105046 us
Successfully connected to device!
X502_StreamsStart took 1332647 us
Measurement started
ReadyCount: 0
ReadyCount: 0
ReadyCount: 0
ReadyCount: 0
ReadyCount: 17408
ReadyCount: 17408
ReadyCount: 17408
ReadyCount: 17408
ReadyCount: 17408
ReadyCount: 17408
X502_StreamsStop took 26249431 us
X502_Close took 26250668 us

Значение 17408 понятно: столько измерений будет выполнено за примерно 4 секунды при 2000Гц. Тот факт, что количество отсчетов далее не меняется, можно объяснить переполнением буфера. Но почему в течение первых 4х секунд буфер пуст, я так и не смог понять.
Попытки установить вручную шаг генерации прерываний при помощи X502_SetStreamStep ни к чему позитивному не привели.
Подскажите пожалуйста, в чем может быть причина такого поведения и куда еще можно покопать?

15.07.2025 16:54:48
#2

Участник
Здесь с 11.07.2025
Сообщений: 2

Re: Задержка при получении измерений АЦП

Пошел смотреть на всякий случай dmesg и увидел там ошибки:

[  408.554192] workqueue: vmstat_update hogged CPU for >10000us 32 times, consider switching to WQ_UNBOUND
[  411.179146] workqueue: psi_avgs_work hogged CPU for >10000us 4 times, consider switching to WQ_UNBOUND
[  419.804140] lpcie: Cannot stop dma channel 0!!
[  419.814709] workqueue: sync_rcu_exp_select_node_cpus hogged CPU for >10000us 4 times, consider switching to WQ_UNBOUND
[  427.689782] workqueue: sync_rcu_exp_select_node_cpus hogged CPU for >10000us 8 times, consider switching to WQ_UNBOUND
[  432.929163] lpcie: Cannot stop dma channel 1!!
[  438.179160] sched: RT throttling activated
[  438.179161] workqueue: psi_avgs_work hogged CPU for >10000us 8 times, consider switching to WQ_UNBOUND
[  443.439924] workqueue: sync_rcu_exp_select_node_cpus hogged CPU for >10000us 16 times, consider switching to WQ_UNBOUND
[  446.054176] lpcie: Cannot stop dma channel 0!!
[  457.866670] watchdog: BUG: soft lockup - CPU#2 stuck for 27s! [l502:6680]
[  457.866677] Modules linked in: intel_rapl_msr intel_rapl_common intel_uncore_frequency intel_uncore_frequency_common isst_if_common skx_edac skx_edac_common nfit x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel snd_hda_codec_realtek kvm snd_hda_codec_generic irqbypass crct10dif_pclmul polyval_clmulni polyval_generic ghash_clmulni_intel sha256_ssse3 snd_hda_intel sha1_ssse3 snd_intel_dspcfg aesni_intel snd_intel_sdw_acpi crypto_simd snd_hda_codec binfmt_misc cryptd snd_hda_core snd_hwdep snd_pcm rapl snd_seq_midi snd_seq_midi_event ipmi_ssif nls_iso8859_1 intel_cstate snd_rawmidi lpcie(OE) snd_seq snd_seq_device snd_timer intel_wmi_thunderbolt altera_cvp fpga_mgr joydev input_leds plx_pci cmdlinepart snd sja1000 ast peak_pciefd spi_nor can_dev i2c_algo_bit soundcore mei_me mtd ioatdma mei intel_pch_thermal acpi_power_meter ipmi_si acpi_ipmi ipmi_devintf ipmi_msghandler acpi_pad mac_hid sch_fq_codel msr parport_pc ppdev lp parport efi_pstore ip_tables x_tables autofs4 hid_generic usbhid hid crc32_pclmul ixgbe
[  457.866793]  spi_intel_pci i2c_i801 xfrm_algo ahci spi_intel dca i2c_smbus lpc_ich xhci_pci mdio libahci xhci_pci_renesas wmi
[  457.866810] CPU: 2 PID: 6680 Comm: l502 Tainted: G           OEL     6.8.0-60-generic #63~22.04.1-Ubuntu
[  457.866814] Hardware name: Supermicro Super Server/X11DPG-QT, BIOS 3.3 02/21/2020
[  457.866816] RIP: 0010:ioread32+0x3c/0x80
[  457.866824] Code: 89 fa ed 31 d2 31 f6 31 ff c3 cc cc cc cc 8b 05 ba 2e e6 01 85 c0 75 1d b8 ff ff ff ff 31 d2 31 f6 31 ff c3 cc cc cc cc 8b 07 <31> d2 31 f6 31 ff c3 cc cc cc cc 55 83 e8 01 48 89 fe 48 c7 c2 a5
[  457.866828] RSP: 0018:ffffb10228bd3a58 EFLAGS: 00000296
[  457.866831] RAX: 00000000ffffffff RBX: ffffb10228bd3ac4 RCX: 0000000000000001
[  457.866833] RDX: ffffb10228bd3ac4 RSI: 0000000000000701 RDI: ffffb10220325c04
[  457.866835] RBP: ffffb10228bd3a68 R08: 0000000000000000 R09: 0000000000000000
[  457.866837] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000002
[  457.866838] R13: 0000000000000001 R14: 0000000000000002 R15: ffff9f49c05e9680
[  457.866840] FS:  0000745b2f85f740(0000) GS:ffff9f60cf300000(0000) knlGS:0000000000000000
[  457.866842] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  457.866844] CR2: 000073b1cc01d898 CR3: 000000012fad8001 CR4: 00000000007706f0
[  457.866845] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  457.866846] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[  457.866848] PKRU: 55555554
[  457.866849] Call Trace:
[  457.866851]  <IRQ>
[  457.866855]  ? show_regs+0x6d/0x80
[  457.866862]  ? watchdog_timer_fn+0x206/0x290
[  457.866867]  ? __pfx_watchdog_timer_fn+0x10/0x10
[  457.866869]  ? __hrtimer_run_queues+0x10f/0x2a0
[  457.866874]  ? rcu_core+0x1d2/0x390
[  457.866878]  ? hrtimer_interrupt+0xf6/0x250
[  457.866881]  ? __sysvec_apic_timer_interrupt+0x4e/0x120
[  457.866887]  ? sysvec_apic_timer_interrupt+0x8d/0xd0
[  457.866893]  </IRQ>
[  457.866894]  <TASK>
[  457.866895]  ? asm_sysvec_apic_timer_interrupt+0x1b/0x20
[  457.866900]  ? ioread32+0x3c/0x80
[  457.866905]  ? lpcie_fpga_reg_read+0x31/0x60 [lpcie]
[  457.866910]  f_dma_stop+0x80/0x560 [lpcie]
[  457.866916]  lpcie_stream_free+0x28/0x140 [lpcie]
[  457.866921]  lpcie_streams_dev_release+0x28/0x50 [lpcie]
[  457.866925]  lpcie_release+0x2e/0x40 [lpcie]
[  457.866930]  __fput+0xa0/0x2e0
[  457.866937]  __fput_sync+0x1c/0x30
[  457.866940]  __x64_sys_close+0x3e/0x90
[  457.866945]  x64_sys_call+0x19ad/0x2480
[  457.866949]  do_syscall_64+0x81/0x170
[  457.866964]  ? iterate_tty_write+0x1a1/0x260
[  457.866971]  ? tty_ldisc_deref+0x16/0x20
[  457.866976]  ? file_tty_write.constprop.0+0x9b/0x110
[  457.866980]  ? tty_write+0x11/0x20
[  457.866983]  ? vfs_write+0x2a5/0x480
[  457.866988]  ? ksys_write+0x73/0x100
[  457.866992]  ? syscall_exit_to_user_mode+0x83/0x260
[  457.866995]  ? do_syscall_64+0x8d/0x170
[  457.866998]  ? restore_fpregs_from_fpstate+0x3d/0xd0
[  457.867002]  ? switch_fpu_return+0x55/0xf0
[  457.867005]  ? syscall_exit_to_user_mode+0x83/0x260
[  457.867009]  ? _copy_from_user+0x2f/0x80
[  457.867015]  ? lpcie_fpga_reg_write+0x32/0x50 [lpcie]
[  457.867018]  ? lpcie_ioctl_unlock+0x53a/0x580 [lpcie]
[  457.867023]  ? __x64_sys_ioctl+0xa0/0xf0
[  457.867030]  ? syscall_exit_to_user_mode+0x83/0x260
[  457.867034]  ? do_syscall_64+0x8d/0x170
[  457.867037]  ? do_syscall_64+0x8d/0x170
[  457.867040]  ? do_syscall_64+0x8d/0x170
[  457.867044]  ? irqentry_exit+0x43/0x50
[  457.867047]  ? clear_bhb_loop+0x15/0x70
[  457.867051]  ? clear_bhb_loop+0x15/0x70
[  457.867061]  ? clear_bhb_loop+0x15/0x70
[  457.867064]  entry_SYSCALL_64_after_hwframe+0x78/0x80
[  457.867070] RIP: 0033:0x745b2f714f67
[  457.867093] Code: ff e8 0d 16 02 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 03 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 41 c3 48 83 ec 18 89 7c 24 0c e8 73 ba f7 ff
[  457.867095] RSP: 002b:00007fff21ca20e8 EFLAGS: 00000246 ORIG_RAX: 0000000000000003
[  457.867098] RAX: ffffffffffffffda RBX: 00005e6929ccb880 RCX: 0000745b2f714f67
[  457.867100] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000003
[  457.867101] RBP: 00007fff21ca2140 R08: 0000000000000000 R09: 00007fff21ca1fea
[  457.867103] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
[  457.867104] R13: 00007fff21ca2130 R14: 00007fff21ca2124 R15: 00005e69198c609b
[  457.867107]  </TASK>

Происходит ошибка "Cannot stop dma channel" уже после вызовов X502_GetRecvReadyCount. Может быть, что проблемы связаны?

16.07.2025 21:01:59
#3

Сотрудник "Л Кард"
Здесь с 17.04.2014
Сообщений: 1,326

Re: Задержка при получении измерений АЦП

Судя по тому, что не читается серийный номер L-502, скорее всего плате нужно обновить прошивку, т.к. была проблема с совместимостью с некоторыми мат. платами. У Вас есть ПК (желательно с Windows),  на котором бы плата виделась и корректно считывался серийный номер (можно в X502Studio проверить)? Если да, то посмотрите версию прошивки и попробуйте обновить с помощью утилиты lxfw_update из последнего sdk