Click here to Skip to main content
15,881,424 members
Articles / Programming Languages / C#

How to perform Serial Peripheral Interface (SPI) communication with a data acquisition device using C# and the National Instruments DAQmx API.

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
23 Jul 2015CPOL3 min read 18.3K   467   8  
A class to implement SPI in C# using DAQmx C api and pInvoke.

Introduction

While developing an automated testing platform that makes use of a National Instruments USB-6210 Data Acquisition device (DAQ) to perform measurements on a panel of devices; I wanted to employ some kind of test fixture command and control using the DAQ's digital output and input lines.

Figure1

 

Figure 1: NI USB-6210 Data Acquisition device

 

The NI USB-6210 has 4 digital inputs and 4 outputs available so I decided to implement a four wire Serial Peripheral Interface (SPI) bus with the DAQ as the master and two slave select lines. While researching SPI I found a useful article on the topic along with some source code examples that I was able to adapt to a C# class that I could use in my project. Here [^] is a link to that article.

This SPI Class

The class constructor provides overloads for specifying the output port (the four output lines), the input line, clock phase, and clock polarity. In addition to this; the class exposes several Properties and methods.

Public Members

  • Constructor overloads
    • daqSPI() - Instantiate class with the following defaults:
    • ouputLines = "Dev0/port1"
    • inputLine = "Dev0/port0/line3"
    • cpol = ClockPolarity.ONE
    • cpha = ClockPhase.LOGICAL_ZERO
    • daqSPI(string outputLines, string inputLine) - Specify the output and input lines.
    • daqSPI(string outputLines, string inputLine, ClockPolarity cpol, ClockPhase cpha) - Specify the output and input lines, as well as the polarity and phase of the clock pulse.
  • Methods
    • Dispose() - Explicitly clean up unmanaged resources employed by the class.
    • Transfer(byte inByte) - Simultaneously write a byte to the bus and read a byte from the bus.
  • Properties
    • SCKPhase - Get or Set the edges of the clock on which a slave latches input-data bits and shifts out bits of output data.
    • SCKPolarity - Get or Set the level of the serial clock line before and after byte transfer.
    • SS0 - Get or Set the SS0 (slave 0 select) wire bit.
    • SS1 - Get or Set the SS1 (slave 1 select) wire bit.
    • Timeout - Get or Set the read and write operation timeout.
  • Enumerations
    • ClockPolarity - Specifies the level of the SCK line before and after byte transfer.
      • ZERO
      • ONE
    • ClockPhase - Specifies the edges of the clock on which a slave latches input-data bits and shifts out bits of output data.
      • LOGICAL_ZERO
      • LOGICAL_ONE

Using the class

This class makes use of several NI-DAQmx C API calls. These are found in a Native wrapper class, containing PInvoke signatures, that I put together and included with this project (DAQmx.cs).

The code below instructs the DAQ to send some purple prose [^] to slave device 0, using the default SPI configuration, while simultaneously printing response bytes to the console.

C#
static void Main(string[] args)
{
    try
    {
        daqSPI spi = new daqSPI("Dev1/port1", "Dev1/port0/line3", 
                                 daqSPI.ClockPolarity.ONE, daqSPI.ClockPhase.LOGICAL_ZERO);

        byte[] data = ASCIIEncoding.Default.
            GetBytes("It was a dark and stormy night; the rain fell in torrents, except at " +
        "occasional intervals, when it was checked by a violent gust of wind " +
        "which swept up the streets (for it is in London that our scene lies), " +
        "rattling along the house-tops, and fiercely agitating the scanty flame " +
        "of the lamps that struggled against the darkness. Through one of the " +
        "obscurest quarters of London, and among haunts little loved by the " +
        "gentlemen of the police, a man, evidently of the lowest orders, was " +
        "wending his solitary way.");

        spi.SS0 = true; //Selects SS0 deselects SS1

        foreach (byte b in data)
        {
            Console.Write((char) spi.Transfer(b));
        }
        Console.WriteLine();
    }
    catch (Exception ex)
    {
        Console.WriteLine("DAQmx Error: {0}", ex.ToString());
    }

    Console.WriteLine("End of program, press Enter key to quit");
    Console.Read();
}

Alas slave device 0 is illiterate and doesn't appreciate good verse. However my Tektronix TDS 420 captured one of the bytes being transmitted.

Figure2

 

Figure 2: Byte transfer captured by the TDS 420 Oscilloscope (image obtained by means of Instrument Snap Shot [^] ).

 

  • Line 1 is the data line
  • Line 2 is the clock
  • Line 3 is SS0 (low = selected)
  • Line 4 is SS1 (unselected)

Notice the clock frequency: this is close to 300 baud and about as fast as I can drive the DAQ. It is fast enough to transfer short command strings, but a bit slow for moving a lot of data. It is however, good enough for my application.

Figure3

 

Figure 3: Old fashioned 300 Baud data transfer.

 

The Demo

The demo is something that I put together to test the SPI class while creating it. The following figure shows the NI USB-6210 with the lines I use labeled. While testing I simply looped the MOSI line to the MISO line.

Figure4

 

Figure 4: Lines used for the 4 wire bus plus power and ground.

 

This class and demo should work with any DAQ that has at least 4 digital output lines and one digital input line by simply supplying the appropriate addresses to the class constructor.

History

  • July. 23, 2015: Version 1.0.0.0.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --