0

我目前正在从事一个涉及 Simulink 中的数据采集和实时处理的项目。我们有一些专门为此项目购买的 DAQ 硬件 - 即 National Instruments DAQ 设备 (USB)。

我必须使用基于会话的界面。我尝试编写 2 级 MS 函数,但我的问题是,我不知道应该在哪里使用 daq.createSession 函数创建会话以及在哪里可以创建模拟输出。有人有代码示例或建议吗?

或者编写 C 库会更容易吗?我可以为这个 C 库使用哪个 Simulink 块?Matlab函数?我可以使用 DAQmxErrChk (DAQmxCreateAOVoltageChan(taskHandle,"Dev1/ao0","",-10.0,10.0,DAQmx_Val_Volts,NULL)); Simulink 中 DAQmx 示例的功能有哪些问题?

4

1 回答 1

0

I can't post my entire workaround for this, as it is many lines of code and that's goes a bit against the SO regulations. I will, however, try my best to explain how it works so that you can do something similar. My life while coding this was certainly made easier by the fact that I have a set of classes that allow you to write a Level-2 M-S function inside a class, but you could certainly do it without.

Basically your best bet is to create a mask for your block. Each time you modify the mask parameters, Simulink will call your onSetup callback - where you would normally setup channels / register other M-S function callbacks. It is in this function that I create my DAQ session, if it's not already created, update the channel count, sample rate, and channel mode (and any other session info). You can then set the UserData property of the block to either your DAQ session interface, or a struct or an instance of some handle class that has a reference to your DAQ session.

You will also need some way of queueing the incoming data. You could get a queue class off the FileExchange (I think there are a few), although I'm not sure about the speed of these. I created a queue class that uses a circular buffer as its data storage, as there is probably no need to go for a full implementation of a linked list here, and this is likely to be faster (no need to delete / instantiate a class every time a new sample comes in). The class errors if you try to pop an empty queue and warns with a 'queue overrun'-type error if you push onto a full queue. The queue is allocated / deallocated in, respectively, onPostPropagationSetup and onTerminate.

When data is received from the DAQ session, the following function is called

function onDaqDataAvailable(self, data)
    self.sampleQueue.push(data.Data);
    self.updateOutputStatus();
end

function updateOutputStatus(self)
    if self.sampleQueue.filledLocs >= 0.5 * self.sampleQueue.queueLength
        self.bOutput = true;
    else
        self.bOutput = false;
    end

    drawnow update;
end

I'm not sure whether the threshold there is actually necessary, and in fact think it might be a bit too high, but you can fiddle with it yourself. Then, in the onOutputs callback, we have

    function onOutputs(self, block)
        if ~self.daqSess.IsRunning
            self.daqSess.startBackground();
        end

        while ~self.bOutput
            pause(0.1 * self.frameLength / self.sampleRate);
            // Displaying number of samples in queue is useful for debugging here...
            // But we should really use a scope instead
            disp(self.sampleQueue.filledLocs)
            self.updateOutputStatus();
        end

        // Displaying number of samples in queue is useful for debugging here...
        // But we should really use a scope instead
        //disp(self.sampleQueue.filledLocs)
        samples = self.sampleQueue.pop(self.frameLength);
        for nChan = 1:self.nChannels
            block.OutputPort(nChan).Data = samples(:, nChan);
        end

        self.updateOutputStatus();
    end

This is the basis for how the block actually works. One note is that you need to set the DAQ session's NotifyWhenDataAvailableExceeds property to something sensible, so that you get a maximum of 20 calls to onDaqDataAvailable per second, but once you have a sample queue this is fairly trivial. Note also that the DAQ session is not actually started until the model is REALLY ready. Starting it before hand would result in lots of queue overruns as scopes etc. opened their UIs.

I hope this has explained it enough for you to produce a comparable workaround.

于 2013-04-06T12:11:36.627 回答