Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

How can I configure a digital input task to read continuous samples?

Solved!
Go to solution

I am trying to create a digital-only task which will do digital reads at a hardware-timed rate using a PCIe-6509. However, when I try to set the task timing as follows (which works on a PCIe-6509), I get the following error:

 

Requested value is not a supported value for this property. The property value may be invalid because it conflicts with another property.

 Property: NationalInstruments.DAQmx.Timing.SampleTimingType

Requested Value: NationalInstruments.DAQmx.SampleTimingType.SampleClock

Possible Values: NationalInstruments.DAQmx.SampleTimingType.OnDemand, NationalInstruments.DAQmx.SampleTimingType.ChangeDetection

 Task Name: DigitalInputTask

 Status Code: -200077

 

 

 

The relevant parts of my code are:

 

    public class DigitalInputReader : IDisposable
{
public DigitalInputReader() { dataReadyHandler = new System.AsyncCallback(DataReadyEventHandler); daqmxTask = new DigitalInputTask(); daqmxTask.Configure(Globals.NI); daqmxTask.Control(TaskAction.Verify); daqmxTask.Control(TaskAction.Commit); daqmxReader = new DigitalMultiChannelReader(daqmxTask.Stream); } public class DigitalInputTask : Task { public DigitalInputTask() : base("DigitalInputTask") { } public virtual void Configure(NiConfiguration niConfig) { for (int i = 0; i <= niConfig.DigitalInputs.Count - 1; i++) { string physicalChannelName = niConfig.Device + "/port" + niConfig.DigitalInputs[i].Port.ToString() + "/line" + niConfig.DigitalInputs[i].Channel.ToString(); string nameToAssignToChannel = niConfig.DigitalInputs[i].Name; DIChannel ch = this.DIChannels.CreateChannel(physicalChannelName, nameToAssignToChannel, ChannelLineGrouping.OneChannelForEachLine); ch.InvertLines = niConfig.DigitalInputs[i].InvertLines; } var signalSource = ""; this.Timing.ConfigureSampleClock(signalSource, Globals.MachineSettings.SampleRate, SampleClockActiveEdge.Rising, SampleQuantityMode.ContinuousSamples);// Globals.MachineSettings.SamplesPerChannel); } }

 That last call to Task.Timing.ConfigureSampleClock is what throws the errors.

 

Of the available options, neither SampleTimingType.OnDemand or NationalInstruments.DAQmx.SampleTimingType.ChangeDetection provide the same precisely timed calls that I'm used to with analog input interrupts.

 

How can this be done in a digital task?  I mean, it looks like I could set up another task to do hardware-timed output of a clock signal and use the ChangeDetection timing mode, but that seems a little convoluted for what should be easy to do.  What am I missing?

0 Kudos
Message 1 of 3
(4,341 Views)

I just noticed: Visual Studio Intellisense (and the NI help) seem to indicate that the fourth parameter to ConfigureSampleClock should be of type "SampleQuantityMode", and that SampleQuantityMode has three members: ContinuousSamples, FiniteSamples, and HardwareTimedSinglePoint

 

However, the error message suggests that the fourth parameter should be of type "SampleTimingType", with possible values of OnDemand and ChangeDetection. When I enter

 

this.Timing.ConfigureSampleClock(signalSource, Globals.MachineSettings.SampleRate, SampleClockActiveEdge.Rising, SampleTimingType.OnDemand);

 

which should (if the error message is accurate) work at least once, or I can call it (temporarily, while we're figuring this out) from a low-accuracy software timer, I get compilation errors:

 

The best overloaded method match for 'NationalInstruments.DAQmx.Timing.ConfigureSampleClock(string, double, NationalInstruments.DAQmx.SampleClockActiveEdge, NationalInstruments.DAQmx.SampleQuantityMode)' has some invalid arguments

 

and

 

Argument 4: cannot convert from 'NationalInstruments.DAQmx.SampleTimingType' to 'NationalInstruments.DAQmx.SampleQuantityMode'

Casting it to (SampleQuantityMode) produces errors that the various other SampleQuantityModes with values equal to their respective SampleTimingTypes are also invalid.

 

It looks more like this is a real bug than the configuration mistake I initially expected it to be!

0 Kudos
Message 2 of 3
(4,338 Views)
Solution
Accepted by topic author kvermeer

Update: I figured it out.  You can't call ConfigureSampleClock when the digital input card is a 650x device, because those devices don't have automated sample clocks at all.  They are configured to run in finite samples mode by default.  You must do all sample clocking with these devices in software.

 

Be careful, though, because .NET timers only guarantee that they will tick no faster than their programmed interval.  In practice, they're typically 5-10 ms slow per tick.  This means that if you intended to read samples every 100 ms by sample clock, you'd end up reading samples every 108 ms.  Any counters based on the elapsed time and sample count would be way off after just a few seconds of this.

 

Instead, you need to do one of four things: Write a doggone driver that runs in ring 0 and interfaces with the PCIe card at the required interval (that's on NI, not you, in practice), tolerate the clock skew, use a multimedia timer like an audio or video interrupt that's more likely to respond at the correct interval, or, my solution, use an accurate clock to adjust the timer interval.  I wrote the following timer code: 

 

var CorrectiveStopwatch = new System.Diagnostics.Stopwatch();
var CorrectedTimer = new System.Timers.Timer()
{
    Interval = targetInterval,
    AutoReset = true,
};
CorrectedTimer.Elapsed += (o, e) =>
{
    var actualMilliseconds = ; 

    // Adjust the next tick so that it's accurate
    // EG: Stopwatch says we're at 2015 ms, we should be at 2000 ms
    // 2000 + 100 - 2015 = 85 and should trigger at the right time
    var newInterval = StopwatchCorrectedElapsedMilliseconds + 
                      targetInterval -
                       CorrectiveStopwatch.ElapsedMilliseconds;

    // If we're over 1 target interval too slow, trigger ASAP!
    if (newInterval <= 0)
    {
        newInterval = 1; 
    }

    CorrectedTimer.Interval = newInterval;

    StopwatchCorrectedElapsedMilliseconds += targetInterval;
};

 
I hope that helps someone.

0 Kudos
Message 3 of 3
(4,291 Views)