Mgl has some functions to handle digital I/O with a National Instruments card (e.g. NI USB 6501) that can be used to read and write digital I/O signals and sine wave analog output. These can be useful to synch to an MR scanner or control an external eye tracker. These functions all live in the directory:
These need to be compiled specially, in mgl 2.0 by running (earlier versions, just go mex by hand):
You will need to have downloaded the National Instruments drivers (see next section) to compile and use these functions.
Note that these functions are now compatible with both 32-bit and 64-bit Matlab even though the NI software (NI-DAQmx Base is not actually 64-bit compliant on Mac). See here for more details.
You can use a National Instruments card for digital I/O with mgl by doing the following:
>> mglDigIO('init') No matching processes belonging to you were found (mglStandaloneDigIO) Initializing NI device with digin port: Dev1/port2 digout port: Dev1/port1. End with mglDigIO('quit'). (mglStandaloneDigIO) Successfully initialized NI device (mglStandaloneDigIO) New connection made: 0 (mglPrivateDigIO) DigIO is running.
>> mglDigIO('digin') ans = type: [1 1 1 1 1 1 1 1] line: [0 1 2 3 4 5 6 7] when: [934383.731613 934383.731624 934383.731625 934383.731626 934383.731626 934383.731628 934383.731629 934383.731629]
availability: mgl 2.0 Mac OS X only
purpose: This is a mac specific command that is used to control a NI digital IO board. It has been tested with the NI USB 6501. It runs as a thread (on 64-bit runs a separate process and communicates via a socket: see below) that reads digital port 2 and logs any change in state (either up or down). It can also be used to set digital lines on port 1 at a time of your choosing. It is used by the task code if you set mglEditScreenParams to use digin. It is the preferred way of dealing with digital I/O since it keeps excellent timing. Note that if you are trying to read events up to about 250Hz (e.g. a square wave of 250Hz), you should be able to read all events without fail. Faster than that at around 500Hz you will likely start dropping events (this is likely due to how fast the NI-DAQ mxBase driver can pool the device). To use this function, you will need to compile it using mglMake('digin');
Here are thte commands it accepts:
|1:'init'||Init the digIO thread. You need to run this before anything else will work. You can optional specify input and output ports which default to 1 and 2 respectively: mglDigIO('init',inputPortNum,outputPortNum); You can also specify the device number using: mglDigIO('init',inputPortNum,outputPortNum,inputDevnum,outputDevnum). You can call init with different port numbers to reset what ports you want to listen/write to/from without calling quit inbetween.|
|2:'digin'||Returns all changes on the input digital port|
|3:'digout'||Set the output digital port a time of your choosing. This takes 2 other values. The time in seconds that you want the digital port to be set. And the value you want it to be set too. Time can be either an absolute time returned by mglGetSecs or it can be relative to now if it is a negative value: mglDigIO('digout',-5,0) → Sets the output port to 0 five secs from now.|
|4:'list'||Lists status and all pending digout events|
|5:'ao'||Sets the output port to produce a sine wave and then return to 0, you call it with parameters time (like digout above), channel (0 or 1 for A0 or A1), frequency,amplitude (volts peak - it will produce a sine wave that goes from -amplitude to amplitude) and duration in seconds. For example: mglDigIO('ao',-1,0,500,2.5,1);You can optionally set the sample rate (default is 250000 samples/second): mglDigIO('ao',-1,0,500,2.5,1,100000); And you can specify the device number (below will use the default sample rate, and use dev2/ao0): mglDigIO('ao',-1,0,500,2.5,1,,2);For more info see below|
|0:'quit'||Closes the nidaq ports, after this you won't be able to run other commands. Note that this does not shutdown the digIO thread. The reason for this is that the NIDAQ library is not thread safe, so you can only call its functions from one thread, so to be able to keep starting and stopping reading from the card, the thread is set to continue to run, and quit simply shuts down the nidaq tasks and stops logging events. After you call quit, you can use init again to restart reading/writing. If you need to shutdown the thread, use 'shutdown'|
|-1:'shutdown'||Quits the digIO thread if it is running, after this you won't be able to run other commands. If you plan on starting and stopping digIO collection, you should use init and quit rather than shutdown|
mglDigIO supports limited analog output functionality. We have used this with NI USB-6211 which has two analog output ports that can run at 250kHz. At this moment, all that is supported is the writing of a sine wave of specified amplitude and frequency. To set it up, you need to connect the NI USB-6211 output pins correctly. Follow the information in the Pinout diagram for your device. For analog output, we connect to Ground Pin 14 (black) and AO 0 Pin 12 (red) for the Analog output 0, and for analog output 1 we use the same ground pin and AO 1 Pin 13 (red). We also connect digital output on Pin 5 Digital Ground (black) and Pin 6 (red) to get output on Port 1 bit 0. For digital input, we use Pin 5 (black, ground) and Pin 1 (red). Note that to use the input, you will need to set the correct port number (since our default is to use port 2, and for this device input is on port 0).
So, to initialize mglDigIO you would do (note port settings for input on port 0 - default is port 2):
Then, for example, to output a sine wave 4 seconds from now which goes from -2 to 2 volts at 500HZ for 3 seconds on analog output port 0, you would do:
Note the following limitation. You must start one event at least 25 ms after the last event even if they are on different output channels. That is, you cannot have staggered output going at the same time on different channels. (the 25 ms is to give enough set up time to so set the new output state). So, you can show a sine wave on one channel followed by a sine wave on another channel like this:
But, the following will give an error and not run the second sine wave:
% This will not work!!! mglDigIO('ao',-4,0,500,2,3); mglDigIO('ao',-7,1,250,5,3);
However, you can produce a sine wave of the same frequency on both output channels at the same time for the same duration with different amplitudes. The following will produce a sine wave in 4 seconds at 500Hz for a duration of 3 seconds, with the sine wave going from -2 to 2 volts on analog output channel 0 and -4 to 4 volts on analog output channel 1:
mglDigIO('ao',-4,[0 1],500,[2 4],3);
But, for the time being you can't have different frequencies, different starting times or different durations. The reason for this is because of the NI-DAQmx Base API. It does not seem to allow only setting one analog output task at a time and gives me an error if I try to load the sine wave data for a second independently created output task even if it is on a different channel. In principle we could workaround this by loading onto the NI card the whole buffer of what we want to present for each channel (like having one start with 0 and then the sine wave and the other one having a different delay followed by a different sine wave). However, this is not what we have implemented now. Right now, we just load one cycle of a sine wave on to the card and then have the card repeat that over again for the desired duration. This is why we cannot have different frequencies (they would require buffers of different lengths).
You can also change the sampling frequency. This might be useful if you do not need a lot of time resolution on the signal produced since it requires less memory transfer on to the card:
You can also specify a different device (e.g. dev2/ao0 would be the 2nd NI device connected to your computer - not sure how you are supposed to know which device is which). This could be useful, for example, if you want to have one device for digital io and another for analog:
mglDigIO('init'); mglDigIO('ao',-4,0,500,2,3,100000,2); mglDigIO('digout',-1,255);
The above should initialize a digital input/output for Dev1 and analog output on Dev2.
purpose: write an ouput to the National Instruments board. portNum defaults to 2, to write from Dev1/port2. The first time you read it needs to open the port to the NI device which can take some time. Subsequent calls will be faster. Note that you can only open one port at a time, so if you need to read from two different ports it will always be closing and reopening the ports which will cause a performance hit (consider rewriting the code to keep multiple ports open if you need this). Also, if you want to switch between reading and writing on a single port, you will need to manually close the port in between read/write calls by setting portNum = -1 (see below).
portNum can also be set to:
|-1||closes any open port|
|-2||displays which port (if any) is open.|
Note that in the distribution, writeDigPort is not compiled. It always returns 0. To use it to read your NI card, you will need to mex readDigPort.c, this requires you to install the NI-DAQmx Base Frameworks.
purpose: read the National Instruments board digital input. portNum defaults to 1, to read from Dev1/port1. The first time you read it needs to open the port to the NI device which can take some time. Subsequent calls will be faster. Note that you can only open one port at a time, so if you need to read from two different ports it will always be closing and reopening the ports which will cause a performance hit (consider rewriting the code to keep multiple ports open if you need this). Also, if you want to switch between reading and writing on a single port, you will need to manually close the port in between read/write calls by setting portNum = -1 (see below).
portNum can also be set to:
|-1||closes any open port|
|-2||displays which port (if any) is open.|
Note that: in the distribution, readDigPort is not compiled. It always returns 0. To use it to read your NI card, you will need to mex readDigPort.c, this requires you to install the NI-DAQmx Base Frameworks.
The NI-DAQmx Base library is currently (4/20/2013) available only for 32-bit (Note that version 3.6 will run on a 64 bit platform, but you can't create 64 applications). To get around this on 64-bit Matlab, we run a separate function called mglStandaloneDigIO and communicate with that function through a socket. This is all done in the background for you so there is nothing that you have to do if you use mglDigIO.
The way it works is as follows. When you init, by doing mglDigIO('init') the function mglStandaloneDigIO is started. This function will connect to the NI card and answer commands through a socket (named .mglDigIO in your home directory). The function mglDigIO when called to get digin events or set digout events connects through the socket and communicates with mglStandaloneDigIO. This function will continue to run until you quit matlab or run mglDigIO('shutdown'). If you need to shut this function down from outside matlab, you can do from a command line:
Measurement Computing makes a USB based digital I/O device with a 64 bit library for Mac which potentially could be a replacement for National Instruments which does not have a 64-bit library. However, we found that the driver needs to have c# support through the Mono Framework and were not able to find a way to make this compatible with Matlab through a Mex file. For reference, the driver can be downloaded from here.