This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RTOS/ADS1118: Best practices for using Tiva C TM4C1294 QSSI with multiple ADS1118 boards

Part Number: ADS1118

Tool/software: TI-RTOS

I'm very new to programming TI processors so I hope my question isn't way off base.

I will be using a configuration that has a Tiva TM4C1294 processor. It will need to acquire data from 4 ADS1118s. Each ADS1118 will be used to acquire data from 2 tranducers. I will be using the QSSI interface on the Tiva processor to communicate with the ADS1118s. I plan on using SSI0Fss through SSI3Fss to chip select each of the ADS1118 boards. I plan on putting each of the ADS1118 boards into continuous sampling mode (set MODE bit to 0). I then plan on acquiring data from each board in a round robin fashion. I assume I can

1. select the ADS1118 I want to sample by setting it's NOT CS low,

2. wait for DOUT/NOT DRDY to be low to indicate a conversion is ready on the selected ADS1118

3. read the value from the ADS1118

It seems to me that if I don't care about the difference in power consumption, eliminating having to trigger starting a sample in single shot mode would be advantageous in sampling 8 TCs per Tiva.

  • The TivaWare PDL has a chapter on the Synchronous Serial Interface (SSI). The TM4C1294 documentation has a section on Quad Synchronous Serial Interface (QSSI). I'm assuming that I'm interfacing to the QSSI for the 4 ADS1118s that I want to interface to. I don't understand how the methods shown in the TivaWare PDL relate to the QSSI. Do I not use the PDL and just access all of the QSSI registers directly?
  • Kenneth,


    I'm not sure I can speak about issues on using the Tiva as a controller. However, I have used the ADS1118 and I would use the device as a series of single-shot conversions instead of running them all as continuous conversions.

    For the ADS1118, the internal oscillator has a variation of ±10%. This makes it difficult to do any set of synchronized (or not-so-synchronized) measurements with multiple devices and multiple channels.

    I would use a set of single-shot conversions. Here's the sequence that I'd run with a set of four ADS1118.

    1. Set /CS1 low, set config register and start new conversion, set /CS1 high.
    2. Set /CS2 low, set config register and start new conversion, set /CS2 high.
    3. Set /CS3 low, set config register and start new conversion, set /CS3 high.
    4. Set /CS4 low, set config register and start new conversion, set /CS4 high.
    5. Wait for 1/(data rate) seconds + 10% for internal oscillation variation.
    6. Set /CS1 low, read data and start new conversion, set /CS1 high.
    7. Set /CS2 low, read data and start new conversion, set /CS2 high.
    8. Set /CS3 low, read data and start new conversion, set /CS3 high.
    9. Set /CS4 low, read data and start new conversion, set /CS4 high.
    10. Repeat starting at #5.

    With this sequence, you get data at regularly spaced intervals. Also, with each start of new conversion, you could be setting a different channel or gain. If you are running with single-shot conversions, and wait a specified time, you don't need to poll for the DOUT/DRDY to return low.

    If you run with continuous conversion mode, you have several devices all running out of sync, and it's harder to keep track of what data is coming out especially when you're making changes to the channel.

    As for the Tiva questions, we've generally not used the QSSI because of limitations in the word length. In the above sequence, I would be using the 32-bit transmission cycle as in Figure 40 or 41. In most of EVMs that use the Tiva, we generally have set GPIOs for communication. I'm not as familiar with the operation of the Tiva as I am with the ADS1118, so for detailed Tiva questions, I'd post them in the "Other microcontrollers forum" here:

    e2e.ti.com/.../908


    Joseph Wu
  • Thank you very much for your reply. I'll try start coding your approach tomorrow and give you feedback as I make progress.
  • I will be sampling 2 inputs on each ADS1118 - AIN0/AIN1 and AIN2/AIN3. How would that change the sequence you suggested to sample from all 8 inputs channels?
  • Kenneth,

    The sequence doesn't change much except that for the start of a new conversions, you write a new configuration to select the input channel. Assuming that AIN0/AIN1 is CH1 and AIN2/AIN3 is CH2, you would set the sequence this way:

    1. Set /CS1 low, set config register CH1 and start new conversion, set /CS1 high.
    2. Set /CS2 low, set config register CH1 and start new conversion, set /CS2 high.
    3. Set /CS3 low, set config register CH1 and start new conversion, set /CS3 high.
    4. Set /CS4 low, set config register CH1 and start new conversion, set /CS4 high.
    5. Wait for 1/(data rate) seconds + 10% for internal oscillation variation.
    6. Set /CS1 low, read data CH1 and start new conversion with CH2, set /CS1 high.
    7. Set /CS2 low, read data CH1 and start new conversion with CH2, set /CS2 high.
    8. Set /CS3 low, read data CH1 and start new conversion with CH2, set /CS3 high.
    9. Set /CS4 low, read data CH1 and start new conversion with CH2, set /CS4 high.
    10. Wait for 1/(data rate) seconds + 10% for internal oscillation variation.
    11. Set /CS1 low, read data CH2 and start new conversion with CH1, set /CS1 high.
    12. Set /CS2 low, read data CH2 and start new conversion with CH1, set /CS2 high.
    13. Set /CS3 low, read data CH2 and start new conversion with CH1, set /CS3 high.
    14. Set /CS4 low, read data CH2 and start new conversion with CH1, set /CS4 high.

    Then you would repeat starting at 5. This would alternate reads between different channels for each ADS1118. Again each read/start conversion is done with a single /CS going low, using the 32-bit data transmission.


    Joseph Wu
  • Why do you indicate to use the 32 bit data transmission. If I refer to TI Doc SBAS457E-October 2010 - Revised October 2015 titled:

    ADS1118 Ultrasmall, Low-Power, SPI-Compatible, 16-Bit Analog-to-Digital Converter with Internal Reference and Temperature Sensor

    Section 9.5.7.1 refers to 32 Bit Data Transmission cycle which returns 16 bits of data and 16 bits of config data and Section 9.5.7.2 refers to 16 Bit Data Transmission cycle which returns 16 bits of data. How would getting 16 bits of config data each cycle be beneficial to me.

  • Kenneth,


    Admittedly, the 32-bit data transmission cycle doesn't by you much except that it does allow you to verify the write of the data. With the read/write of several channels for several devices, I think it helps to have more information available to parse the data. In general, I've always recommended using that method of communication.


    Joseph Wu
  • Base on your suggestion, I've tried to code a test.  I'm using 2 ADS1118 boards located on SSI 0 and 2. I perform 2 samples (ADC and Temperature) for each of the 4 channels I have attached to TCs.

    That code is the 1st snippet below. The second snippet below DOES WORK however it must do "dummy" puts and gets from the boards.

    I prefer to use your example. I know I had to change your pseudo-code loop to sample twice per sample (ADC & Temperature value) but what have I done incorrectly in converting your pseudo-code to actual RTOS code?

    DOES NOT WORK

    void ReadTwoADS1118Boards() {


    uint32_t dataRxADC[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    uint32_t dataRxTEMP[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    uint16_t result1[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    uint16_t result2[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    uint16_t masked[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    float coldJuncTemp[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    float coldJuncMilliVolts[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    float mV[NUM_CHAN_PER_BOARD * NUM_BOARDS];

    // NOTE:
    // The pseudo code list the 4 ADS1118 as 1 - 4
    // It also lists the 2 channels per card as CH1 and CH2
    // CH1 is AIN2 and AIN3
    // CH2 is AIN0 and AIN1
    // We take 2 samples per channel in each loop (1 for the ADC vaule and 1 for the Temperature)

    // Step 1 - BOARD0 CH1 - START ADC CONVERSION
    uint32_t configMask = (ADS118_COMMON & ADS1118_MUX_MASK_TS_MODE_MASK) | ADS1118_MUX_CH1 | ADS1118_TS_MODE_ADC;
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, LOW);
    SSIDataPut(SSI0_BASE, configMask);
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, HIGH);

    // Step 2 - BOARD2 CH1 - START ADC CONVERSION
    configMask = (ADS118_COMMON & ADS1118_MUX_MASK_TS_MODE_MASK) | ADS1118_MUX_CH1 | ADS1118_TS_MODE_ADC;
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, LOW);
    SSIDataPut(SSI2_BASE, configMask);
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, HIGH);

    for (;;) {

    // Step 3 - Wait for conversions to complete 1/64 samples sec + 10 % (>= 17.2 ms)
    SysCtlDelay(ADS1118_DELAY);

    // Step 4 - BOARD0 CH1 - READ PREVIOUS ADC CONVERSION
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, LOW);
    SSIDataGet(SSI0_BASE, &dataRxADC[0]);

    // Step 5 - BOARD0 CH1 - START TEMPERATURE CONVERSION
    configMask = (ADS118_COMMON & ADS1118_MUX_MASK_TS_MODE_MASK) | ADS1118_MUX_CH1 | ADS1118_TS_MODE_TEMP;
    SSIDataPut(SSI0_BASE, configMask);
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, HIGH);

    // Step 6 - BOARD0 CH1 CONVERT TO MV
    result1[0] = dataRxADC[0] & 0x0000FFFF;
    if ( (result1[0] & 0x8000) == 0x8000 ) {
    result1[0] = ~(result1[0] - 1); // Subtract 1 and complement the result
    mV[0] = result1[0] * -LSB * 0.001;
    } else {
    mV[0] = result1[0] * LSB * 0.001;
    }
    // Step 7 - BOARD2 CH1 - READ PREVIOUS ADC CONVERSION
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, LOW);
    SSIDataGet(SSI2_BASE, &dataRxADC[1]);

    // Step 8 - BOARD2 CH1 - START TEMPERATURE CONVERSION
    configMask = (ADS118_COMMON & ADS1118_MUX_MASK_TS_MODE_MASK) | ADS1118_MUX_CH1 | ADS1118_TS_MODE_TEMP;
    SSIDataPut(SSI2_BASE, configMask);
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, HIGH);

    // Step 9 - BOARD2 CH1 CONVERT TO MV
    result1[1] = dataRxADC[1] & 0x0000FFFF;
    if ( (result1[1] & 0x8000) == 0x8000 ) {
    result1[1] = ~(result1[1] - 1); // Subtract 1 and complement the result
    mV[1] = result1[1] * -LSB * 0.001;
    } else {
    mV[1] = result1[1] * LSB * 0.001;
    }
    // Step 10 - Wait for conversions to complete 1/64 samples sec + 10 % (>= 17.2 ms)
    SysCtlDelay(ADS1118_DELAY);

    // Step 11 - BOARD0 CH1 - READ PREVIOUS TEMPERATURE CONVERSION
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, LOW);
    SSIDataGet(SSI0_BASE, &dataRxTEMP[0]);

    // Step 12 - BOARD0 CH2 - START ADC CONVERSION
    configMask = (ADS118_COMMON & ADS1118_MUX_MASK_TS_MODE_MASK) | ADS1118_MUX_CH2 | ADS1118_TS_MODE_ADC;
    SSIDataPut(SSI0_BASE, configMask);
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, HIGH);

    // Step 13 - BOARD0 CH1 CONVERT TO CJ MV
    masked[0] = dataRxTEMP[0] & 0x0000FFFF;
    if ( (masked[0] & 0x8000) == 0x8000 ) {
    // Subtract 1 and complement the result
    result2[0] = ~(masked[0] - 1);
    // Shift after complement since temperature data are represented as a 14-bit result and are output starting with MSB.
    result2[0] = result2[0] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[0] = result2[0] * -0.03125;
    } else {
    // Shift after complement since temperature data are represented as a 14-bit result
    result2[0] = masked[0] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[0] = result2[0] * 0.03125;
    }
    // Get Internal Temp. Sensor mV equivalent.
    coldJuncMilliVolts[0] = LookUp(coldJuncTemp[0], TTABLE, mVTABLE, LUT_LENGTH);

    // Step 14 - BOARD2 CH1 - READ PREVIOUS TEMPERATURE CONVERSION
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, LOW);
    SSIDataGet(SSI2_BASE, &dataRxTEMP[1]);

    // Step 15 - BOARD2 CH2 - START ADC CONVERSION
    configMask = (ADS118_COMMON & ADS1118_MUX_MASK_TS_MODE_MASK) | ADS1118_MUX_CH2 | ADS1118_TS_MODE_ADC;
    SSIDataPut(SSI2_BASE, configMask);
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, HIGH);

    // Step 23 - BOARD2 CH1 CONVERT TO CJ MV
    masked[1] = dataRxTEMP[1] & 0x0000FFFF;
    if ( (masked[1] & 0x8000) == 0x8000 ) {
    // Subtract 1 and complement the result
    result2[1] = ~(masked[1] - 1);
    // Shift after complement since temperature data are represented as a 14-bit result and are output starting with MSB.
    result2[1] = result2[1] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[1] = result2[1] * -0.03125;
    } else {
    // Shift after complement since temperature data are represented as a 14-bit result
    result2[1] = masked[1] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[1] = result2[1] * 0.03125;
    }
    // Get Internal Temp. Sensor mV equivalent.
    coldJuncMilliVolts[1] = LookUp(coldJuncTemp[1], TTABLE, mVTABLE, LUT_LENGTH);

    // Step 17 - Wait for conversions to complete 1/64 samples sec + 10 % (>= 17.2 ms)
    SysCtlDelay(ADS1118_DELAY);

    // Step 18 - BOARD0 CH2 - READ PREVIOUS ADC CONVERSION
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, LOW);
    SSIDataGet(SSI0_BASE, &dataRxADC[2]);

    // Step 19 - BOARD0 CH2 - START TEMPERATURE CONVERSION
    configMask = (ADS118_COMMON & ADS1118_MUX_MASK_TS_MODE_MASK) | ADS1118_MUX_CH2 | ADS1118_TS_MODE_TEMP;
    SSIDataPut(SSI0_BASE, configMask);
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, HIGH);

    // Step 20 - BOARD0 CH2 CONVERT TO MV
    result1[2] = dataRxADC[2] & 0x0000FFFF;
    if ( (result1[2] & 0x8000) == 0x8000 ) {
    result1[2] = ~(result1[2] - 1); // Subtract 1 and complement the result
    mV[2] = result1[2] * -LSB * 0.001;
    } else {
    mV[2] = result1[2] * LSB * 0.001;
    }
    // Step 21 BOARD2 CH2 - READ PREVIOUS ADC CONVERSION
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, LOW);
    SSIDataGet(SSI2_BASE, &dataRxADC[3]);

    // Step 22 - BOARD2 CH2 - START TEMPERATUURE CONVERSION
    configMask = (ADS118_COMMON & ADS1118_MUX_MASK_TS_MODE_MASK) | ADS1118_MUX_CH2 | ADS1118_TS_MODE_TEMP;
    SSIDataPut(SSI2_BASE, configMask);
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, HIGH);

    // Step 23 - BOARD2 CH2 CONVERT TO MV
    result1[3] = dataRxADC[3] & 0x0000FFFF;
    if ( (result1[3] & 0x8000) == 0x8000 ) {
    result1[3] = ~(result1[3] - 1); // Subtract 1 and complement the result
    mV[3] = result1[3] * -LSB * 0.001;
    } else {
    mV[3] = result1[3] * LSB * 0.001;
    }
    // Step 24 - Wait for conversions to complete 1/64 samples sec + 10 % (>= 17.2 ms)
    SysCtlDelay(ADS1118_DELAY);

    // Step 25 - BOARD0 CH2 - READ PREVIOUS TEMPERATURE CONVERSION
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, LOW);
    SSIDataGet(SSI0_BASE, &dataRxTEMP[2]);

    // Step 26 - BOARD0 CH1 - START ADC CONVERSION
    configMask = (ADS118_COMMON & ADS1118_MUX_MASK_TS_MODE_MASK) | ADS1118_MUX_CH1 | ADS1118_TS_MODE_ADC;
    SSIDataPut(SSI0_BASE, configMask);
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, HIGH);

    // Step 27 - BOARD0 CH2 CONVERT TO CJ MV
    masked[2] = dataRxTEMP[2] & 0x0000FFFF;
    if ( (masked[2] & 0x8000) == 0x8000 ) {
    // Subtract 1 and complement the result
    result2[2] = ~(masked[2] - 1);
    // Shift after complement since temperature data are represented as a 14-bit result and are output starting with MSB.
    result2[2] = result2[2] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[2] = result2[2] * -0.03125;
    } else {
    // Shift after complement since temperature data are represented as a 14-bit result
    result2[2] = masked[2] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[2] = result2[2] * 0.03125;
    }
    // Get Internal Temp. Sensor mV equivalent.
    coldJuncMilliVolts[2] = LookUp(coldJuncTemp[2], TTABLE, mVTABLE, LUT_LENGTH);

    // Step 28 - BOARD2 CH2 - READ PREVIOUS TEMPERATURE CONVERSION
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, LOW);
    SSIDataGet(SSI2_BASE, &dataRxTEMP[3]);

    // Step 29 - BOARD2 CH1 - START ADC CONVERSION
    configMask = (ADS118_COMMON & ADS1118_MUX_MASK_TS_MODE_MASK) | ADS1118_MUX_CH1 | ADS1118_TS_MODE_ADC;
    SSIDataPut(SSI2_BASE, configMask);
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, HIGH);

    // Step 30 - BOARD2 CH2 CONVERT TO CJ MV
    masked[3] = dataRxTEMP[3] & 0x0000FFFF;
    if ( (masked[3] & 0x8000) == 0x8000 ) {
    // Subtract 1 and complement the result
    result2[3] = ~(masked[3] - 1);
    // Shift after complement since temperature data are represented as a 14-bit result and are output starting with MSB.
    result2[3] = result2[3] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[3] = result2[3] * -0.03125;
    } else {
    // Shift after complement since temperature data are represented as a 14-bit result
    result2[3] = masked[3] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[3] = result2[3] * 0.03125;
    }
    // Get Internal Temp. Sensor mV equivalent.
    coldJuncMilliVolts[3] = LookUp(coldJuncTemp[3], TTABLE, mVTABLE, LUT_LENGTH);

    // Perform Cold Junction Compensation for all voltage readings
    // Convert the mV to Temperature value interpolating TC data through Lookup Data
    mV[0] += coldJuncMilliVolts[0];
    mV[1] += coldJuncMilliVolts[1];
    mV[2] += coldJuncMilliVolts[2];
    mV[3] += coldJuncMilliVolts[3];

    _TcTemps[0] = LookUp(mV[0], mVTABLE, TTABLE, LUT_LENGTH);
    _TcTemps[1] = LookUp(mV[1], mVTABLE, TTABLE, LUT_LENGTH);
    _TcTemps[2] = LookUp(mV[2], mVTABLE, TTABLE, LUT_LENGTH);
    _TcTemps[3] = LookUp(mV[3], mVTABLE, TTABLE, LUT_LENGTH);
    }

    }

    THIS SNIPPET WORKS (is called from a loop within it's parent routine)

    void ReadFromTwoADS118s() {

    uint32_t dummy;
    uint32_t dataRx[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    uint16_t result[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    uint16_t raw[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    float coldJuncTemp[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    float coldJuncMilliVolts[NUM_CHAN_PER_BOARD * NUM_BOARDS];
    float mV[NUM_CHAN_PER_BOARD * NUM_BOARDS];

    // Read Thermocouple connected to (AIN0, AIN1) or (AIN2, AIN3) in ADS1118

    // First, read differential voltage value (mV) from (AIN0, AIN1) or (AIN2, AIN3) in ADS1118

    // Send the data using the "blocking" put function.
    // This function will wait until there is room in the send FIFO before returning.
    // This allows you to assure that all the data you send makes it into the send FIFO.
    // SPI (TC1_CONFIG) data to request (AIN0, AIN1) or (AIN2, AIN3) value from ADS1118
    SSIDataPut(SSI0_BASE, ADC_CH1_CONFIG);
    SSIDataPut(SSI2_BASE, ADC_CH1_CONFIG);

    // Wait until SSI0 is done transferring all the data in the transmit FIFO.
    while(SSIBusy(SSI0_BASE)){

    }
    while(SSIBusy(SSI2_BASE)){

    }
    // Receive the 2 bytes of data using the "blocking" Get function. This function
    // will wait until there is data in the receive FIFO before returning.
    // Ignore the result at the first reading.
    SSIDataGet(SSI0_BASE, &dummy);
    SSIDataGet(SSI2_BASE, &dummy);

    // Delay a bit (~20ms) to give enough time to ADC so that it can write the new result before it is interrupted by MCU.
    SysCtlDelay(ADS1118_DELAY);

    SSIDataPut(SSI0_BASE, ADC_CH1_CONFIG);
    SSIDataPut(SSI2_BASE, ADC_CH1_CONFIG);

    while(SSIBusy(SSI0_BASE)){

    }
    SSIDataGet(SSI0_BASE, &dataRx[0]);

    // Capture the the least significant 16-bit data after the second reading.
    result[0] = dataRx[0] & 0xFFFF;

    // Check the MSB first and then convert digital code to mV
    if ( (result[0] & 0x8000) == 0x8000 ) {
    result[0] = ~(result[0] - 1); // Subtract 1 and complement the result
    mV[0] = result[0] * -LSB * 0.001;
    } else {
    mV[0] = result[0] * LSB * 0.001;
    }
    while(SSIBusy(SSI2_BASE)){

    }
    SSIDataGet(SSI2_BASE, &dataRx[1]);

    // Capture the the least significant 16-bit data after the second reading.
    result[1] = dataRx[1] & 0xFFFF;

    // Check the MSB first and then convert digital code to mV
    if ( (result[1] & 0x8000) == 0x8000 ) {
    result[1] = ~(result[1] - 1); // Subtract 1 and complement the result
    mV[1] = result[1] * -LSB * 0.001;
    } else {
    mV[1] = result[1] * LSB * 0.001;
    }
    // not sure if this delay is necessary
    SysCtlDelay(ADS1118_DELAY);

    // Read the value of temperature sensor within the ADS1118
    // Convert the temperature sensor reading to a voltage value (mV) using reverse LUT.
    // SPI (CJ_TEMP_CONFIG) data to request temp. sensor reading from ADS1118
    SSIDataPut(SSI0_BASE, CJ_TEMP_CH1_CONFIG);
    SSIDataPut(SSI2_BASE, CJ_TEMP_CH1_CONFIG);

    while(SSIBusy(SSI0_BASE)){
    }
    // Ignore the result at the first reading.
    SSIDataGet(SSI0_BASE, &dummy);

    while(SSIBusy(SSI2_BASE)){
    }
    // Ignore the result at the first reading.
    SSIDataGet(SSI2_BASE, &dummy);

    // Delay a bit (~20ms)
    SysCtlDelay(ADS1118_DELAY);

    SSIDataPut(SSI0_BASE, CJ_TEMP_CH1_CONFIG);
    SSIDataPut(SSI2_BASE, CJ_TEMP_CH1_CONFIG);

    while(SSIBusy(SSI0_BASE)){
    }
    SSIDataGet(SSI0_BASE, &dataRx[0]);

    while(SSIBusy(SSI2_BASE)){
    }
    SSIDataGet(SSI2_BASE, &dataRx[1]);

    // Capture the the least significant 16-bit data after the second reading.
    raw[0] = dataRx[0] & 0xFFFF;
    raw[1] = dataRx[1] & 0xFFFF;

    if ( (raw[0] & 0x8000) == 0x8000 ) {
    // Subtract 1 and complement the result
    result[0] = ~(raw[0] - 1);
    // Shift after complement since temperature data are represented as a 14-bit result and are output starting with MSB.
    result[0] = result[0] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[0] = result[0] * -0.03125;
    } else {
    // Shift after complement since temperature data are represented as a 14-bit result
    result[0] = raw[0] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[0] = result[0] * 0.03125;
    }
    // Get Internal Temp. Sensor mV equivalent.
    coldJuncMilliVolts[0] = LookUp(coldJuncTemp[0], TTABLE, mVTABLE, LUT_LENGTH);

    if ( (raw[1] & 0x8000) == 0x8000 ) {
    // Subtract 1 and complement the result
    result[1] = ~(raw[1] - 1);
    // Shift after complement since temperature data are represented as a 14-bit result and are output starting with MSB.
    result[1] = result[1] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[1] = result[1] * -0.03125;
    } else {
    // Shift after complement since temperature data are represented as a 14-bit result
    result[1] = raw[1] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[1] = result[1] * 0.03125;
    }
    // Get Internal Temp. Sensor mV equivalent.
    coldJuncMilliVolts[1] = LookUp(coldJuncTemp[1], TTABLE, mVTABLE, LUT_LENGTH);

    // not sure if this delay is necessary
    SysCtlDelay(ADS1118_DELAY);

    // Perform Cold Junction Compensation for both voltage readings from AIN0 - AIN1 in ADS1118
    // Convert the mV to Temperature value interpolating TC data through Lookup Data
    mV[0] += coldJuncMilliVolts[0];
    mV[1] += coldJuncMilliVolts[1];

    _TcTemps[0] = LookUp(mV[0], mVTABLE, TTABLE, LUT_LENGTH);
    _TcTemps[1] = LookUp(mV[1], mVTABLE, TTABLE, LUT_LENGTH);

    // Send the data using the "blocking" put function.
    // This function will wait until there is room in the send FIFO before returning.
    // This allows you to assure that all the data you send makes it into the send FIFO.
    // SPI (TC1_CONFIG) data to request (AIN0, AIN1) or (AIN2, AIN3) value from ADS1118
    SSIDataPut(SSI0_BASE, ADC_CH2_CONFIG);
    SSIDataPut(SSI2_BASE, ADC_CH2_CONFIG);

    // Wait until SSI0 is done transferring all the data in the transmit FIFO.
    while(SSIBusy(SSI0_BASE)){

    }
    while(SSIBusy(SSI2_BASE)){

    }
    // Receive the 2 bytes of data using the "blocking" Get function. This function
    // will wait until there is data in the receive FIFO before returning.
    // Ignore the result at the first reading.
    SSIDataGet(SSI0_BASE, &dummy);
    SSIDataGet(SSI2_BASE, &dummy);

    // Delay a bit (~20ms) to give enough time to ADC so that it can write the new result before it is interrupted by MCU.
    SysCtlDelay(ADS1118_DELAY);

    SSIDataPut(SSI0_BASE, ADC_CH2_CONFIG);
    SSIDataPut(SSI2_BASE, ADC_CH2_CONFIG);

    while(SSIBusy(SSI0_BASE)){

    }
    SSIDataGet(SSI0_BASE, &dataRx[2]);

    // Capture the the least significant 16-bit data after the second reading.
    result[2] = dataRx[2] & 0xFFFF;

    // Check the MSB first and then convert digital code to mV
    if ( (result[2] & 0x8000) == 0x8000 ) {
    result[2] = ~(result[2] - 1); // Subtract 1 and complement the result
    mV[2] = result[2] * -LSB * 0.001;
    } else {
    mV[2] = result[2] * LSB * 0.001;
    }
    while(SSIBusy(SSI2_BASE)){

    }
    SSIDataGet(SSI2_BASE, &dataRx[3]);

    // Capture the the least significant 16-bit data after the second reading.
    result[3] = dataRx[3] & 0xFFFF;

    // Check the MSB first and then convert digital code to mV
    if ( (result[3] & 0x8000) == 0x8000 ) {
    result[3] = ~(result[3] - 1); // Subtract 1 and complement the result
    mV[3] = result[3] * -LSB * 0.001;
    } else {
    mV[3] = result[3] * LSB * 0.001;
    }
    // not sure if this delay is necessary
    SysCtlDelay(ADS1118_DELAY);

    // Read the value of temperature sensor within the ADS1118
    // Convert the temperature sensor reading to a voltage value (mV) using reverse LUT.
    // SPI (CJ_TEMP_CONFIG) data to request temp. sensor reading from ADS1118
    SSIDataPut(SSI0_BASE, CJ_TEMP_CH2_CONFIG);
    SSIDataPut(SSI2_BASE, CJ_TEMP_CH2_CONFIG);

    while(SSIBusy(SSI0_BASE)){
    }
    // Ignore the result at the first reading.
    SSIDataGet(SSI0_BASE, &dummy);

    while(SSIBusy(SSI2_BASE)){
    }
    // Ignore the result at the first reading.
    SSIDataGet(SSI2_BASE, &dummy);

    // Delay a bit (~20ms)
    SysCtlDelay(ADS1118_DELAY);

    SSIDataPut(SSI0_BASE, CJ_TEMP_CH1_CONFIG);
    SSIDataPut(SSI2_BASE, CJ_TEMP_CH1_CONFIG);

    while(SSIBusy(SSI0_BASE)){
    }
    SSIDataGet(SSI0_BASE, &dataRx[2]);

    while(SSIBusy(SSI2_BASE)){
    }
    SSIDataGet(SSI2_BASE, &dataRx[3]);

    // Capture the the least significant 16-bit data after the second reading.
    raw[2] = dataRx[2] & 0xFFFF;
    raw[3] = dataRx[3] & 0xFFFF;

    if ( (raw[2] & 0x8000) == 0x8000 ) {
    // Subtract 1 and complement the result
    result[2] = ~(raw[2] - 1);
    // Shift after complement since temperature data are represented as a 14-bit result and are output starting with MSB.
    result[2] = result[2] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[2] = result[2] * -0.03125;
    } else {
    // Shift after complement since temperature data are represented as a 14-bit result
    result[2] = raw[2] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[2] = result[2] * 0.03125;
    }
    // Get Internal Temp. Sensor mV equivalent.
    coldJuncMilliVolts[2] = LookUp(coldJuncTemp[2], TTABLE, mVTABLE, LUT_LENGTH);

    if ( (raw[3] & 0x8000) == 0x8000 ) {
    // Subtract 1 and complement the result
    result[3] = ~(raw[3] - 1);
    // Shift after complement since temperature data are represented as a 14-bit result and are output starting with MSB.
    result[3] = result[3] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[3] = result[3] * -0.03125;
    } else {
    // Shift after complement since temperature data are represented as a 14-bit result
    result[3] = raw[3] >> 2;
    // One 14-bit LSB equals 0.03125 C
    coldJuncTemp[3] = result[3] * 0.03125;
    }
    // Get Internal Temp. Sensor mV equivalent.
    coldJuncMilliVolts[3] = LookUp(coldJuncTemp[3], TTABLE, mVTABLE, LUT_LENGTH);

    // not sure if this delay is necessary
    SysCtlDelay(ADS1118_DELAY);

    // Perform Cold Junction Compensation for both voltage readings from AIN0 - AIN1 in ADS1118
    // Convert the mV to Temperature value interpolating TC data through Lookup Data
    mV[2] += coldJuncMilliVolts[2];
    mV[3] += coldJuncMilliVolts[3];

    _TcTemps[2] = LookUp(mV[2], mVTABLE, TTABLE, LUT_LENGTH);
    _TcTemps[3] = LookUp(mV[3], mVTABLE, TTABLE, LUT_LENGTH);
    }

  • Kenneth,


    I didn't get a chance to read through your code today, as I had some other work I needed to finish. I'll give it a look again later.

    For the code, snippet 1 that doesn't work, what doesn't work? Is it giving bad data, or not giving enough time to complete the conversion? Do you have a logic analyzer to verify timing and the read of the device? It may help to show plots of the SPI communication to verify that the device is being programmed correctly and that the device responds properly.

    If the problem is in bad data, I'd force a known input, and report back with the ADC code. Just ignore the conversions to voltage and CJC for now and analyze the ADC raw data.


    Joseph Wu
  • Hi: No rush. I'm sorry I wasn't more specific. I'm reading the values (ADC & Temperature) from the 4 channels of the 2 ADS1118 boards each loop. However, when I calculate the temperature from the board values (which is the same code in both snippets), the final 4 temperatures are correct from snippet 2 but not snippet 1. 

    I'll test just looking at the raw data. Just as an aside, in code snippet 2 that works, why would I have to read dummy data. I might not be able to test your suggestion until the end of next week as I have to try and get the SORTE communication example for a Sitara PRU working in the next couple of days.

  • I never sent you the common header file


    /*
    * ADS1118.h
    *
    * Created on: Dec 27, 2018
    * Author: kendemers
    */

    #ifndef ADS1118_H_
    #define ADS1118_H_

    #include <stdint.h>

    //***********************************************************************
    // ADS1118 Definitions
    //***********************************************************************

    #define LOW (0)
    #define HIGH (1)

    #define NUM_BOARDS 2
    #define NUM_CHAN_PER_BOARD 2

    #define ADS1118_DATA_WIDTH16 (16)
    #define ADS1118_DATA_WIDTH32 (32)

    // Configuration Register Definitions
    #define ADS1118_RESERVED (0x1) // Bits 0:0 Always 1
    #define ADS1118_NOP_VALID (0x1 << 1) // Bits 2:1 Valid Data
    #define ADS1118_PULL_UP_ENAB (0x1 << 3) // Bits 3:3 Pull Up Resistor Enabled
    #define ADS1118_TS_MODE_ADC (0x0 << 4) // Bits 4:4 0 ADC
    #define ADS1118_TS_MODE_TEMP (0x1 << 4) // Bits 4:4 1 Temperature
    #define ADS1118_DR_64SPS (0x3 << 5) // Bits 7:5 011 64 Samples Per Second
    #define ADS1118_MODE_SS (0x1 << 8) // Bits 8:8 0 Continuous Conversion 1 Power Down / Single Shot
    #define ADS1118_PGA_0_256 (0x5 << 9) // Bits 11:9 Programmable Gain Amplifier 101 = FSR +-0.256V
    #define ADS1118_MUX_CH2 (0x0 << 12) // Bits 14:12 Input Multiplexor
    #define ADS1118_MUX_CH1 (0x3 << 12) // Bits 14:12 Input Multiplexor
    #define ADS1118_SS_START (0x1 << 15) // Bits 15:15 Single Shot Conversion Start

    //#define ADS1118_MUX_MASK (0x8FFF) // Used to clear 3 MUX Bits
    //#define ADS1118_TS_MODE_MASK (0xFFEF) // Used to clear 1 TS_MODE Bit1
    #define ADS1118_MUX_MASK_TS_MODE_MASK (0x8FEF) // Used to clear 3 MUX Bits and 1 TS_MODE Bit1

    #define ADS1118_COMMON (ADS1118_SS_START | ADS1118_PGA_0_256 | ADS1118_MODE_SS | ADS1118_DR_64SPS | ADS1118_PULL_UP_ENAB | ADS1118_NOP_VALID | ADS1118_RESERVED)

    #define SSI0_CH1_ADC (0xBB6B)
    #define SSI0_CH1_TEMP (0xBB7B)

    #define LSB 7.8125 // LSB (uV) of full-scale range selected in ADS1118
    //#define NUM_SSI_DATA 2 // Number bytes of data in SSI
    #define ADC_CH2_CONFIG 0x8B6B // SPI data to request A1 - A0 value from ADS1118
    #define ADC_CH1_CONFIG 0xBB6B // SPI data to request A2 - A3 value from ADS1118
    #define CJ_TEMP_CH2_CONFIG 0x8B7B // SPI data to request Cold Junction Temp value from ADS1118
    #define CJ_TEMP_CH1_CONFIG 0xBB7B // SPI data to request Cold Junction Temp value from ADS1118
    #define ADS1118_DELAY 800000 // Delay required for next data of ADS1118 to be ready (3 * ( ADS1118_DELAY / SYS_CLK_FREQ) = 20.00 ms)
    // Minimum delay setting found to be 640000 (16.01 ms)

    extern void InitializeADS1118ForContinuous();
    extern float ReadContinuousOneTempFromADS1118();

    //extern void get_ADS1118_Temp(float *TC_Temp);
    //extern void GetTemp(float *TC_Temp);
    extern void ReadTCs();
    extern void ReadOneTempFromADS1118();
    extern void xxx();
    extern void InitializeADS1118();
    extern void ReadTwoADS1118Boards();

    #endif /* ADS1118_H_ */
  • Kenneth,


    Out of curiosity, have you been able to get snippet 1 of your code to work? As far as I can tell, my read sequence should be ok, but debugging this would require simplifying the conversion and going back to the output data from the ADC to verify the read. In snippet 2, I don't think you would need to have dummy data to get the proper ADC response.

    I'll close this post for now. However, if you haven't finished debugging the code, you should be able to post back with more questions and comments and we can continue working on this.


    Joseph Wu