Interfacing Hardware with Mathematica

Supplement

Zhe Hu
huzhe@iit.edu

Using PicoScope (3206)

It's just not electronic enough, if the mighty oscilloscope isn't involved. As a supplement to the "Interface Hardware with Mathematica", this notebook demonstrates how to control a USB oscilloscope from within Mathematica.

PicoScope, from my experience, can be very tricky to use, especially if you want to control it by your own software. It often ends up that you have to restart your computer to recover from some deadlock condition that makes your program fail to release the resources. Take a look at the forum on PicoScope's website, you can see how frustrated people get trying to use this little, inexpensive oscilloscope.

Again with Mathematica's .NetLink technology, you can control this piece of equipment in the Mathematica environment step by step.

Here I pick PicoScope 3206, simply because it's the one available in the lab. More importantly, it's because all of these devices have the device drivers and the essential DLL (in this case, ps3000.dll) freely downloadable from the manufacturer's website. If you are using a different model (say 2000 series), please check its programming manual and the example programs, especially the accompanying LabView programs, where the functions in the DLL are explained and declared. Then you shall know how to change the following function definitions accordingly.

Now let's import these functions from "ps3000.dll" into Mathematica. (check the manual for details)

Import the DLL functions from ps3000.dll

Again make sure MS-Windows can find the DLL file "ps3000.dll".

Needs["NETLink`"]

\(ps3000OpenUnit = 
      DefineDLLFunction["ps3000_open_unit", "ps3000.dll", "short", {}]\)

\(ps3000GetUnitInfo = 
      DefineDLLFunction["ps3000_get_unit_info", "ps3000.dll", "short", {"short", "System.Text.StringBuilder", "short", "short"}]\)

\(ps3000FlashLED = 
      DefineDLLFunction["ps3000_flash_led", "ps3000.dll", "short", {"short"}]\)

\(ps3000SetChannel = 
      DefineDLLFunction["ps3000_set_channel", "ps3000.dll", "short", {"short", "short", "short", "short", "short"}]\)

\(ps3000GetTimebase = 
      DefineDLLFunction["ps3000_get_timebase", "ps3000.dll", "short", {"short", "short", "long", "out long", "out short", \ "short", "out long"}]\)

\(ps3000SetTrigger = 
      DefineDLLFunction["ps3000_set_trigger", "ps3000.dll", "short", {"short", "short", "short", "short", "short", "short"}]\)

\(ps3000RunBlock = 
      DefineDLLFunction["ps3000_run_block", "ps3000.dll", "short", {"short", "long", "short", "short", "long*"}]\)

ps3000Ready = 
      DefineDLLFunction["ps3000_ready", "ps3000.dll", "short", {"\
short"}]

\(ps3000GetValues = 
      DefineDLLFunction["ps3000_get_values", "ps3000.dll", "long", {"short", "short[]", "short[]", "short[]", "short[]", "out short", "long"}]\)

\(ps3000Stop = 
      DefineDLLFunction["ps3000_stop", "ps3000.dll", "short", {"short"}]\)

\(ps3000CloseUnit = 
      DefineDLLFunction["ps3000_close_unit", "ps3000.dll", "short", {"short"}]\)

st = NETNew["System.Text.StringBuilder", 100];

Use the unit

It is assumed that you have installed the device driver for the USB oscilloscope successfully and have it recognized correctly by MS-Windows, as you plug in the oscilloscope onto your computer. Then use the manufactuer's "Picoscope" program to verify that your USB oscilloscope is working. This first step could be the most difficult one. Once you have the oscilloscope connected, now you can open it in Mathematica to get its handle.

handle = ps3000OpenUnit[]

This handle variable will be global to the rest of the function calls. Get the information of my PicoScope3206.

Do[ps3000GetUnitInfo[handle, st, 100, k]; 
      Print@(st@ToString[]), {k, 0, 6}]

Fun to flash the LED, just to show that you are in command.

ps3000FlashLED[handle]

Enable both channel A and B; DC coupled; ±10 V full range

ps3000SetChannel[handle, #, 1, 1, 9] & /@ {0, 1}

Since we have enabled two channels, the fastest timebase of my 3206 unit can only be "1" meaning 100  MS/second, i.e. the time interval between samples is 10  ns.

ps3000GetTimebase[handle, 1, 1000, interval, units, 1, max]

{interval,  units,  max}

The manual said that "units = 1" means that the time units is picosecond. I guess it might mean nanosecond here. The 500  k data points for each channel is good enough for many applications. If the time step is 10  ns, it will cover a time period of 5 ms.

ps3000SetTrigger[handle, 0, (2^8), 0, 0, 0]

PicoScope3206 only has 12-bit A/D converter resolution, but the measured data is normalized and stored in 16-bit. So the raw data points range between -(2^15) to (2^15) - 1. To transform between the raw value to the real voltage value, of course, depends on the voltage range you set earlier. Here (2^8) represents ((2^8)/((2^15) - 1)) 10  V=0.07812738425855281` V . Important: if you use a x10 scope probe, you need to put that scaling factor into account by yourself.

Here we use the "Block" sampling mode. There are other modes: ETS, compatible streaming modes for reading data from the oscilloscope.

ps3000RunBlock[handle, 10000, 1, 1, 10]

We can use a While loop to poll the oscilloscope to see if the data have been collected and ready to be tranfered back to PC through the USB port.

ps3000Ready[handle]

These data points are 16-bit numbers in the array. So prepare the array to accept them. (See .NetLink documentation for details)

{a, b, c, d} = {NETNew["System.Int16[]", 10000], 
          NETNew["System.Int16[]", 10000], 
          NETNew["System.Int16[]", 10000], 
          NETNew["System.Int16[]", 10000]};

ps3000GetValues[handle, a, b, c, d, overflow, 10000]

Check to see if there is an overflow. Adjust the full voltage range of the channel as needed.

overflow

I measured the voltage of an abandoned 9V battery lying dead in the trash can of the lab.

Mean[NETObjectToExpression[a]]/((2^15) - 1. ) 10

Transpose[{Table[
            10*(10^(-9)) k, {k, 0, 
              10000 - 1}], Take[NETObjectToExpression[a], 10000]/((2^15) - 
                  1) 10. }] // 
      ListPlot[#, PlotJoined -> True, Frame -> True, 
          FrameLabel -> {"s", "V"}] &

You can also play with the delayed trigger.

ps3000SetTrigger[handle, 0, (2^8), 0, -50, 0]

ps3000RunBlock[handle, 10000, 1, 1, 10]

ps3000Ready[handle]

ps3000GetValues[handle, a, b, c, d, overflow, 10000]

Transpose[{Table[
            10*(10^(-9)) k, {k, 0, 
              10000 - 1}], Take[NETObjectToExpression[a], 10000]/((2^15) - 
                  1) 10. }] // 
      ListPlot[#, PlotJoined -> True, Frame -> True, 
          FrameLabel -> {"s", "V"}] &

Well someone just took my function generator, otherwise I can show you more waveforms captured by this PicoScope3206. Enough fun, have to go back to do real work now.

At last don't forget to close the unit before you leave.

ps3000CloseUnit[handle]

Links

http://www.picotech.com/