SAADC - Scan mode connects inputs to VDD_GPIO?

Hello!

We are using a custom nRF9160 board with 3 differential ADC channels. The differential voltage is measured across two resistors, with the center voltage (normally OpAmp driven) at 1,66 V.

I'm sampling three ADC channels in differential mode and noticed weird results and verified with a scope: Taking a sample with more than one channel enabled results in nRF9160 connecting the ADC-inputs to what appears to be VDD-ish for a period that seems to be TACQ*2. Sampling is triggered by PPI, currently at 1 kHz, and this phenomenon happens at every sample. (Changing the PPI frequency changes the spike positions as well.

Changing to single-ended measurement makes no difference but the problem doesn't show on scope with only one channel enabled.

Do I have something wrong with my SAADC configuration?

UPDATE 2022-10-24: Issue is related to CONFIG_BOOTLOADER_MCUBOOT and pin P0.14, which seems to configured as output and connecting to VDD_GPIO when it is sampling ADC

// SAADC Channel configuration
#define ADC_CONFIG_DIFF (SAADC_CH_CONFIG_GAIN_Gain1     << SAADC_CH_CONFIG_GAIN_Pos) | \
                        (SAADC_CH_CONFIG_MODE_Diff        << SAADC_CH_CONFIG_MODE_Pos) | \
                        (SAADC_CH_CONFIG_REFSEL_Internal  << SAADC_CH_CONFIG_REFSEL_Pos) | \
                        (SAADC_CH_CONFIG_RESN_Bypass      << SAADC_CH_CONFIG_RESN_Pos) | \
                        (SAADC_CH_CONFIG_RESP_Bypass      << SAADC_CH_CONFIG_RESP_Pos) | \
                        (SAADC_CH_CONFIG_TACQ_10us        << SAADC_CH_CONFIG_TACQ_Pos)

static void init_adc(void) {
  NRF_SAADC_NS->TASKS_CALIBRATEOFFSET = 1;
  
  NRF_SAADC_NS->RESOLUTION = SAADC_RESOLUTION_VAL_12bit;
  
  // Set sampling rate to be controlled by "SAMPLE", call.
  NRF_SAADC_NS->SAMPLERATE = SAADC_SAMPLERATE_MODE_Task;

  //Set the EasyDMA buffer
  active_buffer = 0;
  NRF_SAADC_NS->RESULT.PTR = (uint32_t) &m_sample_buffer_0;
  NRF_SAADC_NS->RESULT.MAXCNT = ADC_TOTAL_BUFFER_LEN;

  // Enable interrupt for buffer full
  NRF_SAADC_NS->INTENSET = SAADC_INTENSET_END_Set << SAADC_INTENSET_END_Pos;
  NRF_SAADC_NS->INTEN |= SAADC_INTEN_END_Enabled << SAADC_INTEN_END_Pos;
  NRF_SAADC_NS->EVENTS_END = 0;
  
// Configure SAADC Channels 
  NRF_SAADC_NS->CH[0].CONFIG = ADC_CONFIG_DIFF;
  NRF_SAADC_NS->CH[0].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput0 << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC_NS->CH[0].PSELN = SAADC_CH_PSELN_PSELN_AnalogInput1 << SAADC_CH_PSELN_PSELN_Pos;


  NRF_SAADC_NS->CH[1].CONFIG = ADC_CONFIG_DIFF;
  NRF_SAADC_NS->CH[1].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput2 << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC_NS->CH[1].PSELN = SAADC_CH_PSELN_PSELN_AnalogInput3 << SAADC_CH_PSELN_PSELN_Pos;


  NRF_SAADC_NS->CH[2].CONFIG = ADC_CONFIG_DIFF;
  NRF_SAADC_NS->CH[2].PSELP = SAADC_CH_PSELP_PSELP_AnalogInput4 << SAADC_CH_PSELP_PSELP_Pos;
  NRF_SAADC_NS->CH[2].PSELN = SAADC_CH_PSELN_PSELN_AnalogInput5 << SAADC_CH_PSELN_PSELN_Pos;

  
  NVIC_EnableIRQ( SAADC_IRQn );
  NVIC_SetPriority( SAADC_IRQn, 1UL );
  IRQ_DIRECT_CONNECT(SAADC_IRQn, 0, SAADC_IRQHandler, 0);
  NRF_SAADC_NS->ENABLE = 1;
}

Scope images (OpAmp off, resulting in ~0,7 V average at the OpAmp output)

  • Hi Karl,

    I can't find any mention of "mcuboot-button0" in the compiled devicetree, any nrf9160-board or zephyr/dts/arm/nordic/ directories, neither does is show in the project devicetree view in VS Code (pin 14 shows it's unconfigured). I can't find any overlays for nrf9160-boards in mcuboot-directory either.

    Only hit to P0.14 is the pinctrl-node in the uart1-sections, but uart1 is disabled:

       uart1: arduino_serial: uart@9000 {
                                    compatible = "nordic,nrf-uarte";
                                    reg = <0x9000 0x1000>;
                                    interrupts = <9 NRF_DEFAULT_IRQ_PRIORITY>;
                                    status = "disabled";
                                    label = "UART_1";
                                    current-speed = <115200>;
                                    pinctrl-0 = <&uart1_default>;
                                    pinctrl-1 = <&uart1_sleep>;
                                    pinctrl-names = "default", "sleep";
                            };

          pinctrl: pin-controller {
                    compatible = "nordic,nrf-pinctrl";
                    uart0_default: uart0_default {
                            group1 {
                                    psels = <NRF_PSEL(UART_TX, 0, 29)>,
                                            <NRF_PSEL(UART_RTS, 0, 27)>;
                            };
    
                            group2 {
                                    psels = <NRF_PSEL(UART_RX, 0, 28)>,
                                            <NRF_PSEL(UART_CTS, 0, 26)>;
                                    bias-pull-up;
                            };
    
                    };
    
                    uart0_sleep: uart0_sleep {
                            group1 {
                                    psels = <NRF_PSEL(UART_TX, 0, 29)>,
                                            <NRF_PSEL(UART_RX, 0, 28)>,
                                            <NRF_PSEL(UART_RTS, 0, 27)>,
                                            <NRF_PSEL(UART_CTS, 0, 26)>;
                                    low-power-enable;
                            };
    
                    };
    
                    uart1_default: uart1_default {
                            group1 {
                                    psels = <NRF_PSEL(UART_TX, 0, 1)>,
                                            <NRF_PSEL(UART_RTS, 0, 14)>;
                            };
    
                            group2 {
                                    psels = <NRF_PSEL(UART_RX, 0, 0)>,
                                            <NRF_PSEL(UART_CTS, 0, 15)>;
                                    bias-pull-up;
                            };
    
                    };
    
                    uart1_sleep: uart1_sleep {
                            group1 {
                                    psels = <NRF_PSEL(UART_TX, 0, 1)>,
                                            <NRF_PSEL(UART_RX, 0, 0)>,
                                            <NRF_PSEL(UART_RTS, 0, 14)>,
                                            <NRF_PSEL(UART_CTS, 0, 15)>;
                                    low-power-enable;
                            };
    
                    };

    Where exactly should I be looking at, or even better, what would be the best method of finding such definitions?

  • Hello again,

    Thank you for your patience with this.
    Since you are building for Non-secure, you will have TF-M added, which in turn uses UART1 for logging from the secure application, which is likely why you are seeing this behavior in your application.

    JyriLehtinen said:
    Where exactly should I be looking at, or even better, what would be the best method of finding such definitions?

    If you check the devicetree in Visual Studio code for your application, does UART1 have the okey status? This is a quick way to check whether the UART1 instance is enabled.

    Best regards,
    Karl

  • The compiled devicetree shows uart1 as "disabled". All references to UART1 in the compiled devicetree are shown in the post above yours.

  • That's peculiar.. does the behavior of the pin change if you add TFM_LOG_LEVEL_SILENCE=y in your prj.conf?
    Based on the compiled devicetree I would not expect so, but I ask just so that we can rule it out.

    Best regards,
    Karl

  • Hi,

    I'll test it when I have the time and report back.

Related