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

ADC result not changing

I'm trying to set up the ADC to read a battery voltage but am unable to get the adc result to change regardless of whether the input line is connected to VDD or GND. I'm using the nRF51-DK, and am configuring AIN5 for my ADC input. I'm reading the ADC on a timer interrupt. The timer works fine and I can set a breakpoint there to verify the ADC read occurs every 10 seconds. Every ADC read gives me a result of around 0xCC whether AIN5 is connected to GND or VDD (~2.9V). Looking at the nRF51422 Product Spec, it says that AIN5 is P0.04. This is the pin that I am switching between GND and VDD. I've also verified that LPCOMP is disabled so there is no contention here. I'm using SDK9 with the S110 SoftDevice. Here's my ADC configuration code - battery_init() is called from main(). Am I missing a configuration somewhere? Thanks.

#include <stdint.h>
#include "nordic_common.h"
#include "nrf.h"
#include "nrf_soc.h"
#include "nrf_adc.h"
#include "app_error.h"
#include "nrf51_bitfields.h"
#include "app_timer.h"

#define ADC_REF_VOLTAGE_IN_MILLIVOLTS   1200                                         /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
#define ADC_PRE_SCALING_COMPENSATION    3                                            /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
#define BATTERY_LEVEL_MEAS_INTERVAL     APP_TIMER_TICKS(10000, APP_TIMER_PRESCALER) /**< Battery level measurement interval (ticks). This value corresponds to 10 seconds. */
#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
    ((((ADC_VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / 255) * ADC_PRE_SCALING_COMPENSATION)

static app_timer_id_t _battery_timer_id;

static void adc_configure(void);
static void battery_level_meas_timeout_handler(void * p_context);


void battery_init(void)
{
    uint32_t err_code;

    // Create battery timer.
    err_code = app_timer_create(&_battery_timer_id,
                            APP_TIMER_MODE_REPEATED,
                            battery_level_meas_timeout_handler);
    APP_ERROR_CHECK(err_code);

    adc_configure();

    // Start battery timer
    err_code = app_timer_start(_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
    APP_ERROR_CHECK(err_code);
}

static void adc_configure(void)
{
    uint32_t err_code;
    nrf_adc_config_t adc_config = NRF_ADC_CONFIG_DEFAULT;

    // Configure ADC
    adc_config.reference  = NRF_ADC_CONFIG_REF_VBG;
    adc_config.resolution = NRF_ADC_CONFIG_RES_8BIT;
    adc_config.scaling    = NRF_ADC_CONFIG_SCALING_SUPPLY_ONE_THIRD;
    nrf_adc_configure(&adc_config);

    // Select the battery level input
    nrf_adc_input_select(NRF_ADC_CONFIG_INPUT_5);

    // Enable ADC interrupt
    nrf_adc_int_enable(ADC_INTENSET_END_Msk);
    err_code = sd_nvic_ClearPendingIRQ(ADC_IRQn);
    APP_ERROR_CHECK(err_code);

    err_code = sd_nvic_SetPriority(ADC_IRQn, NRF_APP_PRIORITY_LOW);
    APP_ERROR_CHECK(err_code);

    err_code = sd_nvic_EnableIRQ(ADC_IRQn);
    APP_ERROR_CHECK(err_code);
}

static void battery_level_meas_timeout_handler(void * p_context)
{
    UNUSED_PARAMETER(p_context);
    nrf_adc_start();
}

void ADC_IRQHandler(void)
{
    if (nrf_adc_conversion_finished())
    {
        uint8_t  adc_result;
        static volatile uint16_t batt_lvl_in_milli_volts;

        nrf_adc_conversion_event_clean();

        adc_result = nrf_adc_result_get();

        batt_lvl_in_milli_volts = ADC_RESULT_IN_MILLI_VOLTS(adc_result);
    }
}
  • Hi Brian

    First hint: Make sure you configure the ADC after you initialize the softdevice.

    Perhaps this link could be relevant for further hints.

    Just to verify my understanding of the problem, do you always see 0xCC value from the ADC when a breakpoint is hit in the ADC handler?

    Update 14.10.2015

    I see the error now. Instead of writing

    adc_config.scaling    = NRF_ADC_CONFIG_SCALING_SUPPLY_ONE_THIRD;
    

    you should write

    adc_config.scaling    = NRF_ADC_CONFIG_SCALING_INPUT_ONE_THIRD;
    

    That will make the ADC sample from an analog input pin. With your current configuration, you are sampling the supply voltage of the chip.

  • Hi Stefan,

    I am configuring the ADC after the softdevice is configured. The ADC configuration is one of the last things I do in my initialization. Also, the values I've seen from the ADC result are 0xCA, 0xCB, or 0xCC - so it does actually change values. Here is a register dump of the ADC registers right after the ADC_IRQHandler reads the ADC result:

    0x40007500 00000001 00002018 000000CC 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ..... ..Ì....................................... 0x40007530 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ................................................ 0x40007560 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ................................................ 0x40007590 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 ................................................

    Also, I have verified with a scope that I have 0V on P0.04 when connected to GND and I read ~0xCC from the ADC. I've also verified that when I connect P0.04 to VDD, I have 2.84V on P0.04 and I still read ~0xCC. It also seems like P0.04 is not mapped to AIN5. Is it possible for this mapping to be changed to a different port pin?

    And here is my main function. I have tried removing all of the non-Nordic initialization other than the ADC initialization, but I get the same results.

    int main(void) { uint32_t ticks; uint32_t err_code; bool erase_bonds; uint32_t var1; uint32_t var2;

    // Initialize.
    timers_init();
    gpio_init();
    uart_init();
    system_tick_init();
    buttons_leds_init(&erase_bonds);
    button_init();
    ls_init();
    ble_stack_init();
    device_manager_init(erase_bonds);
    nvm_init();
    gap_params_init();
    
    // Comment out the next line to enable single-step debugging
    //advertising_init();
    
    services_init();
    //sensor_simulator_init();
    conn_params_init();
    
    // Start execution.
    application_timers_start();
    system_tick_start();
    err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
    APP_ERROR_CHECK(err_code);
    
    app_timer_cnt_get(&ticks);
    printf("%"PRIu32": Starting Nordic Strobe\r\n", ticks);
    
    
    var1 = nvm_read(NVM_mode);
    app_timer_cnt_get(&ticks);
    printf("%"PRIu32": NVM_mode = %"PRIu32"\r\n", ticks, var1);
    
    var2 = nvm_read(NVM_speed);
    app_timer_cnt_get(&ticks);
    printf("%"PRIu32": NVM_speed = %"PRIu32"\r\n", ticks, var2);
    
    for(int i=GPIO_LED1; i<=GPIO_LED4; ++i)
    {
        gpio_write(i, GPIO_ACTIVE);
        nrf_delay_ms(100);
    }
    
    for(int i=GPIO_LED1; i<=GPIO_LED4; ++i)
    {
        gpio_write(i, GPIO_INACTIVE);
        nrf_delay_ms(100);
    }
    
    uint8_t battery_levels = display_init();
    battery_init(battery_levels);
    ls_start();
    fsm_init();
    
    // Enter main loop.
    for (;;)
    {
        // Run the state machine if it's time to
        if (fsm_get_ready())
        {
            fsm_run();
        }
    
        // Flush NVM if it's time to
        if (nvm_flush_ready())
        {
            nvm_update();
        }
    
        // If we've advanced past displaying the logo, then...
        if (fsm_get_state() > FSM_STATE_LOGO_DONE)
        {
            // Update the battery level display if it's changed
            if (battery_level_changed())
            {
                display_battery(battery_get_level());
            }
        }
    }
    

    }

  • Hi Stefan,

    That was definitely the issue. Thanks! I'm now getting the expected readings with different voltages on the input. Good catch!

  • Hello Brian could you please tell me whether you got the correct battery voltage reading with this program? because am facing similar kind of issue with the ADC so your ideas or suggestion would be helpful for me to proceed further

  • Hi Prabhudurai, Yes, I am getting the correct battery voltage once I made the change that Stefan suggested.

Related