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.

ADC128D818 - Possible reason for occasional channel misread?

Other Parts Discussed in Thread: ADC128D818

Summary of the Problem

We produce an industrial high speed document scanner which is controlled by an embedded PC. Our embedded PC board is based on the Xilinx Zynq (ZC702) board, running VxWorks on an ARM processor. The board has an ADC128D818 and we communicate with it via IIC to monitor six voltage levels and temperature on the board.

We run the ADC in mode 0 with continuous conversion and internal VREF. We don't use interrupts with the ADC, and simply read the various channel data periodically for our own analysis. We emit an informational warning message if any of the voltages or the temperature is out of tolerance from what we expect (we permit up to 5% error for voltages, and 150 C for temperature). The voltage readings are typically less than 1% from expected and the temperature is typically around 28 C. We really expect never to see a warning message.

But occasionally, a warning message will indeed appear, typically triggered as a voltage out-of-tolerance warning. I seriously doubt that the voltages were really out of tolerance, and what really happened was that the data was wrong. Here are a couple of samples of what was read, from two different systems:

   Channel:   IN0    IN1    IN2    IN3    IN4    IN5    TEMP

  Expected:  1.000  1.500  1.800  2.500  3.300  5.000   <150

Instance 1:  0.995  1.502  1.502  2.504  3.368  5.035   34.0

Instance 2:  0.994  0.994  1.794  2.516  3.355  5.053   30.0

For each read cycle, the channels are read one after the other, in order, IN0, IN1, ... , IN5, TEMP. Note the anomalous red underlined values. In each case, the value read for that channel was EXACTLY the value returned in the previous channel read (1.502 was read for IN1 but then also reported for IN2; and 0.994 was read for IN0 but then also reported for IN1). This isn't a data handling error in our software. These are the actual values reported by the read command issued via IIC (scaled to meaningful voltage and temperature levels).

When we see this, it is always just a single read that is in error, and the next read cycle shows readings that are as expected

How could this possibly happen?

Device Initialization

The task that interfaces with the ADC128D818 initializes it and then falls into a read loop. The loop repeats once every ten seconds (very slow). This is what we are doing to initialize the device. Numbers at left margin are recommended steps in device Quick Start documentation, added for reference. This code was all plunked into the application from a piece of sample code obtained from who-knows-where without knowing what it really does. I inherited this and am trying to understand it. This is my analysis of what the code is doing.

The "wr" and "rd" calls are all IIC write and IIC read calls (see next section for detail) and I have shown the register and data bytes written and read.

    call init():

      - initialize iic driver:
          XIicPs_LookupConfig()
          XIicPs_CfgInitialize()
          XIicPs_SetSClk() 100000

2     - read busy status reg    // wr 0x0c, rd 1 byte // ADC_REG_Busy_Status_Register

      - stop()                  // wr 0x00, wr 0x00 // ADC_REG_Configuration_Register : clr START, clr INT_Enable

3     - program adv cfg reg     // wr 0x0b, wr 0x00 // ADC_REG_Advanced_Configuration_Register : ADC_MODE_0, ADC_VREF_INT

4     - program conv rate reg   // wr 0x07, wr 0x01 // ADC_REG_Conversion_Rate_Register : sel ADC_RATE_CONTINUOUS

5     - enable/disable channels // wr 0x08, wr 0x40 // ADC_REG_Channel_Disable_Register : 0x40 (disable IN6)

6     - mask interrupt status   // wr 0x03, wr 0x7f // ADC_REG_Interrupt_Mask_Register : 0x7f (unmask only TEMP)

7   call init_limit()           // wr 0x2a, wr 0x80 // IN0 High :
    call init_limit()           // wr 0x2c, wr 0xa6 // IN1 High :
    call init_limit()           // wr 0x2e, wr 0x80 // IN2 High :
    call init_limit()           // wr 0x30, wr 0x80 // IN3 High :
    call init_limit()           // wr 0x32, wr 0x80 // IN4 High :
    call init_limit()           // wr 0x34, wr 0x80 // IN5 High :
    call init_limit()           // wr 0x36, wr 0x80 // IN6 High :
    call init_limit()           // wr 0x38, wr 30 // TEMP High :
    call init_limit()           // wr 0x39, wr 29 // TEMP Hyst :

?   call read_register()        // wr 0x0b, rd 1 byte // ADC_REG_Advanced_Configuration_Register
?   call read_channel( 0x0c )   // wr 0x2c, rd 2 bytes // bug? meant 1 byte from ADC_REG_Busy_Status_Register?

8,9 call start()                // wr 0x00, wr 0x03 // ADC_REG_Configuration_Register : START=1, INT_Enable=1, INT_Clear=0

Data Acquisition

This is how we acquire the data after the device is initialized. The loop repeats indefinitely, but is only kicked once every 10 seconds:

    loop:

        wait for semaphore (given every 10 sec)

        call read_channel( IN0 )  // wr 0x20, rd 2 bytes // IN0 Reading
        call read_channel( IN1 )  // wr 0x21, rd 2 bytes // IN1 Reading
        call read_channel( IN2 )  // wr 0x22, rd 2 bytes // IN2 Reading
        call read_channel( IN3 )  // wr 0x23, rd 2 bytes // IN3 Reading
        call read_channel( IN4 )  // wr 0x24, rd 2 bytes // IN4 Reading
        call read_channel( IN5 )  // wr 0x25, rd 2 bytes // IN5 Reading
        call read_channel( TEMP ) // wr 0x27, rd 2 bytes // TEMP Reading

        call read_register()      // wr 0x01, rd 1 byte  // ADC_REG_Interrupt_Status_Register

Read/Write Routines

Here is the C++ code, stripped and simplified, that interfaces with the IIC calls in the BSP:

These routines are called to perform "wr" and "rd" operations above.
Note in particular the delay in the write routine.

    int ADC128D818::I2CWrite( int addr, u8 *data, u16 ByteCount )
    {
        if( XIicPs_MasterSendPolled( &IicInstance, data, ByteCount, addr ) != XST_SUCCESS ) return XST_FAILURE;
        while( XIicPs_BusIsBusy( &IicInstance ) );
        taskDelay( gIicWriteDlyMs ); // gIicWriteDlyMs = 300 ms // was usleep( 250000 )
        return XST_SUCCESS;
    }

    int ADC128D818::I2CRead( int addr, u8 *BufferPtr, u16 ByteCount )
    {
        if( XIicPs_MasterRecvPolled( &IicInstance, BufferPtr, ByteCount, addr ) != XST_SUCCESS ) return XST_FAILURE;
        while( XIicPs_BusIsBusy( &IicInstance ) );
        return XST_SUCCESS;
    }

I can furnish the exact code for the initialization and data acquisition if desired (it's just a little prolix).

  • Hi Mark,

    I don't see anything that stands out as being wrong. Could you take out the two lines that you have labeled "?". I don't think they are needed. Just to make sure that they are not causing a problem.

    Mike
  • I can certainly do that. In fact, I have additional questions about how we are initializing the device.

    (1) It looks to me like we are enabling the temperature interrupt, but we aren't using it (no intConnect() call). That seems like an inherently bad practice to me. Should I just mask out ALL of the interrupts from propagating to the INT line, writing register 0x03 (ADC_REG_Interrupt_Mask_Register) with data value 0xff?

    (2) Since we aren't using interrupts, is there really any reason to write anything at all to any of the limit registers 0x2A-0x39? The specified limit values don't affect the actual data returned, do they?

    (3) There were three more occurrences logged over the weekend. Do you think it is possible that the temperature limit being set to 30, in conjunction with the temperature interrupt occurring with every read that is over the limit, could be interfering with the operation of the device? If so, should I specify some very high limit which would never be reached in order to prevent it? Here are the most recent values, FYI:

       Channel:   IN0    IN1    IN2    IN3    IN4    IN5    TEMP

      Expected:  1.000  1.500  1.800  2.500  3.300  5.000   <150

    Instance 1:  0.994  1.505  1.794  2.516  3.355  3.355   30.000

    Instance 2:  0.994  0.994  1.794  2.516  3.355  5.054   30.000

    Instance 3:  0.994  1.505  1.795  2.516  3.355  3.355   30.500

    I mention also that these instances occurred after the system had been running for 12 hr 5 min, 13 hr 46 min, and 24 hr 16 min, and polling once every 10 seconds the whole time. So it is not a common occurrence -- thousands of reads occurred before the problem appeared (same with the first data set).

  • Hi Mark,

    1. I think that would be a good idea if you are not using the temp int. As you mention, it just seems like it could cause trouble. On steps 8 & 9 you can also set bit 1 to 0 since the interrupt pin is not being used.
    2. That is correct. They don't affect the data and since the interrupt is not being used they don't need to be set.
    3. That is a possibility, since the temp being read is 30 or higher. You could try both, setting the limit very high and not setting it at all, since the interrupt is not being used.

    Mike
  • I modified the initialization and let the system run overnight. There were no instances of the misread, so this is a good sign since I typically see a couple of them by the next morning. I admit that this isn't much evidence, but it is certainly promising. I'll repeat the test overnight and over the weekend. These are the changes I made, following your advice:

    • don't enable the INT pin (write register 0x00 with the value 0x01 rather than 0x03)
    • mask ALL interrupts (write register 0x03 with the value 0xff rather than 0x7f)
    • don't write to any of the limit registers 0x2a-0x39
    • omit the buggy reads (marked with '?' in the original question)

    I'll continue to monitor and, if I don't see the problem again, will accept your answer. Thanks for taking time to consider my question.

  • Alas, I observed another occurrence this morning after the system ran overnight. So the changes I made didn't fix the problem. I guess I can put in a filter so that I only issue the warning if an out-of-tolerance read occurs for (say) five successive reads. That will hide the problem, but I sure would like to know why this is happening.
  • Hi Mark,

    Another thought I had is maybe the data is somehow being read incorrectly. To find out if that is happening, somehow you would need to monitor the I2C lines and then when the micro detects the double read, save the waveforms and see if they match the data read. That way we could see if the double data is actually coming from the ADC128D818 or if it is noise or not being read quickly enough by the processor or something else.

    Mike