SAMD21, ADC12 how to correctly turn off ADC12 before sleep

Go To Last Post
3 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How to correctly disable and turn off ADC12 to reduce current consumption to a minimum prior to sleepmgr_sleep(SLEEPMGR_IDLE_2);

 

It seems to not work correctly. 

 

I only use it to capture the internal power voltage channel for battery state, so after wakeup (by RTC), I just reinit the ADC12 and continue. 

 

void ADC12_Module_Power(bool EnablePower, bool freerunningmode)
{
	if (EnablePower==true)
	{
		struct adc_config conf_adc;
		adc_get_config_defaults(&conf_adc);
		conf_adc.clock_source = GCLK_GENERATOR_3;								// 8MHz
		conf_adc.clock_prescaler = ADC_CLOCK_PRESCALER_DIV512;					// 15K625Hz
		conf_adc.reference = ADC_REFERENCE_INT1V;								// 1.0V
		conf_adc.positive_input = ADC_POSITIVE_INPUT_SCALEDIOVCC;				// VDD or VDDIN
		conf_adc.negative_input = ADC_NEGATIVE_INPUT_GND;
		conf_adc.freerunning = freerunningmode;
		conf_adc.sample_length = 4;												// Sample Length.
		adc_init(&adc_instance, ADC, &conf_adc);								// This include power up the ADC12 module.
		ADC->AVGCTRL.reg = ADC_AVGCTRL_ADJRES(2) | ADC_AVGCTRL_SAMPLENUM_4;		// x4 Average Sample
		//----------------------------------------------------------------------
		ADC12Flags.isPowerEnabled=true;
	}
	else
	{
		adc_disable(&adc_instance);
		system_gclk_chan_disable(ADC_GCLK_ID);
		ADC12Flags.isPowerEnabled=false;
		system_voltage_reference_disable(SYSTEM_VOLTAGE_REFERENCE_BANDGAP);
		system_apb_clock_clear_mask(SYSTEM_CLOCK_APB_APBC, PM_APBCMASK_ADC);		// Turn OFF the digital interface clock
	}
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It seems to jump to dummy handler loop at this juncture below......any suggestion how to fix this?

/**
 * \brief Disables the ADC module.
 *
 * Disables an ADC module that was previously enabled.
 *
 * \param[in] module_inst Pointer to the ADC software instance struct
 */
static inline enum status_code adc_disable(
        struct adc_module *const module_inst)
{
    Assert(module_inst);
    Assert(module_inst->hw);

    Adc *const adc_module = module_inst->hw;

#if ADC_CALLBACK_MODE == true
#   if (ADC_INST_NUM > 1)
    system_interrupt_disable(_adc_interrupt_get_interrupt_vector(
            _adc_get_inst_index(adc_module)));
#   elif (SAMC20)
        system_interrupt_disable(SYSTEM_INTERRUPT_MODULE_ADC0);
#    else
        system_interrupt_disable(SYSTEM_INTERRUPT_MODULE_ADC);
#   endif
#endif

    while (adc_is_syncing(module_inst)) {
        /* Wait for synchronization */
    }

    /* Disbale interrupt */
    adc_module->INTENCLR.reg = ADC_INTENCLR_MASK;       <<<<<<<<< Jump to dummy handler loop, why?
    /* Clear interrupt flag */
    adc_module->INTFLAG.reg = ADC_INTFLAG_MASK;

    adc_module->CTRLA.reg &= ~ADC_CTRLA_ENABLE; 

    while (adc_is_syncing(module_inst)) {
        /* Wait for synchronization */
    }
    return STATUS_OK;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why disabling the interrupt causes an ISR to fire - I do not know.

 

The reason the "dummy handler" is called (Which traps you in a loop), is because you have not implemented/defined the appropriate ISR function for that interrupt.. All of the handlers are defined in your startup_samXXXX.c file with a weak, alias attribute.. meaning if you don't define the handler yourself, they will instead use and jump to the Dummy_Handler function.. You don't need to touch the startup file (Since they are "weak" you can define them elsewhere and the definition in the startup file will be overwritten)

 

You need to figure out which ISR handler is attempting to fire, and create a function definition for it. (ADC0_0_Handler, ADC0_1_Handler, etc....)

 

void ADC0_0_Handler(void)
{
    // Do nothing, but at least give the code an ISR to execute
    // instead of using the Dummy Handler.
}

 

murph

Debugging - Being a detective in a crime movie where you are also the murderer.