Click or drag to resize

4.4.4 ADC Adapter Programming

How to collect sample data from an ADC Adapter by making use of the Trace Buffer facility.

Note  Note

The ADC Adapter will not be configured automatically at power on (in contrast to, for instance, the XY2-100 Adapter).

If the red "Conf Pending" LED is on, the adapter has not yet been configured, which is necessary before the adapter can be used to gather data.

Configuration procedures for the ADC Adapter are described in detail in 4.4.3 ADC Adapter Configuration.

Acquiring ADC-Adapter samples via the Trace Buffer
C#
/// <summary>
/// A convenient container for ADC sample values.
/// </summary>
public class AdcSample
{
    public int Channel0 { get; set; }
    public int Channel1 { get; set; }
    public int Channel2 { get; set; }
    public int Channel3 { get; set; }

    public override string ToString()
    {
        return string.Format( $"Ch0: {Channel0}, Ch1: {Channel1}, Ch2: {Channel2}, Ch3: {Channel3}" );
    }

    public static double ConvertToVolts( int channelValue )
    {
        return ( channelValue * 5.0 ) / 32767;
    }
}

/// <summary>
/// Acquire samples from an ADC-Adapter.
/// </summary>
/// <param name="samples">Buffer for the acquired samples.</param>
/// <param name="sampleIndex">Index of the buffer location for the first acquired sample.</param>
/// <param name="requestedSampleCount">How many samples to acquire.</param>
/// <param name="client">A ClientAPI instance which is connected to a SP-ICE-3 card with an ADC-Adapter.</param>
/// <returns>The number of samples actually acquired.</returns>
public int AcquireADCSamples( AdcSample[] samples, int sampleIndex, int requestedSampleCount, ClientAPI client )
{
    int sampleCount = 0; // How many ADC samples we actually capture.

    using ( TraceBufferClient tbClient = new TraceBufferClient( client.Address.ToString() ) )
    {
        TraceBufferConfig tbConfig = new TraceBufferConfig();
        // The ADC-Adapter sends sample data via SPI0.
        tbConfig.ControlEvents.Spi0Rx = true;
        tbClient.SetConfig( tbConfig );

        // 
        // Set up a buffer for reading TraceEvents.
        // 
        const int traceEventBufferSize = 1024;
        TraceEvent[] traceEvents = new TraceEvent[traceEventBufferSize];

        // 
        // The TraceBuffer protocol can carry a maximum payload of 32 bits per event.
        // The ADC-Adapter converts all four channels simultaneously at 16 bit resolution per channel.
        // Consequently two adjacent TraceBuffer events are required to transport the
        // complete sample data for the four ADC channels.
        // 
        // Due to the configuration of the SPI0 module, the difference between the Timestamps
        // of a pair of adjacent TraceEvents is always less than half of a sample-period (i.e. 10/2µs), and
        // this can be used to distinguish the events that belong together as a pair.
        // 
        // The clock-tick of the TraceEvent.Timestamp is 1/64µs, hence:
        const ulong HALF_SAMPLE_PERIOD_TIMESTAMP_TICKS = 64 * 10 / 2;

        // 
        // Remember timestamp of preceding TraceEvent for comparison when gathering event pairs.
        // 
        ulong? prevEventTimestamp = null;

        // 
        // The ADC-Adapter sends the channel sample pairs in the order (CH0 & CH1), (CH2 & CH3).
        // 
        // If the (CH2 & CH3) event is seen first, we skip it, because we need to start with a
        // (CH0 & CH1) event so that we don't get the channels confused.
        // 
        bool seekFirstEventOfPair = true;

        // 
        // Acquire samples until we have the amount requested, or no more TraceEvents are available.
        // 
        int traceEventCount;
        do
        {
            traceEventCount = tbClient.TryRead( traceEvents, 0, traceEventBufferSize );

            for ( int i = 0; i < traceEventCount; i++ )
            {
                TraceEvent traceEvent = traceEvents[i];

                // 
                // Only Spi0Rx events are of interest, because they contain the ADC-Adapter sample data.
                // 
                if ( traceEvent.EventType != TraceEventType.Spi0Rx )
                    continue;

                // 
                // Use the very first event in the buffer to set prevTimestamp to a valid value.
                // 
                if ( prevEventTimestamp.HasValue )
                {
                    if ( ( traceEvent.Timestamp - prevEventTimestamp ) > HALF_SAMPLE_PERIOD_TIMESTAMP_TICKS )
                    {
                        // 
                        // The current event is the first of a pair.
                        // 
                        seekFirstEventOfPair = false;

                        // 
                        // Each ADC channel sample is retrieved as an integer in the range -32768 .. +32767, representing -5V .. +5V.
                        // 
                        samples[sampleCount + sampleIndex].Channel0 = (Int32)( ( traceEvent.Value & 0xFFFF0000 ) >> 16 );  // Channel 0 Value
                        samples[sampleCount + sampleIndex].Channel1 = (Int32)( traceEvent.Value & 0x0000FFFF );            // Channel 1 Value
                    }
                    else
                    {
                        if ( seekFirstEventOfPair )
                            // We have not yet seen the first event of the pair.
                            continue;

                        // 
                        // The current event is the second of a pair.
                        // 
                        samples[sampleCount + sampleIndex].Channel2 = (Int32)( ( traceEvent.Value & 0xFFFF0000 ) >> 16 );  // Channel 2 Value
                        samples[sampleCount + sampleIndex].Channel3 = (Int32)( traceEvent.Value & 0x0000FFFF );            // Channel 3 Value

                        ++sampleCount;
                    }
                }
                prevEventTimestamp = traceEvent.Timestamp;
            }
        }
        while ( ( traceEventCount > 0 ) && ( sampleCount < requestedSampleCount ) );
    }
    return sampleCount;
}

/// <summary>
/// Just an illustration of one way in which AcquireADCSamples() could be used.
/// </summary>
public void DoSomethingWithAdcSamples()
{
    const string CardIP = "169.254.1.1"; // insert your card's IP address here

    ClientAPI client = new ClientAPI( CardIP );

    AdcSample[] adcSamples = new AdcSample[10];

    int numAdcSamples = AcquireADCSamples( adcSamples, 0, adcSamples.Length, client );

    for ( int n = 0; n < numAdcSamples; ++n )
    {
        Console.WriteLine( $"ADC sample {n}: {adcSamples[n]}" );

        const double TTL_HIGH_THRESHOLD_VOLTAGE = 2.0;

        bool isHigh = AdcSample.ConvertToVolts( adcSamples[n].Channel3 ) > TTL_HIGH_THRESHOLD_VOLTAGE;

        Console.WriteLine( $"Channel 3: TTL signal is {( isHigh ? "definitely HIGH" : "probably LOW" )}" );
    }
}