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.

SSI to ADS130E08 Analog to Digital Converter error

Other Parts Discussed in Thread: ADS130E08

Hi,

Here is a problem involving two TI products not playing nicely together...

The problem is explained in the last post here.

http://e2e.ti.com/support/data_converters/precision_data_converters/f/73/t/388126

This sounds like a software/firmware issue rather than chip.  The chip (from the d/s, Timing Requirements on page 7) should be able to handle up to 20MHz clock for SCLK.

You may want to look at the configuration of the SSI; I would suspect that you may not be reading correctly at the slower speeds even though it may appear that is the case.  If at 1.2MHz you don't read all the data, the slower will not read out all the data either.

So far my intuition tells me that there are certain SSI restrictions that are violated. I've followed the recommended protocol as outlined by TI Application Engineers so this problem seems to be a bit more complex. Maybe ssi.c needs to be examined in more detail?

My code:

#include <stdbool.h>
#include <stdint.h>

#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"
#include "inc/hw_gpio.h"
#include "inc/hw_nvic.h"
#include "inc/hw_types.h"

#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/ssi.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/uart.h"

#include "utils/uartstdio.h"

#include "drivers/pinout.h"
//#include "inc/tm4c1294ncpdt.h"

//*****************************************************************************
//
//!
//! This example uses the following peripherals and I/O signals on EK-TM4C1294XL.
//! You must review these and change as needed for your own board:
//! - SSI0 peripheral
//! - GPIO Port A peripheral (for SSI0 pins)
//! - SSI0CLK - PA2
//! - SSI0Tx  - PA4
//! - SSI0Rx  - PA5
//!
//! - GPIO Port H peripheral (GPIO pin OUT)
//! - CS	  - PH0
//! - RESET	  - PH1
//!
//! - GPIO Port K peripheral (GPIO pin OUT)
//! - START	  - PK6
//!
//! - GPIO Port M peripheral (GPIO pin IN)
//! - DRDY	  - PM6
//!
//! For this example to work, the following connections are needed on the
//! EK-TM4C1294XL evaluation board.
//! - SSI0CLK(PA2) - CLK(3)
//! - SSI0Tx(PA4)  - DIN(11)
//! - SSI0Rx(PA5)  - DOUT(13)
//! - CS(PH0)	   - CS(7)
//! - RESET(PH1)   - RESET(8)
//! - START(PK6)   - START(14)
//! - DRDY(PM6)	   - DRDY(15)
//!
//! The following UART signals are configured only for displaying console
//! messages for this example.  These are not required for operation of SSI0.
//! - UART0 peripheral
//! - GPIO Port A peripheral (for UART0 pins)
//! - UART0RX - PA0
//! - UART0TX - PA1
//!
//
//*****************************************************************************
//
uint32_t g_ui32SysClock;

//Sent Data
uint32_t opcode_tx[1];

uint32_t channel_code[8];

//Received Data
uint32_t cfg_reg[21];
uint32_t rdata_rx[19];
uint32_t rx_stat[3];
uint32_t rx_data[8];
//

//*****************************************************************************
//
// Configure the UART and its pins.  This must be called before UARTprintf().
//
//*****************************************************************************
void ConfigureUART(void) {
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
	GPIOPinConfigure(GPIO_PA0_U0RX);
	GPIOPinConfigure(GPIO_PA1_U0TX);
	GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	UARTStdioConfig(0, 9600, g_ui32SysClock);
}

//*****************************************************************************
//
// This function sets up SPI0 to be used as Master in freescale mode.
//
//*****************************************************************************
void InitSPI0(void) {
	SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

	GPIOPinConfigure(GPIO_PA2_SSI0CLK);
	GPIOPinConfigure(GPIO_PA3_SSI0FSS);
	GPIOPinConfigure(GPIO_PA4_SSI0XDAT0);
	GPIOPinConfigure(GPIO_PA5_SSI0XDAT1);

	GPIOPinTypeSSI(GPIO_PORTA_BASE,
			GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2);

	//ADS SPI settings are CPOL = 0 and CPHA = 1.
	SSIConfigSetExpClk(SSI0_BASE, g_ui32SysClock, SSI_FRF_MOTO_MODE_1,
	SSI_MODE_MASTER, 10000000, 8);

	SSIEnable(SSI0_BASE);
}

//*****************************************************************************
//
// Configure GPIO pins.  This replaces the SSI0Fss with another pin.
//
//*****************************************************************************
void InitGPIO(void) {
	// PH0 - CS
	// PH1 - RESET
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);
	GPIOPinTypeGPIOOutput(GPIO_PORTH_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, GPIO_PIN_0);
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_1, GPIO_PIN_1);

	// PK6 - START
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
	GPIOPinTypeGPIOOutput(GPIO_PORTK_BASE, GPIO_PIN_6);
	GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_6, 0);

	// PM6 - DRDY
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOM);
	GPIOPinTypeGPIOInput(GPIO_PORTM_BASE, GPIO_PIN_6);
}


//*****************************************************************************
//
// Write to SPI
//
//*****************************************************************************
void spi_write(uint32_t len, uint32_t pui32DataTx[]) {

	uint32_t ui32Index;
	uint32_t pui32DataRx[19];

	//
	// Read any residual data from the SSI port.  This makes sure the receive
	// FIFOs are empty, so we don't read any unwanted junk.  This is done here
	// because the SPI SSI mode is full-duplex, which allows you to send and
	// receive at the same time.  The SSIDataGetNonBlocking function returns
	// "true" when data was returned, and "false" when no data was returned.
	// The "non-blocking" function checks if there is any data in the receive
	// FIFO and does not "hang" if there isn't.
	//
	while (SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[0])) {
	}

	for (ui32Index = 0; ui32Index < len; ui32Index++) {
		//
		// 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.
		//
		SSIDataPut(SSI0_BASE, pui32DataTx[ui32Index]);
	}

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

}

//*****************************************************************************
//
// Read from SPI
//
//*****************************************************************************
void spi_read(uint32_t rx_len, uint32_t *rx_Data) {

	uint32_t ui32Index;
	uint32_t pui32DataRx[19];

    //
    // Read any residual data from the SSI port.  This makes sure the receive
    // FIFOs are empty, so we don't read any unwanted junk.  This is done here
    // because the SPI SSI mode is full-duplex, which allows you to send and
    // receive at the same time.  The SSIDataGetNonBlocking function returns
    // "true" when data was returned, and "false" when no data was returned.
    // The "non-blocking" function checks if there is any data in the receive
    // FIFO and does not "hang" if there isn't.
    //
    while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[0]))
    {
    }

    //
    // Read bytes from RX FIFO.
    //
    for(ui32Index = 0; ui32Index < rx_len; ui32Index++)
    {
    	//
    	// Put dummy bytes out to read bytes in
    	//
    	SSIDataPut(SSI0_BASE, 0x00);
        //
        // Receive the data using the "blocking" Get function. This function
        // will wait until there is data in the receive FIFO before returning.
        //
        SSIDataGet(SSI0_BASE, &rx_Data[ui32Index]);
        //
        // Since we are using 8-bit data, mask off the MSB.
        //
        rx_Data[ui32Index] &= 0x00FF;
    }

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

}


//*****************************************************************************
//
// Reads and parses the cell voltage registers
//
//*****************************************************************************
void rdatac_parse(uint32_t rx_data[19], // Controls which cell voltage register is read back.
					uint32_t rx_output[8], // Array of the parsed cell codes
					uint32_t rx_status[3]
					)
{

  uint32_t parsed_cell;
  uint32_t data_counter = 3; //data counter
  uint32_t current_cell;

  	  //a.i
  	  	  for(current_cell = 0; current_cell<3; current_cell++)
  	  	  {
  	  		  rx_status[current_cell] = rx_data[current_cell];
  	  	  }

        //a.ii
		for(current_cell = 0; current_cell<8; current_cell++)	 	// This loop parses the read back data into cell voltages, it
        {														   		  			// loops once for each of the 3 cell voltage codes in the register

          parsed_cell = rx_data[data_counter+1] + (rx_data[data_counter] << 8);//Each cell code is received as two bytes and is combined to
																					 // create the parsed cell voltage code

          rx_output[current_cell] = parsed_cell;
          data_counter = data_counter + 2;											 //Because cell voltage codes are two bytes the data counter
																					//must increment by two for each parsed cell code
        }

}
//*****************************************************************************
//
// Read ADS Registers
//
//*****************************************************************************
void ads_rreg(void){
	//Read all Registers
	uint32_t rregOP[2];
	rregOP[0] = 0x20;
	rregOP[1] = 0x14;
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, 0); //CS low
	spi_write(2,rregOP);
	spi_read(21, cfg_reg);
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, GPIO_PIN_0); //CS high
}


//*****************************************************************************
//
// Initialize
//
//*****************************************************************************
void ads_init(void){
	//Reset Pulse
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_1, 0);
	SysCtlDelay(10); //minimum pulse width timing specifications
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_1, GPIO_PIN_1);
	SysCtlDelay(400); //18 tclk cycles to complete initialization (9252ns ~ 10us)


	//Send SDATAC Command
	opcode_tx[0] = 0x11;
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, 0); //CS low
	spi_write(1, opcode_tx);
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, GPIO_PIN_0); //CS high
}

//*****************************************************************************
//
// Write ADS Config Registers
//
//*****************************************************************************
void ads_config(uint32_t config1, uint32_t config2, uint32_t config3){
	uint32_t refconfig1[3];
	uint32_t refconfig2[3];
	uint32_t refconfig3[3];

	//Config1
	refconfig1[0] = 0x41;
	refconfig1[1] = 0x00;
	refconfig1[2] = config1; //0x01
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, 0); //CS low
	spi_write(3, refconfig1);
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, GPIO_PIN_0); //CS high

	//Config2
	refconfig2[0] = 0x42;
	refconfig2[1] = 0x00;
	refconfig2[2] = config2; //0x60
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, 0); //CS low
	spi_write(3, refconfig2);
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, GPIO_PIN_0); //CS high

	//Config3
	refconfig3[0] = 0x43;
	refconfig3[1] = 0x00;
	refconfig3[2] = config3; //0xC0
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, 0); //CS low
	spi_write(3, refconfig3);
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, GPIO_PIN_0); //CS high
}

//*****************************************************************************
//
// Write ADS Config Channels
//
//*****************************************************************************
void ads_chnset(uint32_t channel_code[8]){
	//Set Channel Settings
	uint32_t CHnSET[3];
	CHnSET[0] = 0x45;
	CHnSET[1] = 0x00;
	CHnSET[2] = 0x10;

	uint32_t channel;
	for (channel = 0; channel <8; channel ++)
	{
		CHnSET[2] = channel_code[channel];
		GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, 0); //CS low
		spi_write(3, CHnSET);
		GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, GPIO_PIN_0); //CS high
		CHnSET[0]++;
	}
}


//*****************************************************************************
//
// MAIN
//
//*****************************************************************************
int main(void) {
	uint32_t read = 0;
	uint32_t i;

	g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
	SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |
	SYSCTL_CFG_VCO_480), 120000000);

	ConfigureUART();
	InitGPIO();
	InitSPI0();

	//Initialize ADS
	UARTprintf("\n\nInitialized. Starting ADS130E08.\n");
	ads_init();

	//Read Registers
	ads_rreg();
//	UARTprintf("\nRREG:");
//    for(i = 0; i < 21; i++){
//    	UARTprintf(" %x,", cfg_reg[i]);
//    }

    //Configure ADS
	ads_config(0x01,0x73,0xcc);
	//INTERNAL DC TEST		- (0x01,0x73,0xcc)
	//INTERNAL PULSED TEST	- (0x01,0x70,0xcc)
	//EXTERNAL MEASURE		- (0x01,0x60,0xcc)

	//Set Channel Settings
	channel_code[0] = 0x10;
	channel_code[1] = 0x11;
	channel_code[2] = 0x11;
	channel_code[3] = 0x11;
	channel_code[4] = 0x11;
	channel_code[5] = 0x11;
	channel_code[6] = 0x11;
	channel_code[7] = 0x11;
	ads_chnset(channel_code);
	//Gain 1	- 0x10
	//Shorted	- 0x11
	//TEST		- 0x15

	//Read Registers
	ads_rreg();
//	UARTprintf("\nRREG:");
//    for(i = 0; i < 21; i++){
//    	UARTprintf(" %x,", cfg_reg[i]);
//    }

    //START
	GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_6, GPIO_PIN_6);
	SysCtlDelay(400); //Wait


	//
	//Read Data Continuously
	//
	//RDATAC
    opcode_tx[0] = 0x10;
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, 0); //CS low
	spi_write(1, opcode_tx);
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, GPIO_PIN_0); //CS high

//	UARTprintf("\n\nRDATA CONTINUOUS");
	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, 0); //CS low

	//
//	uint32_t pui32DataRx2[19];
//    while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx2[0]))
//    {
//    }
//	SSIConfigSetExpClk(SSI0_BASE, g_ui32SysClock, SSI_FRF_MOTO_MODE_1,
//	SSI_MODE_MASTER, 2000000, 8);
    //
	read = 1;

	while(1)
    {
//    		UARTprintf("\n\nWaiting for DRDY high........");
    		while(GPIOPinRead(GPIO_PORTM_BASE, GPIO_PIN_6)){
    		//Wait for DRDY to be high
    			read = 0;
    			for(i=0;i<2;i++)
    				read = 0;
    		}
//    		UARTprintf("Done.");
//    		UARTprintf("\nWaiting...");
    		uint32_t pui32DataRx2[19];
    		while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx2[0]))
    		{
    		}

    		do{
    			if (GPIOPinRead(GPIO_PORTM_BASE, GPIO_PIN_6) == 0){
    				spi_read(19, rdata_rx);
    				read = 1;
    				rdata_rx[0]=0;
//    				UARTprintf("Read.");
    			}
    			else{
//    				UARTprintf(".");
    			}
    		}while(read == 0);

//  		rdatac_parse(rdata_rx, rx_data, rx_stat);

//    		UARTprintf("\nReceived :");
//    		for(i = 0; i < 8; i++){
//    	    	UARTprintf(" %i,", rx_data[i]);
//    	    }
//
//    		UARTprintf("\nSTATUS :");
//    	    for(i = 0; i < 3; i++){
//    	    	UARTprintf(" %x,", rx_stat[i]);
//    	    }

    }

	GPIOPinWrite(GPIO_PORTH_BASE, GPIO_PIN_0, GPIO_PIN_0); //CS high

	//
	// Return no errors
	//
	return (0);
}


Thanks,

Stephen

  • Hello Stephen,

    Some more analysis data is needed from a HW side.

    > What is the CPSR and SCR being set to in the SSI Instance?
    > What is the Actual Frequency being seen on the bus?
    > Is the result same with the HSE bit being set or clear in the SSICR1 register?
    > Also can you confirm that the data from the Slave is to be sampled on the falling edge and that the setup and hold as expected by TM4C129 being met at the IO boundary?

    Regards
    Amit
  • The SCLK seems to jitter between 8MHz and 12MHz per message frame for 10MHz setting.

    1) I have not been manually setting CPSR and SCR because I was using 

    SSIConfigSetExpClk(SSI0_BASE, g_ui32SysClock, SSI_FRF_MOTO_MODE_1, SSI_MODE_MASTER, 10000000, 8);

    2) Do I modify the HSE bit in the ssi.c file or should I move everything over to my main code?

    3) The logic analyzer shows MOSI sending out correct data. So we can confirm that it works in one direction. It's just MISO not getting any response.

    I've attached a screenshot of what transmission looks like at 10MHz

    Thanks,

    Stephen

  • Hello Stephen,

    1. The variation of clock on the LA may be an artifact of capture. Since it is a digital divider and as long as the System Clock is not changed the frequency shall be 10MHz.
    2. You can enable HSE bit after SSIEnable in the InitSPI0 function. But now that is not important as the MOSI is being sent and MISO is not being received. The MISO line is in fact flat line, which would mean that the Slave device did not see the command at all and so does not know what to do. And as you said this happens above 1.2MHz. Is that right?
    3. Also since the MISO is flatline it would make sense to put a Active Scope Probe (reduce the load on the PCB trace) as close as possible to the Slave IC to see if the timing as sent by the TM4C129 are sufficient for the Slave device to meet the setup-hold margins on the slave.

    Regards
    Amit
  • Hi Amit,

    Thanks for the prompt responses.

    1) That makes sense, I was able to verify with an oscilloscope.

    2) Correct. The slave (ADS130E08) stops working properly above 1.2MHz

    3) I hooked up the oscilloscope to the  header pins on the eval board. The SCLK signals are pretty bad... I think it is from the ribbon cable I used (30cm long)

    Could this be the root cause of the problem? How long can the ribbon cable from TIVA to eval board be?

    Thanks,

    Stephen

    See attached pictures.

  • Hello Stephen,

    Purely, my experience with SSI and cables have been not so good either. Never have I tried more than 3" of SSI and expected higher frequency of operation. I prefer to do a solid PCB with SSI (no cables) and ensure that route delays are well managed.

    Regards
    Amit
  • Hi Amit,

    I cut the cable to 2" and set SCLK to 5MHz. Results seem to be the same (only 1MHz works, higher frequency I get data but they are wrong)

    #2 Problem which I think may relate to clock or SSI register

    SSIDataGet seems to get new set of information before the original is complete.

    Is this the slave's doing? or TIVA SSI register not clearing properly? I use SSIDataGetNonBlocking to empty FIFO.

  • Strictly an aside - but our tech group "has" succeeded w/IDC ribbon cable at lengths up to 250mm & at clock rates of 10MHz.   One trick we've found - is to interpose ground between each/every signal lead.  (thus you can accommodate 4 signal SPI w/a standard 2x5 IDC cable/header assembly - power routed separately)

    You may also consider CAT 5/6 cable for high frequency work.

    You once mentioned an Eval Board.   That ADC may be too demanding for prototype work - is it part of a pro eval board?  And have you recently measured the power - at the ADC IC?   (and while running @ full speed?)  

    From experience - we know Amit's SW works - it's unlikely the ADC has a design glitch - that points (heavily I'd say) at your ADC board/cabling/powering!  

    As always - a 2nd ADC board would "save" Amit - other hapless helpers - from (always dreaded) single board anomaly...

    (btw - that 2nd scope cap showed much improved signal edges - I'd wager good stack of chips your issue is ADC layout, interconnect or power problem.   Always blame yourself first - chips last!)

  • Thanks for the input CB1.

    I agree with you guys. The wiring to the ADS130E08 eval board is most likely the culprit.

    It just seems odd that I'm having such an issue with something that was supposed to be straightforward!

  • Stephen Keng said:
    ...seems odd that I'm having such an issue with something...supposed to be straightforward!

    Beware (always) when the boss, key client or beloved G'ovt declares something as easy/straightforward!

    ADC board in your photo looks "real" - yet the multi-color ribbon is (likely) too long & the gray ribbon appears (pardon) unkempt - unlikely to support high speed signals!  If you can some way/how configure your interconnect ribbon as: "G-1-G-2-G-3-G-4-G-G" where G is ground & each # represents an SPI signal - things (likely) will improve. 

    Another concern - marketing chose "launch" for your MCU board - but it very well may be that it's unable to adequately power a serious accessory board - especially when you, "Call for Speed!"  As past stated (my earlier post) have you measured the ADC's power pins - when operating at high speed?  Under demanding conditions - your launch board may reveal itself as, "slight push" pad.  (and - that too - has a neat ring...)

    Yours is (I believe) a system-design issue - unlikely an MCU nor ADC one - and you must "bullet-proof" all elements to gain full performance.  

    And - rarely - does that prove, "straightforward!.."  (despite boss/client/your best hope assurances...)

  • Hello Stephen,

    It is not odd at all, unlike the original post in the Precision Forum where it was put as a Firmware Issue. Ribbon cables are good for clean connectivity instead a mangled bunch of wires, but speed is a restriction if not the correct cable is used.

    Regards
    Amit
  • Hi Amit,

    Might you comment upon the (likelihood) of user's launchboard successfully powering that accessory ADC board - while the MCU is cranked up to (near) full MCU System Clock?

    Repeatedly - over a decade - we've witnessed clients' "pain/suffering" so often caused by, "simple-minded - often inadequate, multi-board, power schemes!" Almost always they "checked power" @ power up - prior to, "putting pedal to the metal" and the (failed) results very closely tracked the degree of voltage deviation from specification! (even worse - DMM could not detect the Vdd "dips" - which tracked major peripheral data rates - and which revealed "instantly" when a scope attached...)

    Again - we see proper operation from your firm's MCU & ADC - lack of system design execution on client/user side - and that's straightforward...
  • Hello cb1,

    The LaunchPad has sufficient current to power the ADC unless it is decided to have more components off it. My main concern is based on the setup are the wires to the ADC rather than the ADC itself. In the past I have been bitten by SSI free wires on the Macronix Flash which when replaced by a board connected to the header's worked flawlessly.

    Regards
    Amit
  • Hi guys,

    Thanks for the involvement.

    I made new cables and the SCLK are much better.

    As before, I have always been able to read and write to the ADS. It's just the quality of data returned is not good.

    Having set the SCLK at 10kHz. I noticed that spi_read doesn't play well with the DRDY (data ready bit) of the ADS. It appears that if I don't read all the data before DRDY triggers again. The data is lost.

    I've sent a post to the Precision Data Converter Thread.

    Cheers,

    Stephen