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.

ADS1118: SPI Communication Problem on ATMEGA8A

Part Number: ADS1118

Dear member of the support team,

I have been trying to get the following to work for quite a while now:
The ADS1118 shall operate to sense voltages of two Type-K-temperature sensors, connected to the differential inputs of the ADC.
The communication via SPI seems not to work properly. As can be seen in the CommFailX.png files, the logic analyser indicates the correct command being sent by the µC. The ADS1118 does not repeat the correct Configuration Bytes as can be seen in the Logic Analyser’s scope within the 32Bit communication cycle. Another thing that is confusing is that the first failure reveals a waiting time of about 60ms for the conversion to take place, because of which I needed to split the scope, otherwise the display wouldn’t have granted a reasonable overview. I guess there is some other thing that is strange here since this is way more than the 20µs you proposed in one of your forum answers.
Find the minimal work example I used attached. The code runs on a ATMEGA8A, CPU clock is crystal-stabilised at 8MHz. Find the schematic attached as well.
Thanks a lot for your attention. I hope you can help me concerning this issue.

Kind regards,

Daniel Philipps

ADS1118Conn.schADS1118Conn.brd

/**from main.c:**/

#define F_CPU 8000000UL
#define TRUE 1
#define FALSE 0


#inlcude “SPIinit.h”
#include “tempADS1118.h“
#include <stdint.h>
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
//(...)
void initialise(){
  spiInit(SPI_MODE_1, SPI_MSB, SPI_NO_INTERRUPT,  SPI_MSTR_CLK32);
  _delay_ms(500);
}
//(...)
int main(void)
{
  initialise();
  float temp1, temp2, temp3, temp4 
  while (1) {                     
    _delay_ms(500); 
    temp1 = perform16BitMeasurement(ADS_DEFAULT_CONFIG_LINKS);
    _delay_us(100);
    temp2 = perform16BitMeasurement(ADS_DEFAULT_CONFIG_LINKS_INT);
    _delay_us(100);
    temp3 = perform16BitMeasurement(ADS_DEFAULT_CONFIG_RECHTS);
    _delay_us(100);
    temp4 = perform16BitMeasurement(ADS_DEFAULT_CONFIG_RECHTS_INT);
    _delay_ms(500);
  }
}


/**From SPIinit.h:**/



#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif


#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <util/delay.h> 
#include <stdlib.h>

#ifndef SPIINIT_H_
#define SPIINIT_H_

//PinDef
#define SPI_SS_STAT_REG PINB
#define SPI_SS_PORT PORTB
#define SPI_SS_PIN PORTB2
#define SPI_SCK_PIN PORTB5
#define SPI_MOSI_PIN PORTB3
#define SPI_MISO_PIN PORTB4

//SPI clock modes
#define SPI_MODE_0 0x00 /* Sample (Rising) Setup (Falling) CPOL=0, CPHA=0 */
#define SPI_MODE_1 0x01 /* Setup (Rising) Sample (Falling) CPOL=0, CPHA=1 */
#define SPI_MODE_2 0x02 /* Sample (Falling) Setup (Rising) CPOL=1, CPHA=0 */
#define SPI_MODE_3 0x03 /* Setup (Falling) Sample (Rising) CPOL=1, CPHA=1 */

// data direction
#define SPI_LSB 1 /* LSB first */
#define SPI_MSB 0 /* MSB first */

// Interrupt when data received (SPIF bit received)
#define SPI_NO_INTERRUPT 0
#define SPI_INTERRUPT 1

// slave or master with clock div
#define SPI_SLAVE 0xF0
#define SPI_MSTR_CLK2 0x04 /* chip clock/2 */
#define SPI_MSTR_CLK4 0x00 /* chip clock/4 */
#define SPI_MSTR_CLK16 0x01 /* chip clock/16 */
#define SPI_MSTR_CLK64 0x02 /* chip clock/64 */
#define SPI_MSTR_CLK128 0x03 /* chip clock/128 */
#define SPI_MSTR_CLK2 0x04 /* chip clock/2 */
#define SPI_MSTR_CLK8 0x05 /* chip clock/8 */
#define SPI_MSTR_CLK32 0x06 /* chip clock/32 */

void spiInit(  uint8_t mode,      // timing mode SPI_MODE[0-4]
        uint8_t dord,             // data direction SPI_LSB|SPI_MSB
        uint8_t interruptEnable, // whether to raise interrupt on recieve
        uint8_t msClock); // clock diviser

uint8_t spiTransceiveByte(uint8_t data, uint8_t eot);

#endif


/**From SPIinit.c:**/



#include "SPIinit.h"

//initialise the SPI Bus
void spiInit(uint8_t mode, uint8_t dord, uint8_t interruptEnable, uint8_t msClock)
{
  // specify pin directions for SPI pins on port B
  if (msClock == SPI_SLAVE) {            // if slave SS and SCK is input
    DDRB &= ~(1<<SPI_MOSI_PIN);          // input
    DDRB |= (1<<SPI_MISO_PIN);          // output
    DDRB &= ~(1<<SPI_SS_PIN);          // input
    DDRB &= ~(1<<SPI_SCK_PIN);          // input
    } else {
    DDRB |= (1<<SPI_MOSI_PIN);          // output
    DDRB &= ~(1<<SPI_MISO_PIN);          // input
    DDRB |= (1<<SPI_SCK_PIN);          // output
    DDRB |= (1<<SPI_SS_PIN);          // output
  }
  SPCR = ((interruptEnable ? 1 : 0)<<SPIE)    // interrupt enabled
  | (dord<<DORD)                  // LSB or MSB
  | (((msClock != SPI_SLAVE) ? 1 : 0) <<MSTR)    // Slave or Master
  | (((mode & 0x02) == 2) << CPOL)        // clock timing mode CPOL
  | (((mode & 0x01)) << CPHA)            // clock timing mode CPHA
  | (((msClock & 0x02) == 2) << SPR1)        // cpu clock divisor SPR1
  | ((msClock & 0x01) << SPR0);          // cpu clock divisor SPR0
  

  SPSR = (((msClock & 0x04) == 4) << SPI2X);    // clock divisor SPI2X
  
  SPCR |= (1<<SPE); //enable SPI
}

uint8_t spiTransceiveByte(uint8_t data, uint8_t eot){
  //pull down the slave select pin
  SPI_SS_PORT &= ~(1<<SPI_SS_PIN);
  _delay_us(10);
  
  // Load data into the buffer
  SPDR = data;
  
  //Wait until transmission complete
  while(!(SPSR & (1<<SPIF)));
  
  //if end of transmission is reached, pull up the ssPin
  if(eot)  SPI_SS_PORT |= (1<<SPI_SS_PIN);
  
  // Return received data
  return SPDR;
}


/**From  tempADS1118.h:**/

 

#include "SPIinit.h"
#include <stdlib.h>
#include <util/delay.h>
#include <stdint.h>

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif


#ifndef TEMPADS1118_H_
#define TEMPADS1118_H_


/* left ext sense
 * SingleShot (SS)
 * default MUX to left (MUX = 000; default)
 * lowest amplification (e.g. PGA = 101)
 * SingleShot + PowerDown (MODE = 1)
 * 16SPS DataRate (DR = 001)
 * ADC mode (TS_MODE = 0; default)
 * internal pullup resistor (PULL_UP_EN = 1)
 * no operation (NOP = 01) otherwise failure
 * Reserved always 1 (R=1)
 */

#define ADS_DEFAULT_CONFIG_LINKS 0b1000101100101011//1 000 101 1 001 0 1 01 1



/* right ext sense
 * SingleShot (SS)
 * MUX to right (MUX = 011)
 * lowest amplification (e.g. PGA = 101)
 * SingleShot + PowerDown (MODE = 1)
 * 16SPS DataRate (DR = 001)
 * internal pullup resistor (PULL_UP_EN = 1)
 * no operation (NOP = 01) otherwise failure
 * Reserved always 1 (R=1)
 * ADC mode (TS_MODE = 0; default)
 */

#define ADS_DEFAULT_CONFIG_RECHTS 0b1011101100101011//1 011 101 1 001 0 1 01 1


/* left int sense
 * SingleShot (SS)
 * MUX to left (MUX = 000; default)
 * lowest amplification (e.g. PGA = 101)
 * SingleShot + PowerDown (MODE = 1)
 * 16SPS DataRate (DR = 001)
 * internal pullup resistor (PULL_UP_EN = 1)
 * no operation (NOP = 01) otherwise failure
 * Reserved always 1 (R=1)
 * Tsense mode (TS_MODE = 0)
 */

#define ADS_DEFAULT_CONFIG_LINKS_INT 0b1000101100111011 //1 000 101 1 001 1 1 01 1



/* right int sense
 * SingleShot (SS)
 * MUX to right (MUX = 011)
 * lowest amplification (e.g. PGA = 101)
 * SingleShot + PowerDown (MODE = 1)
 * 16SPS DataRate (DR = 001)
 * Tsense mode (TS_MODE = 1)
 * internal pullup resistor (PULL_UP_EN = 1)
 * no operation (NOP = 01) otherwise failure
 * Reserved always 1 (R=1)
 */

#define ADS_DEFAULT_CONFIG_RECHTS_INT 0b1011101100111011 //1 011 101 1 001 1 1 01 1


#define ADS_DEFAULT_CONFIG_T0    ADS_DEFAULT_CONFIG_LINKS
#define ADS_DEFAULT_CONFIG_T0_INT  ADS_DEFAULT_CONFIG_LINKS_INT
#define ADS_DEFAULT_CONFIG_T1    ADS_DEFAULT_CONFIG_RECHTS
#define ADS_DEFAULT_CONFIG_T1_INT  ADS_DEFAULT_CONFIG_RECHTS_INT




//perform one 16Bit Measurement (which is the measurement size of our ADC)
uint16_t perform16BitMeasurement(uint16_t valueMsg);

#endif /* TEMPADS1118_H_ */


/**From tempADS1118.c:**/

#include "tempADS1118.h"

//perform one 16Bit Measurement (which is the measurement size of the ADC)
uint16_t perform16BitMeasurement(uint16_t valueMsg){
	//request measurement
	uint8_t msgP1 = (uint8_t) (valueMsg >> 8); //MSB
	uint8_t msgP2 = valueMsg % 256 ; //LSB

	
	
	spiTransceiveByte(msgP1, FALSE);
	spiTransceiveByte(msgP2, TRUE);
	
	_delay_us(50);

	SPI_SS_PORT &= ~(1<<SPI_SS_PIN);
	//wait for conversion to be complete => MISO is pulled down
	while(PINB & (1<<PINB4));
	
	//receive value (16bit = 2Byte); MSB first - shift - LSB
	uint8_t valP1 = 0;
	uint8_t valP2 = 0;	
uint16_t value = 0;
	valP1 = (uint16_t)spiTransceiveByte(msgP1, FALSE);
	valP2 = (uint16_t)spiTransceiveByte(msgP2, FALSE);
	spiTransceiveByte(msgP1, FALSE);
	spiTransceiveByte(msgP2, TRUE);
	
	
	value = valP1 << 8 + valP2;
	
	return value;
}

  • Daniel,

    To me this looks like standard behavior for the device. I'll go through your plots and explain what's going on. I think that the discrepancy lies in the behavior of the MSB, which can be written to, but only reads back as 0.

    1.

    This looks like a standard readout and write to the device. The MISO shows a data read of the ADC, while the MOSI shows the configuration register being written to at the same time.

    2.


    This looks like a standard 32-bit transmission for the ADS1118. It’s the same transmission as shown in Figure 40 of the datasheet. The MISO shows the read from the ADC for the first two bytes, while the last two bytes show the ADS1118 configuration register. At the same time, the MOSI shows that the ADS1118 is being written two (the two same configuration register settings, twice). The difference between the write (BB2Bh) and the read (3B2Bh) is only in the first bit, so that B=1011, 3=0011.
    If you look at the Config Register Field Descriptions Table on page 25 of the datasheet, the MSB (bit 15 is always read back as a 1). This should explain the discrepancy.

    3.

    For this last plot, you read the data first, while writing BB3Bh to the device. By bringing /CS high again, you reset the SPI communications, and the next 32-bit transmission sequence starts over similar to the previous plot.

    For the first byte of the second transmission, the device sends out the data (presumably the same data as before), and the reads back the configuration register that was just written. Again the MSB is read back as a 0.

    Hopefully, this answers your question. If I misunderstood your question, please post back and we’ll look at it again.

    Joseph Wu

  • Dear Joseph,

    Thank you very much for your support. Indeed that solved my issue concerning the configuration register read back. I simply overread the annotation concerning the MSB of the read back config register.

    Still there is the question concerning the delay of the "conversion ready" on DOUT/MISO. Do you have an idea, what could be responsible for this delay?

    Kind regards,

    Daniel Philipps

  • Daniel,


    Just to be clear, here are the two values that you write to the conversion register, and the settings that are associated with the values.

    BB2Bh
    Start single conversion
    AIN2, AIN3
    FSR=±0.256
    Power-down and single shot mode
    16SPS
    ADC mode
    Pullup resistor enabled for DOUT

    BB3Bh
    Start single conversion
    AIN2, AIN3
    FSR=±0.256
    Power-down and single shot mode
    16SPS
    Temperature sensor mode
    Pullup resistor enabled for DOUT

    In this setup, you're using a single shot mode to start the conversion and get data. In these conversions, you set the data rate to 16SPS, which is a conversion time of 62.5ms. When you start the conversion, DOUT/DRDY will go high to indicate a conversion is started. However, it will take the ADC the entire 62.5ms (from 1/16SPS) to sample the input and get an ADC reading.

    I think this is the delay that you had mentioned. If that's not correct, let me know and we can discuss it further.



    Joseph Wu
  • Joseph,

    You are right, this is the "delay" I mentioned and I understand it now.
    But then again, why does it take the 60ms only using BB2Bh and not when using BB3Bh? They both set a data rate of 16SPS but as can be seen from the third scope, the time between setting a new configuration register (BB3Bh) and the availability of new data is only a few 10 µs, disregarding that somehow the poll on the µC's MISO does not work here (MISO is high and however the programme enters the second communication phase). Or is the "MISO held high until new results ready" interrupted by the communication cycle initiated by the µC?
    Kind regards,

    Daniel
  • Daniel,


    When taking a new data with the BB3Bh setting, the device is still taking a new conversion and it is running at 16SPS, and should also take about 62ms to complete. If you look at the third logic analyzer output again:



    The DOUT never indicates that the conversion has completed. At the beginning, /CS is asserted going low. As the this happens DOUT remains high, indicating that there isn't new data. Then the previous output data is still clocked out, because the data readout is cyclical and hasn't been cleared out of the output data buffer.

    After the first byte /CS is de-asserted going high and then re-asserted for the next 32-bit transmission cycle. As /CS transitions low, DOUT is still high, again indicating that there is no new data. With the 32-bit transmission cycle, the data is then read out and then the configuration register is read out.

    Note that we recommend that you use DOUT transitioning low as an interrupt to read data, or track the amount of time to read out the data after the conversion is started. Just checking to see if the DOUT is high or low when /CS is low might give a readout error. If you are in the middle of clocking out data and a new conversion completes, the DOUT is immediately updated so the data you get will be the first part of the old conversion data and the first part of the new conversion data.


    Joseph Wu

  • Joseph,

    Thank you very much for your explanations and efforts. I now got all my issues solved concerning this post!

    Kind regards,

    Daniel Philipps