Switching ADC (ads1220) input channels in run-time

Thread Starter

esha123

Joined Jun 10, 2019
1
Hi everyone,

The ads1220 ADC by TI has 3 analog input lines, AIN0, AIN1, AIN2. I am using a micro-controller (I've established 4 wire SPI communication bw the controller and ADC) to read these three inputs alternately so that I can switch bw different input lines and send respective data to my app.

The TI datasheet for ADS1220 says that the registers have to be re-written (reconfigured) in order for the ADC to switch from AIN0 to AIN1. When I try doing this in run time when the ADC is in single-shot mode, my program does not work. I receive all highs as all my input data (i.e. in hex I receive "FFF..." on all channels which I think means that my ADC is not getting enough time to perform its conversions).

When I put my ADC in continuous conversion mode instead of single-shot mode, and then re-write my register settings in run time to switch bw different inputs, everything works fine.


My question is: Is there a way to re-write to the ADC registers and get data from multiple channels in one-shot mode? Because the continuous conversion mode uses a lot of power and this isn't the most power efficient way to run my application... If anyone who has used this ADC before knows another way I can switch from AIN0 to AIN1 or AIN2 during run time (other than re-writing to my CONFIG Registers) please do let me know.

Thank you.
 

JohnInTX

Joined Jun 26, 2012
4,787
I am familiar with the I2C version of this ADC in the single shot mode. The SPI is a bit different but here goes:

After selecting the MUX values you have to send command 08h to start the conversion.
The end of conversion is indicated by the dedicated DRDY/ line and optionally by the SPI DOUT (MISO) line as determined by the DRDYM bit in Register 3.
If you're using DOUT, you have to set the CE/ = 0 to enable DOUT and poll for EOC (DOUT==0).
When DOUT==0, you can clock in the data via SPI then raise CE/ to end the transaction.
You can also just send the RDATA command and read the last conversion. If you hold CE/ low, the final value of DOUT (MISO) will indicate if a conversion completed while you were reading the data and you should read it again.

My understanding is that the data register should always be the last complete conversion even if you changed the input MUX so I would think that all FFFFFFh would indicate an SPI problem - not dropping CE/?
Also, double check your MUX settings to be sure you aren't railing the inputs.

Is there a way to re-write to the ADC registers and get data from multiple channels in one-shot mode? Because the continuous conversion mode uses a lot of power and this isn't the most power efficient way to run my application... If anyone who has used this ADC before knows another way I can switch from AIN0 to AIN1 or AIN2 during run time (other than re-writing to my CONFIG Registers) please do let me know.
Not that I know of. We are replacing an ADC that does sequence channels automatically but not this one. I had to write a little sequencer for the one-shot mode to send MUX settings, start convert, poll for EOC, read, next MUX etc.. In the continuous mode, any register write like changing the MUX channel starts a new conversion but I used explicit commands in the one-shot mode.

Welcome to AAC by the way.
Good luck!
 
Last edited:

nsaspook

Joined Aug 27, 2009
16,271
I think I used the ADS1220 in single-shot mode (it's the default) for my RPi driver for the comedi DAQ system but here is a link to the code.

https://github.com/nsaspook/daq_gert/blob/master/daq_gert.c

Defined but not used for configuration register 1.
C:
// Define CM (conversion mode)
#define ADS1220_CC 0x04

//  configuration register 1
static const uint8_t ads1220_r1 = ADS1220_DR_20 | ADS1220_MODE_TURBO;
C:
            if (daqgert_conf == 4 || daqgert_conf == 14) { /* ads1220 mode */
                /*
                * setup ads1220 registers
                */
                pdata->one_t.len = 5;
                pdata->tx_buff[0] = ADS1220_CMD_WREG + 3;
                pdata->tx_buff[1] = ads1220_r0;
                pdata->tx_buff[2] = ads1220_r1; // usage in ADC setup
                pdata->tx_buff[3] = ads1220_r2;
                pdata->tx_buff[4] = ads1220_r3;
                spi_message_init_with_transfers(&m,
                    &pdata->one_t, 1);
                pdata->slave.spi->max_speed_hz = daqgert_devices[ads1220].max_speed_hz;
                pdata->slave.spi->mode = daqgert_devices[ads1220].spi_mode;
                spi_setup(pdata->slave.spi);
                spi_bus_lock(pdata->slave.spi->master);
                spi_sync_locked(pdata->slave.spi, &m);
                spi_bus_unlock(pdata->slave.spi->master);
                usleep_range(40, 50);
                pdata->one_t.len = 5;
                pdata->tx_buff[0] = ADS1220_CMD_RREG + 3;
                pdata->tx_buff[1] = 0;
                pdata->tx_buff[2] = 0;
                pdata->tx_buff[3] = 0;
                pdata->tx_buff[4] = 0;
                spi_message_init_with_transfers(&m,
                    &pdata->one_t, 1);
                spi_bus_lock(pdata->slave.spi->master);
                spi_sync_locked(pdata->slave.spi, &m);
                spi_bus_unlock(pdata->slave.spi->master);
                usleep_range(40, 50);
                /*
                * Check to be sure we have a device
                */
                pdata->slave.device_detect = pdata->rx_buff[2];
                if ((pdata->rx_buff[1] != ads1220_r0) ||
                    (pdata->rx_buff[2] != ads1220_r1)) {
                    dev_err(dev->class_dev,
                        "ADS1220 configuration error: %x %x %x %x\n",
                        pdata->rx_buff[1], pdata->rx_buff[2],
                        pdata->rx_buff[3], pdata->rx_buff[4]);
                    return -ENODEV;
                }
                pdata->one_t.len = 1;
                pdata->tx_buff[0] = ADS1220_CMD_SYNC;
                spi_message_init_with_transfers(&m,
                    &pdata->one_t, 1);
                spi_bus_lock(pdata->slave.spi->master);
                spi_sync_locked(pdata->slave.spi, &m);
                spi_bus_unlock(pdata->slave.spi->master);
            }
C:
static void daqgert_ai_set_chan_range_ads1220(struct comedi_device *dev,
    struct comedi_subdevice *s,
    uint32_t chanspec)
{
    struct daqgert_private *devpriv = dev->private;
    uint32_t range = CR_RANGE(chanspec);
    uint32_t chan = CR_CHAN(chanspec);
    uint32_t cMux;

    /*
    * convert chanspec to input MUX switches/gains if needed
    * we could just feed the raw bits to the Mux if needed
    */

    if ((devpriv->ai_chan != chan) || (devpriv->ai_range != range)) {
        switch (chan) {
        case 0:
            cMux = ADS1220_MUX_0_1;
            break;
        case 1:
            cMux = ADS1220_MUX_2_3;
            break;
        case 2:
            cMux = ADS1220_MUX_2_G;
            break;
        case 3:
            cMux = ADS1220_MUX_3_G;
            break;
        case 4:
            cMux = ADS1220_MUX_DIV2;
            break;
        default:
            cMux = ADS1220_MUX_0_1;
        }
        cMux |= ((range & 0x03) << 1); /* setup the gain bits for range with NO pga */
        cMux |= ads1220_r0_for_mux_gain;
        ADS1220WriteRegister(ADS1220_0_REGISTER, 0x01, &cMux, s);
    }
}
C:
    /* Make SPI messages for the type of ADC are we talking to */
    switch (devpriv->ai_spi->device_type) {
    case ads1220:
        /* read the ads1220 3 byte data result */
        pdata->one_t.len = 4;
        pdata->one_t.cs_change = false;
        pdata->one_t.delay_usecs = 0;
        pdata->tx_buff[0] = ADS1220_CMD_RDATA;
        pdata->tx_buff[1] = 0;
        pdata->tx_buff[2] = 0;
        pdata->tx_buff[3] = 0;
        spi_message_init_with_transfers(&m,
            &pdata->one_t, 1);
        spi_bus_lock(spi->master);
        spi_sync_locked(spi, &m);
        spi_bus_unlock(spi->master);
        val = pdata->rx_buff[1];
        val = (val << 8) | pdata->rx_buff[2];
        val = (val << 8) | pdata->rx_buff[3];

        /* mangle the data as necessary */
        /* Bipolar Offset Binary */
        val &= 0x0ffffff;
        val ^= 0x0800000;

        sync = ADS1220_CMD_SYNC;
        spi_write(spi, &sync, 1);
        devpriv->ai_count++;
        break;
The defines/constants and routines are in the code link.
 
Top