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

NRF52840 SAADC High Current with FreeRTOS SDK15.2

Hello,

I am need to add voltage sampling functionality to existing FreeRTOS framework on a low-power BLE device. When I enable BLE, I get 3mA of current, which stays there permanently . I have read all sorts of posts and guides for low power SAADC with a multi-channel setup but cannot seem to figure out the culprit... I only need to sample this once every 2-5min so the original approach was to my intent is to enable ADC, sample and disable it completely. However, for some reason I cannot turn it off. My code is attached below, wls_adc_start_sample() is triggered by the main task. It runs like it's supposed to, the interrupt gets triggered and I can read correct values. The only issue is extremely high current, which I am having trouble resolving...

/************************************************************************************************
	LOCAL VARIABLES
*************************************************************************************************/
static nrf_saadc_value_t       m_adc_buffer[SAADC_SAMPLES_IN_BUFFER];

//ADC config
const nrf_drv_saadc_config_t saadc_config = 	{
																.resolution         = NRF_SAADC_RESOLUTION_12BIT, 			\
																.oversample         = NRF_SAADC_OVERSAMPLE_DISABLED, 		\
																.interrupt_priority = APP_IRQ_PRIORITY_LOW,           	\
																.low_power_mode     = true                           	\
															};

const nrf_saadc_channel_config_t channel_config_vbat =	{
																			.resistor_p = NRF_SAADC_RESISTOR_DISABLED,      \
																			.resistor_n = NRF_SAADC_RESISTOR_DISABLED,      \
																			.gain       = NRF_SAADC_GAIN1_2,                \
																			.reference  = NRF_SAADC_REFERENCE_INTERNAL,     \
																			.acq_time   = NRF_SAADC_ACQTIME_10US,           \
																			.mode       = NRF_SAADC_MODE_SINGLE_ENDED,      \
																			.burst      = NRF_SAADC_BURST_DISABLED,         \
																			.pin_p      = NRF_SAADC_INPUT_AIN2,       		\
																			.pin_n      = NRF_SAADC_INPUT_DISABLED          \
																			};

const nrf_saadc_channel_config_t channel_config_3p3v = 	{
																			.resistor_p = NRF_SAADC_RESISTOR_DISABLED,      \
																			.resistor_n = NRF_SAADC_RESISTOR_DISABLED,      \
																			.gain       = NRF_SAADC_GAIN1_2,                \
																			.reference  = NRF_SAADC_REFERENCE_INTERNAL,     \
																			.acq_time   = NRF_SAADC_ACQTIME_10US,           \
																			.mode       = NRF_SAADC_MODE_SINGLE_ENDED,      \
																			.burst      = NRF_SAADC_BURST_DISABLED,         \
																			.pin_p      = NRF_SAADC_INPUT_AIN0,       		\
																			.pin_n      = NRF_SAADC_INPUT_DISABLED          \
																			};

const nrf_saadc_channel_config_t channel_config_vcc =		{
																			.resistor_p = NRF_SAADC_RESISTOR_DISABLED,      \
																			.resistor_n = NRF_SAADC_RESISTOR_DISABLED,      \
																			.gain       = NRF_SAADC_GAIN1_2,                \
																			.reference  = NRF_SAADC_REFERENCE_INTERNAL,     \
																			.acq_time   = NRF_SAADC_ACQTIME_10US,           \
																			.mode       = NRF_SAADC_MODE_SINGLE_ENDED,      \
																			.burst      = NRF_SAADC_BURST_DISABLED,         \
																			.pin_p      = NRF_SAADC_INPUT_AIN3,       		\
																			.pin_n      = NRF_SAADC_INPUT_DISABLED          \
																			};
/************************************************************************************************
	FLAGS
*************************************************************************************************/
static bool m_saadc_initialized = false;

/************************************************************************************************
	FUNCTIONS
*************************************************************************************************/
																			
void wls_adc_deinit( void );

/************************************************************************************************
 ************************************************************************************************
 * @name				saadc_callback
 *
 * @description 	initialize WLS ADC drivers
 *
 * @param[in] 		*p_event - event passed to the callback
 *
 * @return 			none
 ************************************************************************************************
 ************************************************************************************************/
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;
		TaskMsg msg;
		BaseType_t xHigherPriorityTaskWoken = pdFALSE;
		
		
		//process ADC data 
		msg.type = APP_EVENT_PROCESS_ADC;
		msg.msg = p_event->data.done.p_buffer;
		msg.val = NULL;
		
		//Send to App queue
		xQueueSendFromISR(App_EventQueue, &msg, &xHigherPriorityTaskWoken);
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
	}
	
	//Turn off power to resistor dividers
	nrf_gpio_pin_clear(ADC_V_CHECK_EN);
	
	//turn off ADC
	wls_adc_deinit();
}

/************************************************************************************************
 ************************************************************************************************
 * @name				wls_adc_deinit
 *
 * @description 	Turn off WLS ADC
 *
 * @param[in] 		none
 *
 * @return 			none
 ************************************************************************************************
 ************************************************************************************************/
void wls_adc_deinit( void )
{
	//De-init ADC
	nrfx_saadc_uninit();
	m_saadc_initialized = false;

	//Clear interrupts
	NRF_SAADC->INTENCLR = (SAADC_INTENCLR_END_Clear << SAADC_INTENCLR_END_Pos);
	NVIC_ClearPendingIRQ(SAADC_IRQn);
}

/************************************************************************************************
 ************************************************************************************************
 * @name				wls_adc_init
 *
 * @description 	Turn on WLS ADC
 *
 * @param[in] 		none
 *
 * @return 			none
 ************************************************************************************************
 ************************************************************************************************/
void wls_adc_init( void )
{
	ret_code_t err_code;
	
	//Initialize SAADC
	err_code = nrfx_saadc_init(&saadc_config, saadc_callback);   //Initialize the SAADC with configuration and callback function. The application must then implement the saadc_callback function, which will be called when SAADC interrupt is triggered
	APP_ERROR_CHECK(err_code);
	
	//Initialize SAADC channels
	err_code = nrfx_saadc_channel_init(ADC_CHANNEL_VBAT, &channel_config_vbat);                            //Initialize SAADC channel 0 with the channel configuration
	APP_ERROR_CHECK(err_code);
	
	err_code = nrfx_saadc_channel_init(ADC_CHANNEL_V3P3, &channel_config_3p3v);                            //Initialize SAADC channel 0 with the channel configuration
	APP_ERROR_CHECK(err_code);
	
	err_code = nrfx_saadc_channel_init(ADC_CHANNEL_VCC, &channel_config_vcc);                            //Initialize SAADC channel 0 with the channel configuration
	APP_ERROR_CHECK(err_code);
	
	m_saadc_initialized = true;
	
}

/************************************************************************************************
 ************************************************************************************************
 * @name				wls_adc_start_sample
 *
 * @description 	Kicks off ADC sampling process
 *
 * @param[in] 		none
 *
 * @return 			none
 ************************************************************************************************
 ************************************************************************************************/
void wls_adc_start_sample( void )
{
	ret_code_t err_code;
	
	//Turn on power to resistor dividers
	nrf_gpio_pin_set(ADC_V_CHECK_EN);
	
	if(m_saadc_initialized == false)
	{
		//turn on ADC
		wls_adc_init();
	}
	
	//create conversion buffer 
	err_code = nrfx_saadc_buffer_convert(m_adc_buffer, SAADC_SAMPLES_IN_BUFFER);    //Set SAADC buffer 1. The SAADC will start to write to this buffer
	APP_ERROR_CHECK(err_code);
	
	//start sampling
	nrfx_saadc_sample();
}

Any insight on what I might be doing wrong is highly apprciated.

Thanks!

Parents
  • Just to understand if this is related to FreeRTOS and/or BLE solely, what happens if you disable BLE and keep the SAADC functionality exercised ? does the power consumption stays high?

    If the answer to the above is yes, then most likely it is something that keeps the chip awake even though your app is (assuming) sleeping  in tickless idle sleep. Possibly some interrupt is being pended and not serviced, you can check that in you read NVIC->ISPR register.

Reply
  • Just to understand if this is related to FreeRTOS and/or BLE solely, what happens if you disable BLE and keep the SAADC functionality exercised ? does the power consumption stays high?

    If the answer to the above is yes, then most likely it is something that keeps the chip awake even though your app is (assuming) sleeping  in tickless idle sleep. Possibly some interrupt is being pended and not serviced, you can check that in you read NVIC->ISPR register.

Children
  • Hi Susheel, your advice to check NVIC->ISPR was spot on. I saw that the FPU interrupt was active, this is becasue the data collected from ADC was converted to voltage using floating point math, I didn't connect the issue with that until I check the NVIC register. I used the solution from this thread to fix it. Specifically, I enabled the FPU interrupt handler. I added the following to my initialization code:

    //Configure FPU interrupts
    NVIC_SetPriority(FPU_IRQn, APP_IRQ_PRIORITY_LOW);
    NVIC_EnableIRQ(FPU_IRQn);

    I also added the interrupt that clears FPU exceptions (apparently I'm throwing an exception although when I redo the calculations they are sound ... need to investigate further):

    #define FPU_EXCEPTION_MASK 0x0000009F
    
    void FPU_IRQHandler(void)
    {
        uint32_t *fpscr = (uint32_t *)(FPU->FPCAR+0x40);
        (void)__get_FPSCR();
    
        *fpscr = *fpscr & ~(FPU_EXCEPTION_MASK);
    }

    With this code, I'm getting proper functionality and the current draw is low, as expected. Thanks again!

  • Checked the exact FPU flags and am getting the IXC - Inexact cumulative exception bit. This seems to imply that the result is "inexact" and needs to be rounded. The rounding is what throws the exception. I guessing this is because at some point of the calculation I get a repeating decimal. 

  • good to hear that we were on the right track and you fixed the issue.

Related