Post Go back to editing

AD4111 – Basic Configuration for Single-Ended Voltage Reading on VIN0 with STM32

Category: Software
Product Number: AD4111
Software Version: STM32 with Cube IDE

Hello,

I am working with the AD4111 ADC interfaced via SPI to an STM32 microcontroller using bit-banged SPI.

I want to read a single-ended analog voltage input on VIN0. Could you please confirm the minimum required register configurations for this mode?

Specifically:

  1. Which channel setup is needed to read VIN0 as single-ended input (e.g., AIN0-AGND or pseudo-differential with AINCOM)?

  2. What Mode Register configuration is recommended for simple polling-based reading?

  3. What default Setup Register settings (gain, reference) are safe to start with for 0–5V inputs using internal reference?

Currently, I am able to read the ID register successfully via SPI but want to ensure my channel, setup, and mode registers are configured correctly before starting ADC reads.

Any simple initialization sequence example for single-ended voltage reading would be very helpful.

Thank you for your support.

Manohar Emandi

Edit Notes

NA
[edited by: ManoharCapuf at 1:03 PM (GMT -4) on 24 Jul 2025]
  • Hi  ,

    Please see below the suggested register configurations for a basic Single-Ended voltage measurement and ADC configuration flow.

    ADC Mode: 0xA004 - Enable internal reference, Continuous conversion mode

    Interface Mode: 0x0000 - default state only, enable DATA_STAT bit if more than 1 channel will be used

    CH0: 0x8010 - Enable CH0, Select VIN0,VINCOM

    SETUPCON0: 0x1320 - Bipolar coding, Enable input buffers, Internal 2.5V reference

    FILTCON: 0x0500 - default state only, you may configure this depending on the required output data rate and sinc filter type.

    • Which channel setup is needed to read VIN0 as single-ended input (e.g., AIN0-AGND or pseudo-differential with AINCOM)?

    • What Mode Register configuration is recommended for simple polling-based reading?

    • What default Setup Register settings (gain, reference) are safe to start with for 0–5V inputs using internal reference?

    1. AIN0,AINCOM must be used as mentioned above.

    2. Continuous conversion mode can be used.

    3. The configuration above can be used.

    One thing to note is that an internal full-scale calibration must be performed when using voltage input channels.

    Regards,

    JC

  • Hi JC,

    Thank you for your clear recommendations. I implemented the suggested configuration and here is my code snippet and readback results:

     void AD4111_BasicConfig(void)
     {
         uint32_t readback = 0;
    
         // ADCMODE Register (0x01): 0xA004
         AD4111_SetRegisterValue(0x01, (0xA004), 2);
         readback = AD4111_GetRegisterValue(0x01, 2);
         DEBUG_PRINT("ADCMODE (0x01) set to: 0x%04lX\r\n", readback);
    
    //      IFMODE Register (0x02): 0x0000 (default)
         AD4111_SetRegisterValue(0x02, (0x0000), 2);
         readback = AD4111_GetRegisterValue(0x02, 2);
         DEBUG_PRINT("IFMODE (0x02) set to: 0x%04lX\r\n", readback);
    //
         // CH0 Register (0x10): 0x8010 (Enable CH0, VIN0-VINCOM)
         AD4111_SetRegisterValue(0x10, 0x8010, 2);
         readback = AD4111_GetRegisterValue(0x10, 2);
         DEBUG_PRINT("CH0 (0x10) set to: 0x%04lX\r\n", readback);
    //
         // SETUPCON0 Register (0x20): 0x1320 (Bipolar, Buffers enabled, internal 2.5V ref)
         AD4111_SetRegisterValue(0x20, 0x1320, 2);
         readback = AD4111_GetRegisterValue(0x20, 2);
         DEBUG_PRINT("SETUPCON0 (0x20) set to: 0x%04lX\r\n", readback);
    //
         // FILTCON0 Register (0x28): 0x0500 (default filter config)
         AD4111_SetRegisterValue(0x28, 0x0500, 2);
         readback = AD4111_GetRegisterValue(0x28, 2);
         DEBUG_PRINT("FILTCON0 (0x28) set to: 0x%04lX\r\n", readback);
    
         DEBUG_PRINT("AD4111 Basic configuration completed.\r\n");
     }


    Readback Results:

    Register Written Value Readback Value
    ADCMODE (0x01) 0xA004 0xA005
    IFMODE (0x02) 0x0000 0x0001
    CH0 (0x10) 0x8010 0x8011
    SETUPCON0 (0x20) 0x1320 0x1321
    FILTCON0 (0x28) 0x0500 0x0501

    I observed that bit0 is always read back as 1 in these registers despite writing 0.

    please confirm if this mismatch in bit0 readback is expected behavior for these registers.

    Thank you for your continuous support.

    Manohar Emandi

  • Hi  ,

    That is not an expected behavior. Kindly check item #8 of this Digital Interface FAQ - Sigma Delta ADC - Q&A - Precision ADCs - EngineerZone and see if it resolves your issue.

    Regards,

    JC

  • Hi JC,

    Thank you for your suggestion – it worked, and I really appreciate your support.

    I was able to read the raw data correctly in unipolar mode from 0V up to around 6V, but beyond that, the raw data seems to saturate and remains fixed. For example, the ADC raw average data is:

    AD4111 Raw Average Data: [3945711]

    when input voltage is above 6V.

    Additional Information:

    • I am using the internal reference (2.5V) as configured in SETUPCON0.

    • I have performed internal zero-scale calibration as well as internal full-scale calibration before reading the data.

    • My input is configured as VIN0 - VINCOM (single-ended).

    Iam attaching my ADC input schematic for your reference.
    My Schematic

    Could you please clarify:

    1. Why am I unable to read voltages up to 10V?

    2. Is there any configuration or external circuit scaling needed to measure a full 0–10V range with AD4111 in this setup?

    Thank you again for your continuous guidance.


    Manohar Emandi

  • Hi  ,

    Does the output data saturates even when bipolar coding is used? Does the Status Register flag any error?

    Is there any configuration or external circuit scaling needed to measure a full 0–10V range with AD4111 in this setup?

    The AD4111 should be able to measure a full 0-10V range. We would need to debug the find the source of the issue.

    Would it be possible to share a scope shot to see if the actual data measured is correct.

    Regards,

    JC

  • Hi JC,

    We found and fixed a hardware issue on our board, and now I’m able to read
    0–10V correctly on all channels in single-ended unipolar mode.

    Thanks again for your helpful support.

    While testing further, I noticed a behavior that I’d like your input on:

    When the ADC input pins are left floating (not connected to any signal), the ADC reports a voltage in the range of 2.5V to 2.7V. As soon as I connect a proper analog input, the readings become accurate.

    Is this behavior expected? Do you have any recommendations for handling floating analog inputs, such as using pull-downs or internal biasing, to avoid spurious voltage readings?

    Looking forward to your suggestions.

    Best regards,
    Manohar Emandi

  • Hi  ,

    When the ADC input pins are left floating (not connected to any signal), the ADC reports a voltage in the range of 2.5V to 2.7V. As soon as I connect a proper analog input, the readings become accurate.

    Yes, this is expected. This is due to voltage divider circuitry in the analog front-end. When VINx is disconnected then the divider will just form from AVDD and –VBIAS. And when VBIAS is connected to AVSS = 0V and AVDD = 5V. Then, the output of the divider will be around 2.5V. To avoid this during floating inputs, you may operate in ±2.5V split supply so that when VBIAS- = AVSS, AVSS = -2.5V, and AVDD = 2.5V then the output of the voltage divider is ~0V.

    Hope this clarifies your concerns.

    Regards,

    JC

  • Hi JC,

    Thank you again for your previous help, it was very helpful in resolving my earlier measurement issue.

    I now have another behavior I’d like your guidance on.

    I am using two functions to configure channels on the AD4111:

    • Voltage channels: CH0–CH5, 0–10 V, single-ended unipolar

    • Current channels: CH0–CH3, 0–20 mA

    What I observe:

    1. If I configure all channels as voltage, readings are accurate.

    2. If I configure all channels as current, readings are accurate.

    3. The issue occurs when I configure a mix of voltage and current channels (e.g., CH0 & CH1 as voltage, CH2 & CH3 as current). In this case, both the voltage and current readings become inaccurate.

    4. When all channels are set to voltage, if CH0 is configured as voltage, the other voltage channels also read correctly. If CH0 is not configured as voltage, other voltage channels give incorrect readings.

      My channel configuration functions:

      /*=================================================================================================
       *  AD4111_ConfigureVoltChannel
       *  Brief: Configures specified ADC channel for voltage input (0–10V).
       *================================================================================================*/
      void AD4111_ConfigureVoltChannel(uint8_t channel)
      {
          if (channel > ADC_CHANNEL6) {
              DEBUG_PRINT("Invalid channel: %d. Must be between 0 and 5.\r\n", channel+1);
              return;
          }
      
          uint32_t readback = 0;
          uint32_t id = AD4111_GetRegisterValue(0x07, 2); // ID Register
          DEBUG_PRINT("AD4111 ID Register: 0x%04lX\r\n", id);
      
          // ADCMODE (multi-channel)
          AD4111_SetRegisterValue(0x01, 0x8004, 2);
          readback = AD4111_GetRegisterValue(0x01, 2);
          DEBUG_PRINT("ADCMODE (0x01) set to: 0x%04lX\r\n", readback);
      
          // IFMODE
          AD4111_SetRegisterValue(0x02, 0x0840, 2);
          readback = AD4111_GetRegisterValue(0x02, 2);
          DEBUG_PRINT("IFMODE (0x02) set to: 0x%04lX\r\n", readback);
      
          //GPIOCON Register (0x06): 0x0800 to Enable SYNC_EN  : 0x00000
          AD4111_SetRegisterValue(0x06, (0x00000), 2);
          readback = AD4111_GetRegisterValue(0x02, 2);
          DEBUG_PRINT("GPIOCON (0x02) set to: 0x%04lX\r\n", readback);
      
          // Predefined channel enable values for CH0–CH5
          const uint16_t ch_reg_values[6] = {
              0x8010, // CH0: VIN0-VINCOM
              0x8030, // CH1: VIN1-VINCOM
              0x8050, // CH2: VIN2-VINCOM
              0x8070, // CH3: VIN3-VINCOM
              0x8090, // CH4: VIN4-VINCOM
              0x80B0  // CH5: VIN5-VINCOM
          };
      
          // Channel Enable
          uint8_t ch_reg_addr = 0x10 + channel;
          uint16_t ch_val = ch_reg_values[channel];
          AD4111_SetRegisterValue(ch_reg_addr, ch_val, 2);
          readback = AD4111_GetRegisterValue(ch_reg_addr, 2);
          DEBUG_PRINT("CH%d (0x%02X) set to: 0x%04lX\r\n", channel+1, ch_reg_addr, readback);
      
          // Setup and Filter Registers
          uint8_t setup_reg_addr = 0x20 + channel;
          uint8_t filt_reg_addr = 0x28 + channel;
      
          AD4111_SetRegisterValue(setup_reg_addr, 0x0320, 2); // Setup for unipolar
          readback = AD4111_GetRegisterValue(setup_reg_addr, 2);
          DEBUG_PRINT("SETUPCON%d (0x%02X) set to: 0x%04lX\r\n", channel+1, setup_reg_addr, readback);
      
          AD4111_SetRegisterValue(filt_reg_addr, 0x0500, 2); // Filter config
          readback = AD4111_GetRegisterValue(filt_reg_addr, 2);
          DEBUG_PRINT("FILTCON%d (0x%02X) set to: 0x%04lX\r\n", channel+1, filt_reg_addr, readback);
      
          //Set Quad Switch for channel
          EnableCurrentChannel(channel, GPIO_PIN_RESET);
          EnableVoltChannel(channel, GPIO_PIN_SET);
      
          DEBUG_PRINT("AD4111 Configuration completed for CH%d.\r\n", channel+1);
      }
      
      /*=================================================================================================
       *  AD4111_ConfigureCurrentChannel
       *  Brief: Configures specified ADC channel for 4–20 mA current input using internal burden resistor.
       *================================================================================================*/
      void AD4111_ConfigureCurrentChannel(uint8_t channel)
      {
          if (channel > ADC_CHANNEL4) {
              DEBUG_PRINT("Invalid current input channel: %d. Must be between 1 and 4.\r\n", channel);
              return;
          }
      
          uint32_t readback = 0;
          uint32_t id = AD4111_GetRegisterValue(0x07, 2);
          DEBUG_PRINT("AD4111 ID Register: 0x%04lX\r\n", id);
      
          AD4111_SetRegisterValue(0x01, 0x8004, 2);
          readback = AD4111_GetRegisterValue(0x01, 2);
          DEBUG_PRINT("ADCMODE (0x01) set to: 0x%04lX\r\n", readback);
      
          AD4111_SetRegisterValue(0x02, 0x0840, 2);
          readback = AD4111_GetRegisterValue(0x02, 2);
          DEBUG_PRINT("IFMODE (0x02) set to: 0x%04lX\r\n", readback);
      
          const uint16_t channel_configs[4] = {0x81E8, 0x81C9, 0x81AA, 0x818B};
          uint8_t ch_reg = 0x10 + channel;
      
          AD4111_SetRegisterValue(ch_reg, channel_configs[channel], 2);
          readback = AD4111_GetRegisterValue(ch_reg, 2);
          DEBUG_PRINT("CH%d configured for IIN±: 0x%04X\r\n", channel, readback);
      
          uint8_t setup_reg = 0x20 + channel;
          AD4111_SetRegisterValue(setup_reg, 0x0020, 2); // Setup for unipolar
          readback = AD4111_GetRegisterValue(setup_reg, 2);
          DEBUG_PRINT("SETUPCON%d (0x%02X) set to: 0x%04lX\r\n", channel, setup_reg, readback);
      
          uint8_t filt_reg = 0x28 + channel;
          AD4111_SetRegisterValue(filt_reg, 0x0500, 2); // Filter config
          readback = AD4111_GetRegisterValue(filt_reg, 2);
          DEBUG_PRINT("FILTCON%d (0x%02X) set to: 0x%04lX\r\n", channel, filt_reg, readback);
      
          DEBUG_PRINT("AD4111 Current Channel CH%d configuration completed.\r\n", channel);
      
          //Set Quad Switch for channel
          EnableVoltChannel(channel, GPIO_PIN_RESET);
          EnableCurrentChannel(channel, GPIO_PIN_SET);
      }

    5. Calibration effect: perform internal zero and full-scale calibration before voltage measurements using the function below:

      /*=================================================================================================
       *  AD4111_Calibrate
       *  Brief: Performs internal zero and full-scale calibration.
       *================================================================================================*/
      void AD4111_Calibrate(void)
      {
          AD4111_WriteADCModeReg(ADC_MODE_ZERO_CAL);
          AD4111_WaitRdyGoLow();
          HAL_Delay(500);
      
          AD4111_WriteADCModeReg(ADC_MODE_FULLSCALE_CAL);
          AD4111_WaitRdyGoLow();
          HAL_Delay(500);
      
          AD4111_WriteADCModeReg(ADC_MODE_CONTINUOUS);
          HAL_Delay(500);
      
          AD4111_PrintCalibrationRegisters();
      }

    While this improves voltage measurement accuracy, it negatively impacts current readings, even if all channels are configured as current. If I skip calibration, current readings remain accurate.

    I have attached my AD4111_ConfigureVoltChannel() and AD4111_ConfigureCurrentChannel() functions for reference.

    Could you help me understand:

    • Why mixing voltage and current configurations causes inaccurate results?

    • Why CH0’s configuration as voltage seems to affect other voltage channels?

    • Why the voltage calibration procedure impacts current readings, even when no voltage channels are in use?

    Your insights would be greatly appreciated.

    Best regards,
    Manohar Emandi

  • Hi  ,

    I have looked into the code snip that you have provided. It seems that your voltage and current channels share the same setup. Kindly try using a different SETUPCON for the current inputs. 

    The gain registers are configured for currents with factory calibrated coefficients. So there is no need to perform the internal full-scale calibration for current inputs. This is also stated in the Calibration section in the datasheet. 

    In your setup, since you have performed an internal full-scale calibration, the gain coefficients are updated for the voltage inputs. So , you would need to use a different setup for the current inputs to use the factory calibrated gain coefficients.

    Hope this helps.

    Regards,

    JC

Before You Switch


Switching languages will make ADI Explorer unavailable. Resume your session by switching back to English and reopening ADI Explorer.