Instrument control package
Instrument-Control is a package for interfacing the outside world of hardware via Serial, i2c or Parallel interfaces. It is currently under development by Andrius Sutas, you can browse the SVN repository here and download the package here.
Compatibility
Below is a table showing package compatibility with various platforms:
Linux | Windows (Cygwin) | FreeBSD | Mac OS X | |
---|---|---|---|---|
Serial | v0.1.0 | WIP | WIP | WIP |
Parallel | v0.1.0 | - | WIP | - |
i2c | v0.1.0 | - | WIP | - |
TCP | WIP | WIP | - | - |
USBTMC | WIP | - | - | - |
GPIB | WIP | - | - | - |
VXI11 | WIP | WIP | - | - |
Where: (VER) - Included since VER version; (WIP) - Work In Progress and will probably be included in next release; (-) - Not supported / Untested.
You might see errors while doing "pkg install", which mean that package is unable to support related interface on your platform (e.g. due to missing header files), therefore one should always check for the supported interfaces before trying to use them:
Code: Check for interface support |
pkg load instrument-control
if (exist("serial") == 3)
disp("Serial: Supported")
else
disp("Serial: Unsupported")
endif
#similarly with:
#exist("parallel") == 3
#exist("i2c") == 3
|
Examples
Serial
Configuring interface
You might want to configure interface in a loopback mode for testing.
- If you have a Serial adapter, then simply connect pins 3 (Tx) and 2 (Rx) together (assuming your adapter is RS-232 DE9, otherwise check before doing anything). One can also insert an LED in series to view the actual bitstream.
- If you do not have a Serial adapter then create a virtual port using:
$ socat PTY,link=/dev/ttyS15 PTY,link=/dev/ttyS16
which will open two interconnected interfaces, where one (e.g. /dev/ttyS15) can be opened in Octave and second (e.g. /dev/ttyS16) using something like GNU screen:
$ screen /dev/ttyS16 <baudrate>
do not forget to change interface permissions if you want to use them with Octave/screen without root privileges.
Example: basic use
Here is a simple example to get started with the serial package. It tests the RxD and TxD lines. Make sure you have defined a loopback or connected the pins of your adapter.
Code: Serial port example |
# Open default serial port ttyUSB0 in default configuration of 115200, 8-N-1
s0 = serial()
# Opens serial port ttyUSB1 with baudrate of 115200 (config defaults to 8-N-1)
s1 = serial("/dev/ttyUSB1", 115200)
# Flush input and output buffers
srl_flush(s1);
# Blocking write call, currently only accepts strings
srl_write(s1, "Hello world!")
# Blocking read call, returns uint8 array of exactly 12 bytes read
data = srl_read(s1, 12)
# Convert uint8 array to string,
char(data)
|
Chaging some configurations is simple done by calling helper functions
Code: Serial port example: helper functions |
srl_baudrate(s1, 9600) # Change baudrate
srl_bytesize(s1, 5) # Change byte size (config becomes 5-N-1)
srl_parity(s1, "E") # Changes parity checking (config becomes 5-E-1),
# possible values [E]ven, [O]dd, [N]one.
srl_stopbits(s1, 2) # Changes stop bits (config becomes 5-E-2), possible
# values 1, 2.
|
Some properties can be set at opening time
Code: Serial port example: constructor call |
s2 = serial("/dev/ttyS0", 9600, 10) # Opens serial port ttyS0 in
# 9600 baudrate with 1s read timeout
|
Do not forget to close the ports when you are done!
srl_close(s0) # Closes and releases file descriptor
srl_close(s1) # Closes and releases file descriptor
srl_close(s2) # Closes and releases file descriptor
|
Parallel
Configuring interface
You will need to load following modules:
# modprobe parport # modprobe ppdev
Now you should see devices like "/dev/parport0". In case you do not, you will need to create them manually and give sufficient privileges for your user:
# mknod /dev/parport0 c 99 0 -m 666
Example 1: LED trace
For this example you will need to connect 8 LEDs to Parallel interface Data port as shown below:
And then the actual script:
Code: trace.m |
delay = 0.1;
pp = parallel("/dev/parport0", 0);
pp_data(pp, 0);
pins = pow2([0, 1, 2, 3, 4, 5, 6, 7]);
while 1
for p = pins
pp_data(pp, p);
sleep(delay);
endfor
# Reverse the order
pins = pins(end:-1:1);
endwhile
pp_close(pp);
|
Run it and you should see LEDs "tracing" forwards and backwards.
Example 2: LED dimming a.k.a. PWM control
Use the same schematic as in Example 1.
Code: dim.m |
delay = 0.00005;
pp = parallel("/dev/parport0", 0);
pp_data(pp, 0);
dRange = 0:100;
while 1
for duty = dRange
pp_data(pp, 255);
for dOn = 0:duty
sleep(delay);
endfor
pp_data(pp, 0);
for dOff = 0:(100-duty)
sleep(delay);
endfor
endfor
# Reverse order
dRange = dRange(end:-1:1);
endwhile
pp_close(pp);
|
Run it and you should see LEDs changing brightness from lowest to maximum.
Example 3: Logic analyzer
We can surely make something more interesting, right? Enter basic logic analyzer.
Assume you are working with [this] temperature sensor. Something is not right. You do not have one of those expensive logic analyzers, but you do have a Parallel port! You remember that someone made a package for interfacing it with GNU Octave. So you connect your probes to appropriate Data port terminals and change settings accordingly. In this example:
Probe | Port terminal |
---|---|
!CS | DATA0 |
SCK | DATA1 |
SO | DATA2 |
NB: Parallel ports usually have weak pull-ups to +5V even when in "input" mode, so do not do this if unsure. One could potentially use different terminals in Control/Status ports to get true high-impedance inputs.
And write a simple script below:
Code: logic_analyzer.m |
#####################################################################
# Settings
#####################################################################
# Channels to capture
#channels = [0, 1, 2, 3, 4, 5, 6, 7];
channels = [2, 1, 0];
# Channel labels
#channel = {"CH0"; "CH1"; "CH2"; "CH3"; "CH4"; "CH5"; "CH6"; "CH7"};
channel = {"SO"; "SCK"; "!CS"};
# Trigger channel
triggerCh = 0;
# When to trigger
trigger = 0; # Capture on low. For high - 1
#####################################################################
samplesTime = [];
samplesValue = [];
#pp_close(pp);
pp = parallel("/dev/parport0", 1);
printf("Waiting for trigger...\n");
fflush(stdout);
data = pp_data(pp);
while (bitget(data, triggerCh + 1) != trigger)
oldData = data;
data = pp_data(pp);
endwhile
printf("Capturing...\n");
fflush(stdout);
startTime = time();
samplesTime(end + 1) = 0;
samplesValue(end + 1) = oldData;
while (bitget(data, triggerCh + 1) == trigger)
data = pp_data(pp);
samplesTime(end + 1) = time() - startTime;
samplesValue(end + 1) = data;
endwhile
# Statistics
printf("Average sample rate: %f kHz\n", size(samplesValue)(2) / samplesTime(end) / 1000.0);
pp_close(pp);
# Plotting
figure;
for p = 1:size(channels)(2)
subplot (size(channels)(2), 1, p)
plot(samplesTime, bitget(samplesValue, channels(p) + 1))
ylabel(channel{p});
axis([-0.01, samplesTime(end)+ 0.01, -1, 2], "manual");
set(gca(), 'ytick', -1:2);
set(gca(), 'yticklabel', {''; '0'; '1'; ''});
endfor
xlabel ("t");
|
If connections and settings are correct you should see something like this:
Now you can fully debug what is going with your hardware from comfort of GNU Octave.
i2c
i2c
TCP
Example: basic use
For testing you could start a tcp listener
$ socat TCP-LISTEN:8000 -
Now you can connect your listener.
Code: TCP example |
# Open TCP connection to 127.0.0.1:8000
t0 = tcp("127.0.0.1",8000)
# write to listener
tcp_write(t0, "Hello world!")
# Blocking read call, returns uint8 array of exactly 12 bytes read
data = tcp_read(t0, 12)
# Convert uint8 array to string,
char(data)
|
There are several ways to set timeout of read call.
Code: set TCP timeout |
# Open TCP connection to 127.0.0.1:8000 with timeout of 100 ms
t0 = tcp("127.0.0.1",8000,100)
# set timeout to blocking
tcp_timeout(t0, -1)
# the timeout can be overwritten for single read call, in this case 1000ms
data = tcp_read(t0, 12, 1000)
# close tcp session
tcp_close(t0)
|
USBTMC
Configuring interface
Recent linux kernels support USBTMC out of the box. Connect your instrument and check if /dev/usbtmc* exists. Set appropriate permissions to /dev/usbtmc*
Example: basic use
Code: USBTMC example |
# Open interface to USB instrument
t0 = usbtmc('/dev/usbtmc0')
# write to listener
usbtmc_write(t0, '*IDN?')
# Blocking read call, returns uint8 array
data = usbtmc_read(t0, 10000)
# Convert uint8 array to string,
char(data)
# close usbtmc session
usbtmc_close(t0)
|
VXI11
Example: basic use
Code: VXI11 example |
# Open VXI11 connection to 192.168.100.100
t0 = vxi11('192.168.100.100')
# write to listener
vxi11_write(t0, '*IDN?')
# read from instrument, returns uint8 array
data = vxi11_read(t0, 10000)
# Convert uint8 array to string,
char(data)
# close usbtmc session
vxi11_close(t0)
|
Limitations
For now,
- it's not possible to connect more than one instrument per IP address (e.g. VXI11-GPIB gateways)
- only instrument _inst0_ can be connected
GPIB
Configuring interface
For using GPIB you need to install and configure the linux-gpib kernel modules and libraries.
Example: basic use
Code: GPIB example |
# Open GPIB instrument with ID 7 and set timeout to 1 second (see ibtmo / NI-488.2 Function Reference)
t0 = gpib(7,11)
# write to listener
gpib_write(t0, '*IDN?')
# read from instrument, returns uint8 array
data = gpib_read(t0, 10000)
# Convert uint8 array to string,
char(data)
# close usbtmc session
gpib_close(t0)
|
Limitations
- setting minor, sad, send_eoi and eos_mode not implemented yet