We employ a ADS8686 16 ch ADC in one of our designs. Most of the time the ADC reads the entire sequence of channels properly. However, occasionally it appears to read one sequence only and repeats that sequence reading instead of reading all channels. I'm not sure what is causing this or how to reproduce it reliably. But once it starts repeating the seq0 readings in stays in that mode.
Background:
Analog VDD=5 VDC, Digital VDD=3.3 VDC, schematic attached.
Out HW has configured the ADS8686 to operate in serial SPI, 1 wire mode. The chip goes through a long reset at power up. We run the SPI bus @ 4 MHz and are able to program the config registers to run in SW configured sequence of 5 sequences that are read back with one CONVST in Burst mode. I have confirmed that the sequence configuration is correct by reading back all programmed registers and confirming that the values read back match the programmed values. The configuration register contents remain correct even when the ADC readings are bad. At no point do we reset the chip after the initial configuration. At the end of the initial ADC configuration, we do a dummy ADC reading of our entire sequence of 10 ADC channels and confirm that the data read is proper. We can tell this because Ch4A is programmed to always output 0xAAAA, and when the readings are good, this channel reads 0xAAAA as expected. I have (see the SPI timing screen shot attached. BTW: the CLK signal going to the ADS8686 is actually inverted from what is shown on the Saleae screen shot.)
Once initialized the subsequent ADC readings also produce expected results. However after a some time and for an unknown reason, the ADS8686 starts returning 1 sequence worth of data which repeats 5 times during our read. In fact, even the one sequence data appears to be very close to the CH_A0 data that is repeated for CH_B0 and the subsequent CH_AX & CH_BX values. (when accounting for the different ranges that CH_As and CH_Bs had.) I have confirmed that when the ADC readings are bad (repetitive CH A0 ) that the SPI readings and CONVST, BUSY timing on the scope are the same as the normal reading case. Jus the data contents are garbage.
My ADC initialization and readADC functions are both attached.
We also found out that if we generate two successive CONVST pulse and wait for the BUSY line to indicate “conversion complete”, then our intermittent read problem frequency gets harder to reproduce. But we don’t quite know why nor can we be sure that this is a real solution to our problem.
Thank you for your help,
Fred
void InitADC(void) { unsigned char cmd[2]; unsigned char txBuf[ADC_COUNT * 2]; // First byte = read cmd, rest = garbage // Ensure that we get a FULL RESET (> 1.2 uS) since InitIO call put us in reset DEASSERT_ADC_RESET; // First real CONVST must wait 15 mS AFTER the reset is De-asserted __delay_cycles(DELAY_100US); // SW Mode Sequencer Configuration Steps(From ADS8686S manual section 7.4.2.5.2) // Step 1: Program the Analog Input range for each channel. We use A0-A4 & B0 - B4. // All channels use a 10 V range which is the default range. So no additional init is required. // Step 2: Program the sequencer stack registers to selectc sequence channels // Seq_stk_0 is already ADC_CH_A0 & ADC_CH_B0 after reset // configure the ADC_REG_SEQ_STK_0 cmd[0] = ADC_WRITE + ADC_REG_SEQ_STK_0; cmd[1] = (ADC_CH_A0 + ADC_CH_B0 ); gpSpiTxBuf = cmd; WriteGPSPI(GS_BLD_ADC); // Write the Register // Start by configuring the ADC_REG_SEQ_STK_1 cmd[0] = ADC_WRITE + ADC_REG_SEQ_STK_1; cmd[1] = (ADC_CH_A1 + ADC_CH_B1 ); gpSpiTxBuf = cmd; WriteGPSPI(GS_BLD_ADC); // Write the Register // configure the ADC_REG_SEQ_STK_2 cmd[0] = ADC_WRITE + ADC_REG_SEQ_STK_2; cmd[1] = (ADC_CH_A2 + ADC_CH_B2 ); gpSpiTxBuf = cmd; WriteGPSPI(GS_BLD_ADC); // Write the Register // configure the ADC_REG_SEQ_STK_3 cmd[0] = ADC_WRITE + ADC_REG_SEQ_STK_3; cmd[1] = (ADC_CH_A3 + ADC_CH_B3 ); gpSpiTxBuf = cmd; WriteGPSPI(GS_BLD_ADC); // Write the Register // configure the ADC_REG_SEQ_STK_4. Note CH_A4 is not used. // Step 3: Add the SSREN marker for the last sequence cmd[0] = ADC_WRITE + ADC_REG_SEQ_STK_4 + SSREN; cmd[1] = (ADC_CH_A_AAAA + ADC_CH_B4 ); // Note that A4 is used as a test input gpSpiTxBuf = cmd; WriteGPSPI(GS_BLD_ADC); // Write the Register // Step 4: Set SEQEN in the config register cmd[0] = ADC_WRITE + ADC_REG_CONFIG; cmd[1] = SEQEN + OSR_2 + BURSTEN; gpSpiTxBuf = cmd; gpSpiTxCount = 2; WriteGPSPI(GS_BLD_ADC); // Step 5A: Provide a dummy CONVST pulse ASSERT_CONVST; txBuf[0] = ADC_READ; // Adding an instruction inbetween to ensure CONVST pulse is long enough gpSpiTxBuf = txBuf; DEASSERT_CONVST; while(P2IN_bit.P2IN6); // Wait till conv completes. // Step 6: Assert another CONVST then read back the conversion results ASSERT_CONVST; gpSpiRxBuf = adcResults; // Adding an instruction inbetween to ensure CONVST pulse is long enough gpSpiRxCount = (ADC_COUNT * 2); DEASSERT_CONVST; while(P2IN_bit.P2IN6); // Wait till conv completes. ReadGPSPI(GS_BLD_ADC); // Reading all 5 sequences in BURST mode // Look for unexpected results if ( (adcResults[16] != 0xAA) || (adcResults[17] != 0xAA)) __no_operation(); // For Debug breakpoint only } // This is the example of ADC reading with 2 CONVST pulses. Normally only one CONVST is applied. void ReadADCs(void) { unsigned char txBuf[ADC_COUNT * 2]; // First byte = read cmd, rest = garbage readADCError = false; ASSERT_CONVST; // Conversion starts at the CONVST rising edge gpSpiRxCount = (ADC_COUNT * 2); // Put other instructions here while converting gpSpiRxBuf = adcResults; DEASSERT_CONVST; while(P2IN_bit.P2IN6); // Wait till conv completes. (BUSY pin) // CONVST needs to stay asserted only 50 nS. Measured to stay asserted for at // least 275 nS with the one instruction inbetween ASSERT_CONVST; // Conversion starts at the CONVST rising edge txBuf[0] = ADC_READ;; gpSpiTxBuf = txBuf; // Contents of TXBuf can remain garbage. DEASSERT_CONVST; while(P2IN_bit.P2IN6); // Wait till conv completes. ReadGPSPI(GS_BLD_ADC); // Call the SPI IO handler with SPI CH name // Look for unexpected results if ( (adcResults[16] != 0xAA) || (adcResults[17] != 0xAA)) { memset(adcResults, 0x00, ADC_COUNT * 2); readADCError = true; } __no_operation(); // For Debug breakpoint only } //////////////////////////////////////////////////////////////////////////// // The header file contents: // ADS8686 ADC Register definitions #define ADC_READ (0x00) #define ADC_WRITE (0x80) #define ADC_REG_CONFIG (0x02 <<1) #define ADC_REG_CH_SEL (0x03 <<1) #define ADC_REG_RNG_A1 (0x04 <<1) #define ADC_REG_RNG_A2 (0x05 <<1) #define ADC_REG_RNG_B1 (0x06 <<1) #define ADC_REG_RNG_B2 (0x07 <<1) #define ADC_REG_STATUS (0x08 <<1) #define ADC_REG_ORS_A (0x0A <<1) #define ADC_REG_LPF_CONF (0x0D <<1) #define ADC_REG_DEV_ID (0x10 <<1) #define ADC_REG_SEQ_STK_0 (0x20 <<1) #define ADC_REG_SEQ_STK_1 (0x21 <<1) #define ADC_REG_SEQ_STK_2 (0x22 <<1) #define ADC_REG_SEQ_STK_3 (0x23 <<1) #define ADC_REG_SEQ_STK_4 (0x24 <<1) #define ADC_REG_SEQ_STK_5 (0x25 <<1) #define ADC_REG_SEQ_STK_6 (0x26 <<1) #define ADC_REG_SEQ_STK_7 (0x27 <<1) #define ADC_REG_SEQ_STK_8 (0x28 <<1) #define ADC_REG_SEQ_STK_9 (0x29 <<1) #define ADC_REG_SEQ_STK_10 (0x2A <<1) #define ADC_IN_RNG_2P5 0x01 // +- 2.5 V Range #define ADC_IN_RNG_5 0x02 // +- 5 V Range #define ADC_IN_RNG_10 0x03 // +- 10 V Range // ADC_REG_SEQ_STK_? Regs: #define ADC_CH_A0 0x00 #define ADC_CH_A1 0x01 #define ADC_CH_A2 0x02 #define ADC_CH_A3 0x03 #define ADC_CH_A4 0x04 #define ADC_CH_A5 0x05 #define ADC_CH_A6 0x06 #define ADC_CH_A7 0x07 #define ADC_CH_B0 0x00 #define ADC_CH_B1 0x10 #define ADC_CH_B2 0x20 #define ADC_CH_B3 0x30 #define ADC_CH_B4 0x40 #define ADC_CH_B5 0x50 #define ADC_CH_B6 0x60 #define ADC_CH_B7 0x70 #define ADC_CH_A_AVDD 0x08 #define ADC_CH_B_AVDD 0x80 #define ADC_CH_A_ALDO 0x09 #define ADC_CH_B_ALDO 0x90 #define ADC_CH_A_AAAA 0x0B #define ADC_CH_B_5555 0xB0 #define SSREN 0x01 //Config Reg: #define SDEF 0x80 #define BURSTEN 0x40 #define SEQEN 0x20 // Bits 4-2: OSR #define OSR_0 (0x00 <<2) // No oversampling / OS Disbaled #define OSR_2 (0x01 <<2) #define OSR_4 (0x02 <<2) #define OSR_8 (0x03 <<2) #define OSR_16 (0x04 <<2) #define OSR_32 (0x05 <<2) #define OSR_64 (0x06 <<2) #define OSR_128 (0x07 <<2) #define STATUSEN 0x02 #define CRCEN 0x01 // The ADC sequence conversion order #define AIN_0A 0 #define AIN_0B 1 #define AIN_1A 2 #define AIN_1B 3 #define AIN_2A 4 #define AIN_2B 5 #define AIN_3A 6 #define AIN_3B 7 #define AIN_4A 8 #define AIN_4B 9