Home : Platform Support Library | XS1 U-series multi-channel ADC

Description

<TODO>

Interface Functions

<TODO>

Example Usage

<TODO>

Source Files

xsystem_xs1u_adc.h XS1 U-series multi-channel ADC interface
xsystem_xs1u_adc.xc XS1 U-series multi-channel ADC implementation

Source Listings

xsystem_xs1u_adc.h

// The U-series device has a 12-bit 1MSample/second Successive Approximation Register (SAR) Analogue to Digital
// Converter (ADC). It has 4 input pins which are multiplexed into the ADC. The sampling of the ADC is controlled using
// GPIO pin X0D24 that is triggered either by writing to port 1I, or by driving the pin externally. On each rising edge
// of the sample pin the ADC samples, holds and converts the data value from one of the analog input pins. Each of the
// 4 inputs can be enabled individually. Each of the enabled analog inputs is sampled in turn, on successive rising
// edges of the sample pin. The data is transmitted to the channel-end that the user configures during initialization
// of the ADC. Data is transmitted over the channel in individual packets, or in packets that contain multiple
// consecutive samples. The ADC uses an external reference voltage, nominally 3V3, which represents the full range of
// the ADC.

#ifndef XSYSTEM_XS1U_ADC__INCLUDED
#define XSYSTEM_XS1U_ADC__INCLUDED

// These must be defined in "xconfig.h" ...
//
// XSYSTEM_XS1U_ADC__PERIPHERAL_TILE    Integer, XS1 tile number
// XSYSTEM_XS1U_ADC__TRIGGER_MODE       1, 2, 3, or 4 (see trigger modes below)

#include "xsystem_types.h"

#define XSYSTEM_XS1U_ADC__TRIGGER_MODE__AUTOMATIC   1  // Invoke a trigger during a sample read by writing to XS1_PORT_1I
#define XSYSTEM_XS1U_ADC__TRIGGER_MODE__INTERNAL    3  // Trigger using internally declared trigger port (XS1_PORT_1I)
#define XSYSTEM_XS1U_ADC__TRIGGER_MODE__EXTERNAL    4  // Trigger using externally declared trigger port (XS1_PORT_1I)

void xsystem_xs1u_adc__initialize( chanend data_channel, int sample_width, int channel_mask );
void xsystem_xs1u_adc__enable    ( void );
void xsystem_xs1u_adc__disable   ( void );
void xsystem_xs1u_adc__read      ( chanend data_channel, unsigned int sample_data[4] );

// ====================================================================================================================
// ====================================================================================================================

#endif

xsystem_xs1u_adc.xc

// The U-series device has a 12-bit 1MSample/second Successive Approximation Register (SAR) Analogue to Digital
// Converter (ADC). It has 4 input pins which are multiplexed into the ADC. The sampling of the ADC is controlled using
// GPIO pin X0D24 that is triggered either by writing to port 1I, or by driving the pin externally. On each rising edge
// of the sample pin the ADC samples, holds and converts the data value from one of the analog input pins. Each of the
// 4 inputs can be enabled individually. Each of the enabled analog inputs is sampled in turn, on successive rising
// edges of the sample pin. The data is transmitted to the channel-end that the user configures during initialization
// of the ADC. Data is transmitted over the channel in individual packets, or in packets that contain multiple
// consecutive samples. The ADC uses an external reference voltage, nominally 3V3, which represents the full range of
// the ADC.

#include "xsystem_xs1u_adc.h"
//#include "xconfig.h"

#ifdef XSYSTEM_XS1U_ADC

#include <print.h>
#include <xs1.h>
#include <xclib.h>
#include <platform.h>
#include <xs1_su.h>

#ifndef XSYSTEM_XS1U_ADC__TRIGGER_MODE
#error "XSYSTEM_XS1U_ADC__TRIGGER_MODE not defined"
#endif

static unsigned int xsystem_xs1u_adc__configuration[5][1] = {{0},{0},{0},{0},{0}};
static unsigned int xsystem_xs1u_adc__sample_format = 0;
static unsigned int xsystem_xs1u_adc__channel_mask = 0;
static unsigned int xsystem_xs1u_adc__enabled_flag = 0;

#if XSYSTEM_XS1U_ADC__TRIGGER_MODE == XSYSTEM_XS1U_ADC__TRIGGER_MODE__INTERNAL

    on tile[0] : out port xsystem_xs1u_adc__trigger_port = XS1_PORT_1I;

#endif

static inline unsigned _chanend_res_id( chanend c )
{
    unsigned int id;
    asm("mov %0, %1" : "=r"(id): "r"(c));
    return id;
}

#if XSYSTEM_XS1U_ADC__TRIGGER_MODE == XSYSTEM_XS1U_ADC__TRIGGER_MODE__INTERNAL

void _xsystem_xs1u_adc__trigger( void )
{
   unsigned time;
   xsystem_xs1u_adc__trigger_port <: 1 @ time;
   time += 40; // Ensure 1 is held for >400ns
   xsystem_xs1u_adc__trigger_port @ time <: 0x80000;
   time += 40; // Ensure 0 is held for >400ns
   xsystem_xs1u_adc__trigger_port @ time <: 0x00000;
}

#endif

void xsystem_xs1u_adc__initialize( chanend data_channel, int sample_width, int channel_mask )
{
    int count = 0;

    xsystem_xs1u_adc__channel_mask = channel_mask;
    switch( sample_width )
    {
        case 1: xsystem_xs1u_adc__sample_format = 0; break;
        case 2: xsystem_xs1u_adc__sample_format = 1; break;
        case 4: xsystem_xs1u_adc__sample_format = 3; break;
        default: /*ASSERT*/ break;
    }

    // Drive trigger port low to ensure calibration pulses are all seen
    // Perform the ADC calibration - requires a number of initial pulses (6 for the U-series)
    #if XSYSTEM_XS1U_ADC__TRIGGER_MODE == XSYSTEM_XS1U_ADC__TRIGGER_MODE__INTERNAL
    xsystem_xs1u_adc__trigger_port <: 0;
    #endif

    // Configure ADC Inputs
    // Write each ADC input channel control register
    for( int ii = 0; ii < 4; ++ii )
    {
        xsystem_xs1u_adc__configuration[ii][0] = 0;
        if( channel_mask & (1<< ii) ) {
            xsystem_xs1u_adc__configuration[ii][0] = 1 | (_chanend_res_id(data_channel) & ~0xff);
            ++count;
        }
    }

    // Configure ADC - Write the ADC general control register
    xsystem_xs1u_adc__configuration[4][0] = 0;
    if( channel_mask != 0 ) xsystem_xs1u_adc__configuration[4][0] |= 0x01;
    //if( use_internal_ref != 0 ) xsystem_xs1u_adc__configuration[4] |= 0x02;
    xsystem_xs1u_adc__configuration[4][0] |= (xsystem_xs1u_adc__sample_format & 3) << 16;
    xsystem_xs1u_adc__configuration[4][0] |= (count & 255) << 8;
    printhexln( xsystem_xs1u_adc__configuration[4][0] );
}

void xsystem_xs1u_adc__enable( void )
{
    unsigned int data[1] = {0};

    // Ensure that the global configuration is disabled, otherwise the individual ADC config regs are read-only
    write_periph_32( usb_tile, 2, 0x20, 1, data );

    // Drive trigger port low to ensure calibration pulses are all seen
    // Perform the ADC calibration - requires a number of initial pulses (6 for the U-series)
    #if XSYSTEM_XS1U_ADC__TRIGGER_MODE == XSYSTEM_XS1U_ADC__TRIGGER_MODE__INTERNAL
    xsystem_xs1u_adc__trigger_port <: 0;
    #endif

    // Configure each ADC input
    write_periph_32( usb_tile, 2, 0x00, 1, xsystem_xs1u_adc__configuration[0] );
    write_periph_32( usb_tile, 2, 0x04, 1, xsystem_xs1u_adc__configuration[1] );
    write_periph_32( usb_tile, 2, 0x08, 1, xsystem_xs1u_adc__configuration[2] );
    write_periph_32( usb_tile, 2, 0x0C, 1, xsystem_xs1u_adc__configuration[3] );

    // Configure ADC - Write the ADC general control register
    write_periph_32( usb_tile, 2, 0x20, 1, xsystem_xs1u_adc__configuration[4] );

    // Perform the ADC calibration - requires a number of initial pulses (6 for the U-series)
    #if XSYSTEM_XS1U_ADC__TRIGGER_MODE == XSYSTEM_XS1U_ADC__TRIGGER_MODE__INTERNAL
    for( int i = 0; i < 6; i++ ) _xsystem_xs1u_adc__trigger();
    #endif

    xsystem_xs1u_adc__enabled_flag = 1;
}

void xsystem_xs1u_adc__disable( void )
{
    unsigned data[1] = {0};
    write_periph_32(usb_tile, 2, 0x20, 1, data);
    xsystem_xs1u_adc__enabled_flag = 0;
}

void xsystem_xs1u_adc__read( chanend data_channel, unsigned int sample_data[4] )
{
    if( !xsystem_xs1u_adc__enabled_flag )
    {
        sample_data[0] = sample_data[1] = sample_data[2] = sample_data[3] = 0;
        return;
    }
    for( int ii = 0; ii < 4; ii++ )
    {
        if( xsystem_xs1u_adc__channel_mask & (1<<ii) )
        {
            #if XSYSTEM_XS1U_ADC__TRIGGER_MODE == XSYSTEM_XS1U_ADC__TRIGGER_MODE__INTERNAL
            _xsystem_xs1u_adc__trigger();
            #endif
            // Wait for sample data to arrive via the channel
            if( testct(data_channel) ) chkct( data_channel, XS1_CT_END );

            // Extract sample data from channel
            switch( xsystem_xs1u_adc__sample_format )
            {
                case 0: sample_data[ii] = inuchar( data_channel );
                break;
                case 1: sample_data[ii]  = inuchar( data_channel ) << 8;
                        sample_data[ii] |= inuchar( data_channel );
                        sample_data[ii] >>= 4;
                break;
                case 3: sample_data[ii] = inuint( data_channel );
                        sample_data[ii] >>= 20;
                break;
            }
        }
        else sample_data[ii] = 0;
    }
    chkct( data_channel, XS1_CT_END );
}

#endif