This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SAADC Configuration

Hi All,

I am developing one project and there we are using ADC (i.e., SAADC in nRF). I have designed the hardware where I am using Capacitor ( C14, 2.2µF) which is connected to Analog pin and my requirement is I have to read the voltage level.

Here I am attaching details and image,

The voltage level of the input capacitor C14, 2.2µF / 400V to the Step-Down controller must be monitored. A voltage divider is used across the capacitor from R2, 3M3, and R3, 24k. The tapped voltage is measured at pin port P0.04 / AIN2 of the nRF52840. The expected voltage at the input of the controller is between 2.35V and 0V. The voltage at the capacitor must not fall below the value of 100V.

This is my requirement can anyone help how to configure SAADC with respect to my requirement.

Thanks in advance

Rohit R

Parents
  • Hello Rohit R,

    Have you seen the formulas for calculating SAADC input range in the SAADC documentation?

    The expected voltage at the input of the controller is between 2.35V and 0V.
    This is my requirement can anyone help how to configure SAADC with respect to my requirement.

    How is your nRF52840 supplies with power, are you using the coin cell battery, USB or external power?
    In the case of USB / 3 V power:
    If you would like your SAADC to have a the input range 0 - 2.35 V, you could either use reference VDD/4 and gain 1/3, which yields the input range 0 - 2.27 V, or you could use VDD/4 and gain 1/4 and get 0 - 3 V.
    Have you seen the SAADC Example from the SDK? It demonstrates how to setup and use the SAADC - then you could just input your own configuration, and you should start seeing your measurements.

    Please let me know if anything should be unclear with the forumlas, or how to go about doing the configuration.

    Best regards,
    Karl

Reply
  • Hello Rohit R,

    Have you seen the formulas for calculating SAADC input range in the SAADC documentation?

    The expected voltage at the input of the controller is between 2.35V and 0V.
    This is my requirement can anyone help how to configure SAADC with respect to my requirement.

    How is your nRF52840 supplies with power, are you using the coin cell battery, USB or external power?
    In the case of USB / 3 V power:
    If you would like your SAADC to have a the input range 0 - 2.35 V, you could either use reference VDD/4 and gain 1/3, which yields the input range 0 - 2.27 V, or you could use VDD/4 and gain 1/4 and get 0 - 3 V.
    Have you seen the SAADC Example from the SDK? It demonstrates how to setup and use the SAADC - then you could just input your own configuration, and you should start seeing your measurements.

    Please let me know if anything should be unclear with the forumlas, or how to go about doing the configuration.

    Best regards,
    Karl

Children
  • Hi Karl,

    Yes, I have seen the document and SDK examples too.

    I am modifying SDK example as per my requirement, I have shared the changes in the previous post.

    How is your nRF52840 supplies with power, are you using the coin cell battery, USB or external power?
    In the case of USB / 3 V power:

    - It is basically a light Dimming control circuit we are using 230V and then using the voltage regulator and divider we are powering up MCU with 3.3V. If you make this ticket private then I can share the schematic file here as it confidential.

    If you would like your SAADC to have a the input range 0 - 2.35 V, you could either use reference VDD/4 and gain 1/3, which yields the input range 0 - 2.27 V, or you could use VDD/4 and gain 1/4 and get 0 - 3 V.

    - Okay, let me try this configuration related Gain and check the result.

    - One more question, I am using P0.04 / AIN2, with respect to SDK example as per my understanding AIN2 means the channel 2 and to configure this we need to call,

    nrf_saadc_channel_config_t channel_config =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);  //single shot and channel config

    I am correct in configuring pin or did I miss something.

    Correct me If i am doing anything wrong.

    Thanks and Regards

    Rohit R

  • Hello again Rohit,

    Rohit Rajapure said:
    Yes, I have seen the document and SDK examples too.

    Great, then we are off to a good start!

    Rohit Rajapure said:
    I am modifying SDK example as per my requirement, I have shared the changes in the previous post.

    Regarding what you have shared in your other comment, I have some questions:
    - 1), 2), and 3) is just setting up a configuration structure, but they are not actually initializing any channel with that configuration. You will need to use nrfx_saadc_channel_init for this. Please also note that the NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE only generates a "default configuration struct for single ended input on the specified channel" struct, so any modifications to the default single ended input configuration will have to be done after this line.

    - 4) triggers a sampling, but no error check is performed. You will need to pass the error code to APP_ERROR_CHECK for this.

    - 5) this function is used to provide a new buffer to the SAADC. You should call this function as part of the DONE event callback and the SAADC init.

    - 7) a & b:

    You seem to get the SAADC outputs ~1024 and ~0 when connected to VDD and GND respectively. Given the 1), 2), and 3) I suspect that these values were acquired on pin AIN0, with the default single ended configuration. The SAADC output 1024 with 10 bit resolution, 1 / 6 gain and 600 mV reference equals 3.6 V using the formula provided in the documentation:

    RESULT = (V(P) – V(N)) * (GAIN/REFERENCE) * 2(RESOLUTION - m)


    You should also perform a calibration of the SAADC after initialization and following significant temperature changes. 

    For future reference, please use the "Insert -> Code" option here on DevZone when submitting code, this drastically increases readability.

    Rohit Rajapure said:
    - Okay, let me try this configuration related Gain and check the result.

    Yes, please try the other configuration and let me know what samples you receive.

    Rohit Rajapure said:

    - One more question, I am using P0.04 / AIN2, with respect to SDK example as per my understanding AIN2 means the channel 2 and to configure this we need to call,

    nrf_saadc_channel_config_t channel_config =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);  //single shot and channel config

    I am correct in configuring pin or did I miss something.

    Yes, at least partly - see my above comments. This is just generating the configuration struct - you are not actually initializing the channel.

    Rohit Rajapure said:
    If you make this ticket private then I can share the schematic file here as it confidential.

    For the current SAADC issues I do not think I will need to see the schematics, but I may convert the ticket to private at any time if you would like - just let me know and it will be done.

    As a side note I will also have to mention that I highly recommend making use of the NRFX SAADC driver, instead of the nrf_drv_* legacy driver.
    As you will see if you dive into the nrf_drv_* legacy drivers source code, it is now just forwarding macros to the NRFX driver, so functionally the change from switching to the NRFX driver is nothing, but it will decrease future work if the legacy driver is discontinues in future releases. 

    Looking forward to resolving this issue together!

    Best regards,
    Karl

  • Hi Karl,

    Thanks for the response.

    I will note down points and try the configuration accordingly and share my result.

    And sorry for the late response, I was not well so could not check any messages or notification. I will check and let you know.

    Thanks and Regards

    Rohit R

  • Hi Karl,

    Please find my main.c file, I tried below configuration can you please check the file and let me know

    Is this correct or not? asap.

    /**
     * Copyright (c) 2014 - 2019, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    /** @file
     * @defgroup nrf_adc_example main.c
     * @{
     * @ingroup nrf_adc_example
     * @brief ADC Example Application main file.
     *
     * This file contains the source code for a sample application using ADC.
     *
     * @image html example_board_setup_a.jpg "Use board setup A for this example."
     */
    
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include "nrf.h"
    #include "nrf_drv_saadc.h"
    #include "nrf_drv_ppi.h"
    #include "nrf_drv_timer.h"
    #include "boards.h"
    #include "app_error.h"
    #include "nrf_delay.h"
    #include "app_util_platform.h"
    #include "nrf_pwr_mgmt.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #define SAMPLES_IN_BUFFER 1
    #define SAADC_BURST_MODE 1                        //Set to 1 to enable BURST mode, otherwise set to 0.
    
    volatile uint8_t state = 1;
    
    static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0);
    static nrf_saadc_value_t     m_buffer_pool[1][SAMPLES_IN_BUFFER];
    static nrf_ppi_channel_t     m_ppi_channel;
    static uint32_t              m_adc_evt_counter;
    
    
    void timer_handler(nrf_timer_event_t event_type, void * p_context)
    {
    
    }
    
    
    //void saadc_sampling_event_init(void)
    //{
    //    ret_code_t err_code;
    //
    //    err_code = nrf_drv_ppi_init();
    //    APP_ERROR_CHECK(err_code);
    //
    //    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    //    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    //    err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
    //    APP_ERROR_CHECK(err_code);
    //
    //    /* setup m_timer for compare event every 400ms */
    //    uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, 1000);
    //    nrf_drv_timer_extended_compare(&m_timer,
    //                                   NRF_TIMER_CC_CHANNEL0,
    //                                   ticks,
    //                                   NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
    //                                   false);
    //    nrf_drv_timer_enable(&m_timer);
    //
    //    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer,
    //                                                                                NRF_TIMER_CC_CHANNEL0);
    //    uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();
    //
    //    /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
    //    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
    //    APP_ERROR_CHECK(err_code);
    //
    //    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
    //                                          timer_compare_event_addr,
    //                                          saadc_sample_task_addr);
    //    APP_ERROR_CHECK(err_code);
    //}
    
    
    //void saadc_sampling_event_enable(void)
    //{
    //    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
    //
    //    APP_ERROR_CHECK(err_code);
    //}
    
    
    void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
    {
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {
            ret_code_t err_code;
    
            err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
            APP_ERROR_CHECK(err_code);
    
            
            int i;
            NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);
    
            for (i = 0; i < SAMPLES_IN_BUFFER; i++)
            {
                NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
            }
            m_adc_evt_counter++;
        }
    }
    
    
    void saadc_init(void)
    {
        ret_code_t err_code;
        nrf_drv_saadc_config_t saadc_config;
        saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;   //12 bit resolution
    
        //saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X;
    
        nrf_saadc_channel_config_t channel_config =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);  //singale shot and channel config 
            
       channel_config.gain = NRF_SAADC_GAIN1_4;
       channel_config.reference = NRF_SAADC_REFERENCE_VDD4;
       channel_config.mode = NRF_SAADC_MODE_SINGLE_ENDED;
       channel_config.pin_n = NRF_SAADC_INPUT_DISABLED; 
       channel_config.pin_p = NRF_SAADC_INPUT_AIN2;
       channel_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED;
       channel_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED;
    
        err_code = nrf_drv_saadc_init(NULL, saadc_callback);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_channel_init(2, &channel_config);
        APP_ERROR_CHECK(err_code);
       
    //    if(SAADC_BURST_MODE)
    //    {
    //       NRF_SAADC->CH[2].CONFIG |= 0x01000000;
    //    }
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
    
     //   NRF_LOG_INFO("%d", m_buffer_pool[0]);
    //    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
    //    APP_ERROR_CHECK(err_code);
    
    }
    
    void saadc_sampling_trigger(void)
    {
        ret_code_t err_code;
        //Event handler is called immediately after conversion is finished.
        err_code = nrf_drv_saadc_sample(); // Check error
        APP_ERROR_CHECK(err_code);
    }
    
    /**
     * @brief Function for main application entry.
     */
    int main(void)
    {
        uint32_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
    //    ret_code_t ret_code = nrf_pwr_mgmt_init();
    //    APP_ERROR_CHECK(ret_code);
    
        saadc_init();
        //saadc_sampling_event_init();
        //saadc_sampling_event_enable();
        NRF_LOG_INFO("SAADC HAL simple example started.");
    
        while (1)
        {
            //nrf_pwr_mgmt_run();
            saadc_sampling_trigger();
            nrf_delay_ms(1000);
            NRF_LOG_FLUSH();
        }
    }
    
    
    /** @} */
    

    I tried, I am getting same result -3 and 1023. I think I am still making any mistake or i did not understand it clear.

    Thanks and Regards

    Rohit R

  • Hello Rohit,

    Rohit Rajapure said:
    sorry for the late response, I was not well so could not check any messages or notification.

    It is no problem at all, do not worry. I am glad to hear that you are back to health!

    Rohit Rajapure said:
    I tried, I am getting same result -3 and 1023. I think I am still making any mistake or i did not understand it clear.

    With the SAADC at 10-bit resolution, 1023 = VDD, and -3 = GND. I would at least expect the LSB to fluctuate on your measurements.

    Rohit Rajapure said:
    Is this correct or not? asap.

    Well, it is hard to say really - you have not told me what your goal is, so it is hard for me to confirm if this is the right approach.
    If you are trying to measure one VDD pin and one GND pin, then you are correct.

    I would also recommend that you make use of the nrfx_saadc driver rather than the legacy nrf_drv driver. Additionally, I would advise you to use the PPI peripheral to connect your SAMPLE_STARt task to a timer event ( like demonstrated in the SAADC example from the SDK ) rather than having a given delay and manually having the CPU start each conversion ( with the call to nrf_drv_saadc_sample. I would not recommend the current approach, as it uses excess CPU resources, and is generally inaccurate and not power optimized at all.
    On a general note, I would also mention that it is less-than-ideal for you to provide the code as you have done here. The large chunks of commented out code should not be included, because it takes the focus away from what you actually would like me to take a look at and provide feedback for.

    In conclusion, I would recommend that you revert to the general setup and approach of the SAADC example from the SDK ( which uses PPI ), instead of the implementation you have now. However, since I do not know exactly what you are trying to achieve, then the current approach that you have implemented might very well suit your purpose.

    If you would like to have the measurements displayed in millivolts ( mV ) instead of raw SAADC outputs then you need to convert them using the formula I referenced in my earlier comment. If you would like to see how battery measurement is implemented in another example, please have a look at the BLE Proximity Example from the SDK.

    Best regards,
    Karl

Related