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


Форум

Вы не вошли.

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

#1 Re: Техническая поддержка и выбор оборудования » E14-440. Неправильный съём данных (C#) » Сегодня 09:45:46

1. Мы снимаем с фотоприёмного устройства сигнал биений, синусоидальные колебания высокой частоты (4кГц). Регулируя ток накачки лазера, мы можем менять их амплитуду. И когда я меняю амплитуду, в моей программе съёма сигнал по амплитуде не меняется, а в LGraph II меняется.
2. Вот архив с проектом: https://www.lcard.ru/forums/img/members … client.rar, но ужимать здесь особо нечего, тут и так только и есть съём с АЦП. Только, поскольку Microsoft не добавила в WPF контролы для построения графиков, мы используем DevExpress 23.1, так что, возможно, он у вас не соберётся.
3. И можете пояснить, почему 8000, а не 8192? разве 14-битный АЦП не возвращает код от -2^13 до 2^13-1?

#2 Техническая поддержка и выбор оборудования » E14-440. Неправильный съём данных (C#) » 13.11.2025 18:06:27

AlexZharikov
Ответов: 2

Добрый день!

Карта E14-440. Библиотека lcomp, wlcomp.

После запуска съёма устройство не реагирует на изменения амплитуды входного сигнала. При этом при съёме через LGraph II этого не происходит.

Подскажите пожалуйста в чем может быть причина?

using DevExpress.XtraRichEdit.Import.Html;
using LCardClient.Infrastructure.Exceptions;
using LCardClient.Infrastructure.Services.Interfaces;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LCardClient.Infrastructure.Services.Implementation
{
    public sealed class LCardE440Controller : IAdc, IDisposable
    {
        private const string ApiLibrary = "wlcomp.dll";
        private const string DriverBinary = "lcomp.dll";
        private const string DeviceBiosName = "e440";
        private const uint LAdcParam = 1;
        private const uint AdcParamT3 = 2;
        private const uint StreamAdc = 1;
        private const int BytesPerSample = sizeof(short);
        private const double CodesToVolts = 10.0 / 8192.0;

        private uint driverHandle;
        private uint interfaceHandle;
        private double calibrationOffset;

        private IntPtr dataPtr;
        private IntPtr syncPtr;
        private WAdcParamU0 param;

        public bool CorrectionEnabled { get; private set; } = false;
        public double SampleRateHz { get; private set; } = 10_000;
        public IReadOnlyList<uint>? Channels { get; private set; }

        public void ReadVoltages(
            double seconds,
            CancellationToken token,
            IList<float> bufferTime,
            IList<float> bufferSamples)
        {
            long samplesPerChannelNeeded = (long)Math.Ceiling(seconds * SampleRateHz);
            long totalSamplesNeeded = samplesPerChannelNeeded * Channels.Count;
            long producedSamples = 0;
            double dt = 1.0 / SampleRateHz;

            uint ringLength = param.IrqStep * param.Pages;
            uint readIndex = 0;
            short[] staging = ArrayPool<short>.Shared.Rent((int)Math.Min(ringLength, 65536u));
            uint oldSyncValue = 0;
            
            try
            {
                while (producedSamples < totalSamplesNeeded)
                {
                    if (token.IsCancellationRequested)
                    {
                        return;
                    }
                    Check(GetSyncData(ref interfaceHandle, syncPtr, 0, out uint syncValue), nameof(GetSyncData));
                    //uint available = syncValue >= readIndex
                    //    ? syncValue - readIndex
                    //    : ringLength - readIndex + syncValue;

                    uint available = 0;
                    if (syncValue < oldSyncValue)
                        available = ringLength - oldSyncValue + syncValue;
                    else
                        available = syncValue - oldSyncValue;
                    oldSyncValue = syncValue;

                    if (available == 0)
                    {
                        Thread.SpinWait(64);
                        continue;
                    }

                    uint toCopy = Math.Min(available, (uint)staging.Length);
                    CopySamples(dataPtr, readIndex, ringLength, staging, toCopy);
                    var span = staging.AsSpan(0, (int)toCopy);
                    for (int i = 0; i < span.Length && producedSamples < totalSamplesNeeded; i++)
                    {
                        short code = span[i];
                        long frameIndex = producedSamples / Channels.Count;

                        bufferTime.Add((float)(frameIndex * dt));
                        bufferSamples.Add((float)((code) * CodesToVolts));

                        producedSamples++;
                    }

                    readIndex = (readIndex + toCopy) % ringLength;
                }
            }
            finally
            {
                ArrayPool<short>.Shared.Return(staging);
            }
        }

        public string Initialize(object? slot = null) => InitializeLCard(DriverBinary, DeviceBiosName, slot.CheckedCast<uint>());

        public void Start(IReadOnlyList<uint> channels, double sampleRateHz, bool enableCorrection = false)
        {
            if (interfaceHandle == 0)
                throw new InvalidOperationException("Device not initialized. Call Start() first.");
            if (channels == null || channels.Count == 0)
                throw new ArgumentException("channels empty", nameof(channels));
            if (sampleRateHz <= 0)
                throw new ArgumentOutOfRangeException(nameof(sampleRateHz));

            param = BuildAdcParameters(channels, sampleRateHz);

            uint ringSize = param.IrqStep * param.Pages;
            Check(RequestBufferStream(ref interfaceHandle, ref ringSize, StreamAdc), nameof(RequestBufferStream));
            Check(FillDAQparameters(ref interfaceHandle, ref param, AdcParamT3), nameof(FillDAQparameters));

            Check(SetParametersStream(
                ref interfaceHandle,
                ref param,
                AdcParamT3,
                out uint _,
                out dataPtr,
                out syncPtr,
                StreamAdc), nameof(SetParametersStream));

            if (dataPtr == nint.Zero || syncPtr == nint.Zero)
                throw new InvalidOperationException("Driver returned invalid data or sync pointers.");

            Channels = channels;
            SampleRateHz = sampleRateHz;
            CorrectionEnabled = enableCorrection;

            Check(EnableCorrection(ref interfaceHandle, (ushort)(CorrectionEnabled ? 1 : 0)), nameof(EnableCorrection));
            
            Check(InitStartLDevice(ref interfaceHandle), nameof(InitStartLDevice));
            Check(StartLDevice(ref interfaceHandle), nameof(StartLDevice));
                        
        }

        public void Stop()
        {
            Check(StopLDevice(ref interfaceHandle), nameof(StopLDevice));
        }

        public double ReadOneSample(uint channel)
        {
            if (interfaceHandle == 0)
                throw new InvalidOperationException("Device not initialized.");

            var asyncParam = new AsyncParam(128)
            {
                s_Type = 6,
                NCh = 1u,
                Chn = { [0] = channel }
            };

            asyncParam.Chn[0] = channel;

            CheckError(IoAsync(ref interfaceHandle, ref asyncParam), "IoAsync failed when reading a sample.");

            short value = unchecked((short)asyncParam.Data[0]);
            return value;
        }

        public static uint MakeChWord(int channel, bool differential, int gainCode)
        {
            uint word = (uint)(gainCode & 0x3) << 6;
            if (differential)
            {
                word |= (uint)(channel & 0x0F);
            }
            else
            {
                word |= 1u << 5 | (uint)(channel & 0x1F);
            }
            return word;
        }

        public void Dispose()
        {
            CloseDevice();
            GC.SuppressFinalize(this);
        }

        ~LCardE440Controller() => CloseDevice();

        private static WAdcParamU0 BuildAdcParameters(IReadOnlyList<uint> channels, double sampleRateHz)
        {
            var param = new WAdcParamU0(true)
            {
                s_Type = LAdcParam,
                FIFO = 4096,
                IrqStep = 4096,
                Pages = 32,
                AutoInit = 1,
                dRate = sampleRateHz * channels.Count / 1000.0,
                dKadr = 0,
                dScale = 0,
                SynchroType = 0,
                SynchroSensitivity = 0,
                SynchroMode = 0,
                AdChannel = 0,
                AdPorog = 0,
                NCh = (uint)channels.Count,
                IrqEna = 1,
                AdcEna = 1
            };

            for (int i = 0; i < channels.Count; i++)
            {
                param.Chn[i] = channels[i];
            }

            return param;
        }

        private string InitializeLCard(string driverBinary, string bios, uint? slot)
        {
            CloseDevice();

            driverHandle = LoadAPIDLL(driverBinary);
            if (driverHandle == 0)
                throw new InvalidOperationException("LoadAPIDLL failed.");

            uint slotToUse = slot ?? DetectE440Slot(driverBinary, bios);

            uint err = 0;
            interfaceHandle = CallCreateInstance(ref driverHandle, slotToUse, ref err);
            if (err != 0 || interfaceHandle == 0)
                throw new InvalidOperationException($"CallCreateInstance failed for slot {slotToUse} (err={err}).");

            uint openStatus = OpenLDevice(ref interfaceHandle);
            if (openStatus == 0xFFFFFFFFu)
                throw new InvalidOperationException($"OpenLDevice failed for slot {slotToUse} (status=0x{openStatus:X}).");

            var slotInfo = new SlotPar();
            uint slotStatus = GetSlotParam(ref interfaceHandle, ref slotInfo);
            if (slotStatus != 0)
                throw new InvalidOperationException($"GetSlotParam failed for slot {slotToUse} (status=0x{slotStatus:X}).");

            CheckError(LoadBios(ref interfaceHandle, bios), $"LoadBios failed. Ensure {bios}.bin is present.");
            CheckError(PlataTest(ref interfaceHandle), "PlataTest failed.");

            var descr = new PlataDescr(0);
            CheckError(ReadPlataDescr(ref interfaceHandle, ref descr), "ReadPlataDescr failed.");

            calibrationOffset = ReadOneSample(48u);

            return $"Serial number: {Encoding.ASCII.GetString(descr.SerNum)}, " +
                $"board name: {Encoding.ASCII.GetString(descr.BrdName)}, " +
                $"DSP type: {Encoding.ASCII.GetString(descr.DspType)}";
        }

        private void CloseDevice()
        {
            if (interfaceHandle != 0)
            {
                CloseLDevice(ref interfaceHandle);
                interfaceHandle = 0;
            }

            if (driverHandle != 0)
            {
                FreeAPIDLL(ref driverHandle);
                driverHandle = 0;
            }
        }

        private uint DetectE440Slot(string driverBinary, string bios)
        {
            var inspected = new StringBuilder();

            for (uint slot = 0; slot < 32; slot++)
            {
                uint err = 0;
                uint testHandle = CallCreateInstance(ref driverHandle, slot, ref err);
                if (err != 0 || testHandle == 0)
                {
                    inspected.AppendLine($"slot {slot}: CallCreateInstance err={err}");
                    continue;
                }

                bool opened = false;
                try
                {
                    uint openStatus = OpenLDevice(ref testHandle);
                    if (openStatus == 0xFFFFFFFFu)
                    {
                        inspected.AppendLine($"slot {slot}: OpenLDevice status=0x{openStatus:X}");
                        continue;
                    }

                    opened = true;

                    var slotInfo = new SlotPar();
                    uint slotStatus = GetSlotParam(ref testHandle, ref slotInfo);
                    inspected.AppendLine($"slot {slot}: boardType={slotInfo.BoardType} status=0x{slotStatus:X}");
                    if (slotStatus != 0)
                        continue;

                    if (slotInfo.BoardType == SlotPar.BoardTypeE440)
                        return slot;

                    if (slotInfo.BoardType == 0)
                    {
                        if (LoadBios(ref testHandle, bios) != 0)
                            continue;

                        if (PlataTest(ref testHandle) != 0)
                            continue;

                        var descr = new PlataDescr(0);
                        if (ReadPlataDescr(ref testHandle, ref descr) != 0)
                            continue;

                        string name = BytesToAscii(descr.BrdName);
                        inspected.AppendLine($"slot {slot}: name={name}");
                        if (name.Contains("E-14-440", StringComparison.OrdinalIgnoreCase) ||
                            name.Contains("E440", StringComparison.OrdinalIgnoreCase))
                            return slot;
                    }
                }
                finally
                {
                    if (opened && testHandle != 0)
                        CloseLDevice(ref testHandle);
                }
            }

            throw new InvalidOperationException($"E14-440 board not found. Specify slot explicitly.\nChecked:\n{inspected}");
        }

        private static string BytesToAscii(byte[] bytes)
        {
            int len = Array.IndexOf(bytes, (byte)0);
            if (len < 0)
                len = bytes.Length;
            return Encoding.ASCII.GetString(bytes, 0, len);
        }

        private void CopySamples(nint dataPtr, uint readIndex, uint ringLength, short[] staging, uint samplesToCopy)
        {
            if (samplesToCopy == 0)
                return;

            uint firstChunk = Math.Min(samplesToCopy, ringLength - readIndex);
            uint secondChunk = samplesToCopy - firstChunk;

            GCHandle destHandle = GCHandle.Alloc(staging, GCHandleType.Pinned);
            try
            {
                nint destPtr = destHandle.AddrOfPinnedObject();

                if (firstChunk > 0)
                {
                    nint srcPtr = nint.Add(dataPtr, (int)(readIndex * BytesPerSample));
                    Check(GetDataFromBuffer(ref interfaceHandle, srcPtr, destPtr, firstChunk * BytesPerSample, 0), nameof(GetDataFromBuffer));
                }

                if (secondChunk > 0)
                {
                    nint srcPtr = dataPtr;
                    nint destPtr2 = nint.Add(destPtr, (int)(firstChunk * BytesPerSample));
                    Check(GetDataFromBuffer(ref interfaceHandle, srcPtr, destPtr2, secondChunk * BytesPerSample, 0), nameof(GetDataFromBuffer));
                }
            }
            finally
            {
                destHandle.Free();
            }
        }

        private static void Check(uint status, string where)
        {
            if (status != 0)
                throw new InvalidOperationException($"{where} failed (status 0x{status:X}).");
        }

        private static void CheckError(uint status, string message)
        {
            if (status != 0)
                throw new InvalidOperationException(message);
        }

        #region Native structures

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct AsyncParam
        {
            public uint s_Type;
            public uint FIFO;
            public uint IrqStep;
            public uint Pages;
            public double dRate;
            public uint Rate;
            public uint NCh;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
            public uint[] Chn;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
            public uint[] Data;
            public uint Mode;

            public AsyncParam(int channelCount = 128)
            {
                s_Type = 0;
                FIFO = 0;
                IrqStep = 0;
                Pages = 0;
                dRate = 0;
                Rate = 0;
                NCh = 0;
                Mode = 0;
                Chn = new uint[channelCount];
                Data = new uint[channelCount];
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct PlataDescr
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
            public byte[] SerNum;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
            public byte[] BrdName;
            public byte Rev;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
            public byte[] DspType;
            public int Quartz;
            public ushort IsDacPresent;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
            public ushort[] Reserv1;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            public ushort[] KoefADC;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
            public ushort[] KoefDAC;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
            public ushort[] Custom;

            public PlataDescr(int _)
            {
                SerNum = new byte[9];
                BrdName = new byte[5];
                DspType = new byte[5];
                Reserv1 = new ushort[7];
                KoefADC = new ushort[8];
                KoefDAC = new ushort[4];
                Custom = new ushort[32];
                Rev = 0;
                Quartz = 0;
                IsDacPresent = 0;
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct WAdcParamU0
        {
            public uint s_Type;
            public uint FIFO;
            public uint IrqStep;
            public uint Pages;
            public uint AutoInit;
            public double dRate;
            public double dKadr;
            public double dScale;
            public uint Rate;
            public uint Kadr;
            public uint Scale;
            public uint FPDelay;
            public uint SynchroType;
            public uint SynchroSensitivity;
            public uint SynchroMode;
            public uint AdChannel;
            public uint AdPorog;
            public uint NCh;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
            public uint[] Chn;
            public uint IrqEna;
            public uint AdcEna;

            public WAdcParamU0(bool _)
            {
                s_Type = 0;
                FIFO = 4096;
                IrqStep = 4096;
                Pages = 4;
                AutoInit = 1;
                dRate = 0;
                dKadr = 0;
                dScale = 0;
                Rate = 0;
                Kadr = 0;
                Scale = 0;
                FPDelay = 0;
                SynchroType = 0;
                SynchroSensitivity = 0;
                SynchroMode = 0;
                AdChannel = 0;
                AdPorog = 0;
                NCh = 0;
                Chn = new uint[128];
                IrqEna = 1;
                AdcEna = 1;
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct SlotPar
        {
            public uint Base;
            public uint BaseL;
            public uint Base1;
            public uint BaseL1;
            public uint Mem;
            public uint MemL;
            public uint Mem1;
            public uint MemL1;
            public uint Irq;
            public uint BoardType;
            public uint DSPType;
            public uint Dma;
            public uint DmaDac;
            public uint DtaReg;
            public uint IdmaReg;
            public uint CmdReg;
            public uint IrqReset;
            public uint DtaArray;
            public uint ReadyReg;
            public uint CfgReg;

            public const uint BoardTypeE440 = 30u;
        }

        #endregion

        #region Native bindings

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint LoadAPIDLL(string dll);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint FreeAPIDLL(ref uint hDll);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint CallCreateInstance(ref uint hDll, uint slot, ref uint err);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint OpenLDevice(ref uint hIfc);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint CloseLDevice(ref uint hIfc);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint LoadBios(ref uint hIfc, string bios);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint PlataTest(ref uint hIfc);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint IoAsync(ref uint hIfc, ref AsyncParam p);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint ReadPlataDescr(ref uint hIfc, ref PlataDescr p);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint GetSlotParam(ref uint hIfc, ref SlotPar slot);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint FillDAQparameters(ref uint hIfc, ref WAdcParamU0 p, uint spType);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint RequestBufferStream(ref uint hIfc, ref uint size, uint streamId);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint SetParametersStream(
            ref uint hIfc,
            ref WAdcParamU0 sp,
            uint spType,
            out uint usedSize,
            out nint data,
            out nint sync,
            uint streamId);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint EnableCorrection(ref uint hIfc, ushort ena);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint InitStartLDevice(ref uint hIfc);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint StartLDevice(ref uint hIfc);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint StopLDevice(ref uint hIfc);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint GetSyncData(ref uint hIfc, nint syncPtr, uint offset, out uint syncValue);

        [DllImport(ApiLibrary, CallingConvention = CallingConvention.Cdecl)]
        private static extern uint GetDataFromBuffer(ref uint hIfc, nint dataPtr, nint destination, uint size, uint mask);

        #endregion
    }
}

P.S. Если будут вопросы к импортам функций, ULONG в Win32 - это 32 бита, в C# этому соответствует uint.

Контакты

Адрес: 117105, Москва, Варшавское шоссе, д. 5, корп. 4

Многоканальный телефон:+7 (495) 785-95-25

Письма и запросы: lcard@lcard.ru
Отдел продаж: sale@lcard.ru
Техническая поддержка: support@lcard.ru

Время работы: с 9-00 до 19-00 мск