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?

  • So I'll assume that SAADC calibrate won't work for me? I presently have it disabled since it hangs, and I'll keep it that way unless see an answer that helps in getting it working.

Reply Children
  • I've had a chance to get back to this issue and have made some changes. I've added the _buffer_convert as you have suggested and have moved the _sample trigger into the main loop. I also found that if the beacon is advertising, it hangs on the while(!nrf_saadc_event_check(NRF_SAADC_EVENT_CALIBRATEDONE)); statement, so I added a flag to make sure it's not advertising before running the calibration. Here is the modified code:

    	if (m_saadc_calibrate && advertising_completed)
    	{
    	    nrfx_saadc_evt_t const *p_event;
    	    nrfx_saadc_abort();
    	    while(nrfx_saadc_calibrate_offset() != NRF_SUCCESS);
    	    while(!nrf_saadc_event_check(NRF_SAADC_EVENT_CALIBRATEDONE));
    	    m_saadc_calibrate = false;
    	    SEGGER_RTT_WriteString(0, "SAADC Calibrated\n");
    	    ret_code_t err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
    	    APP_ERROR_CHECK(err_code);
    	}
    
    	if (intiate_advertising)
    	{
    	    for (uint8_t i = 0; i < SAMPLES_IN_BUFFER; i++)
    	    {
    		    ret_code_t err_code = nrfx_saadc_sample();
    		    APP_ERROR_CHECK(err_code);
    	    }
    
    	    intiate_advertising = false;
    	}

    The init code and the callback code posted previously is the same. SAMPLES_IN_BUFFER is set to 3.

    Now I'm getting bad values out of the SAADC after calibration:

    ADC event number: 0
    24563
    -32934
    21965

    ADC event number: 1
    24563
    -32934
    21965

    ADC event number: 2
    24563
    -32934
    21965

    And so on. The values never change. The values should be around 2990 which is what they are without running the calibration.

    If I remove _buffer_convert, it crashes when  _sample trigger is executed.

Related