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

nrf_drv_saadc_calibrate_offset() doesn't work

Running nrf_drv_saadc_calibrate_offset() doesn't seem to work. I'm monitoring a 3v battery. Shown below is the debug output where the calibration is run after the event number 4. As you can see, the voltage is reading what is should (scaled by 1000) before the calibration, but reads 3600 on each event after the calibration, never moving. 

ADC event number: 0
2960

ADC event number: 1
2960

ADC event number: 2
2949

ADC event number: 3
2953

ADC event number: 4
2953

ADC event number: 5
3600

ADC event number: 6
3600

ADC event number: 7
3600

	if (m_saadc_calibrate)
	{
	    nrf_drv_saadc_abort();
	    while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS);
	    while(!nrf_saadc_event_check(NRF_SAADC_EVENT_CALIBRATEDONE));
	    m_saadc_calibrate = false;
	}

This is the code that runs the calibrate in the main forever loop. m_saadc_calibrate is set in saadc_callback().

Parents
  • Are you also powering the nRF with the coin cell battery? If so you can expect massive voltage fluctuations depending on the given load and state of charge from the battery.

    I suggest you get a fixed voltage source and sample that instead. 

    Aslo, what is the SAADC configuration?  

  • Normally, it would be powered with a battery, but for testing I'm using a bench power supply.

    static void saadc_init(void)
    {
        ret_code_t              err_code;
        nrf_drv_saadc_config_t  saadc_config;
    
        saadc_config.low_power_mode = true;
        saadc_config.resolution = NRF_SAADC_RESOLUTION_10BIT;	//Set SAADC resolution to 10-bit. This will make the SAADC output values from 0 (when input voltage is 0V) to 2^10=1024 (when input voltage is 3.6V for channel gain setting of 1/6).
        saadc_config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;    //Disable oversample. Can't oversample with more than one channel active.
        saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW;     //Set SAADC interrupt to low priority.
        nrf_saadc_channel_config_t channel_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_VDD);
    
        err_code = nrf_drv_saadc_init(NULL, saadc_callback);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_channel_init(0, &channel_config);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
    
    //    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
    //    APP_ERROR_CHECK(err_code);
    }

  • I have since simplified the SAADC code by eliminating the PPI channel and use nrfx_saadc_sample() occasionally to get the battery voltage.

    Here's the callback code:

    static void saadc_callback(nrf_drv_saadc_evt_t const *p_event)
    {
        static uint32_t m_adc_evt_counter;
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {
    	ret_code_t err_code;
    	uint32_t voltage;
    	uint16_t average_sample = 0;
    	err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
    	APP_ERROR_CHECK(err_code);
    
    	SEGGER_RTT_printf(0, "\nADC event number: %d\n", m_adc_evt_counter);
    
    	for (uint8_t i = 0; i < SAMPLES_IN_BUFFER; i++)
    	{
    	    voltage = p_event->data.done.p_buffer[i] * 3600 / 1024;
    
    	    SEGGER_RTT_printf(0, "%d\n", voltage);
    	    average_sample += voltage;
    	}
    
    	if((++m_adc_evt_counter % SAADC_CALIBRATION_INTERVAL) == 0)
    	    m_saadc_calibrate = true;
    
    	battery_voltage = average_sample / SAMPLES_IN_BUFFER;
    	change_manuf_specific_data();
        }
    }

  • I see one issue at least: 

    You need to call nrf_drv_saadc_buffer_convert() after you've calibrated the SAADC, otherwise you will fill your buffer with invalid samples collected during calibration. 

    What SDK version are you on?

Reply Children
Related