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

Accurate ADC measurement without the Soft Device

I'd like to measure the voltage of a lithium ion battery every minute or so in a way that's highly accurate. I need to do this without using the Soft Device because a) I need all the memory I can get for logging the voltages and b) what I'm really testing is a generator that's charging my battery so I want the power consumption of my board while I'm testing to be fairly low.

There's plenty of discussion about using the high frequency clock with the soft device to get an accurate ADC reading here:

devzone.nordicsemi.com/.../adc-softdevice-sample-rate-on-nrf51822

That code does this ten times a second:

static void adc_sampling_timeout_handler(void * p_context)
{
	uint32_t p_is_running = 0;
		
	sd_clock_hfclk_request();
	while(! p_is_running) {  							//wait for the hfclk to be available
		sd_clock_hfclk_is_running((&p_is_running));
	}               
	nrf_gpio_pin_toggle(NRF6310_LED_2);		//Toggle LED2 to indicate start of sampling
	NRF_ADC->TASKS_START = 1;							//Start ADC sampling
}

and then releases the clock at the end of the IRQ handler:

sd_clock_hfclk_release();

My question is, how do I do the same thing but without use of the Soft Device?

  • Hi Eliot,

    You'll have to manually start the 16M XTAL in a sequence like this:

        
    if (!(NRF_CLOCK->HFCLKSTAT & (BIT_0 | BIT_16)))
    {
        NRF_CLOCK->TASKS_HFCLKSTART = 1;
        while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
        NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    }
        
    /*
    Do something, then stop the HFCLK.
    */
    NRF_CLOCK->TASKS_HFCLKSTOP = 1;
    
    

    Best regards Håkon

  • You can save a bit of power by using SEV/WFE instead of polling (been meaning to learn how this WFE works): NRF_CLOCK->INTENSET = CLOCK_INTENSET_HFCLKSTARTED_Msk; // Enable wake-up on event SCB->SCR |= SCB_SCR_SEVONPEND_Msk;
    //NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; NRF_CLOCK->TASKS_HFCLKSTART = 1;
    __WFE(); __SEV();
    __WFE(); NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn); NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;

    Hope this helps on power consumption.

  • FormerMember
    0 FormerMember in reply to Håkon Alseth

    Håkon, can you please explain the code snippet that you've posted. What is the advantage of using the method that you have posted to enabling the interrupt for POWER_CLOCK and using WFI? And why SEV between WFE?

  • Eliot, I must apologize for going off-topic. This is a continuation of the WFE-talk in the comments area.

    As events and interrupts are closely coupled in the MCU core, you'll need to enable this in the INTENSET register prior to using SEV/WFE.

    1. you enable SEV instruction in SCB->SRC.
    2. You enable the event/interrupt for HFCLK-started (INTENSET)
    3. Start the task enabled in 2)
    4. The first WFE will cause the MCU to go to sleep and wake up on EVENTS_HFCLKSTARTED.
    5. SEV will set the Event Register in the core register.
    6. Second WFE will see that the event register is set, and clear this. No sleep will be executed at this call.
    7. EVENTS_HFCLKSTARTED and IRQn is cleared.

    Safest approach: I had a chat with our nrf_soc developer, and was told a better implementation. The above usage of WFE/SEV/WFE can cause you to wake up immediately if the internal event register is already set.

    To be certain that you wake up on the correct event, you should do like this:

    
    NRF_CLOCK->INTENSET = CLOCK_INTENSET_HFCLKSTARTED_Msk;
    // Enable wake-up on event
    SCB->SCR |= SCB_SCR_SEVONPEND_Msk; 
    NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_HFCLKSTART = 1; 
    while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) 
    {
      __WFE();
    }
    NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn);
    
    

    In general, I recommend reading up on WFI/WFE/SEV instructions: infocenter.arm.com/.../index.jsp infocenter.arm.com/.../index.jsp

    BR Håkon

Related