atmel-asf-projects/sam0/applications/sleepwalking_adc/vcc_monitor.c

290 lines
9.8 KiB
C

/*
* \file
*
* \brief SAM ADC Sleepwalking Example
*
* Copyright (C) 2013-2016 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
/**
* \mainpage SAM ADC Sleepwalking Example
* See \ref appdoc_main "here" for project documentation.
* \copydetails appdoc_preface
*
*
* \page appdoc_preface Overview
* This application demonstrates how to configure the ADC in a sleepwalking
* operation, where the input voltage is measured at a fixed interval,
* but the device is not woken from sleep until the measured value is below
* a given threshold; in this application 0.5 Volts.
*/
/**
* \page appdoc_main SAM ADC Sleepwalking Example
*
* Overview:
* - \ref appdoc_sam0_adc_sleepwalking_intro
* - \ref appdoc_sam0_adc_sleepwalking_setup
* - \ref appdoc_sam0_adc_sleepwalking_imp
* - \ref appdoc_sam0_adc_sleepwalking_compinfo
* - \ref appdoc_sam0_adc_sleepwalking_contactinfo
*
* \section appdoc_sam0_adc_sleepwalking_intro Introduction
* This application demonstrates how to configure the ADC in a sleepwalking
* operation, where the input voltage is measured at a fixed interval,
* but the device is not woken from sleep until the measured value is below
* a given threshold; in this application 0.5 Volts.
*
* This application has been tested on following boards:
* - SAM D20/D21/R21/D11/L21/L22/C21/R30 Xplained Pro
*
* \section appdoc_sam0_adc_sleepwalking_setup Hardware Setup
* This application use AIN0 as ADC input channel.
* When the measured voltage is lower than 0.5V, then the device will start
* toggling the led pin to signal that the low voltage condition has happened.
* Connect the PA02(EXT3 pin3) to GND in SAM D20/D21 Xplained Pro to trigger it.
* Connect the PA06(EXT1 pin3) to GND in SAM R21 Xplained Pro to trigger it.
* Connect the PA02(EXT1 pin3) to GND in SAM D10/D11 Xplained Pro to trigger it.
* Connect the PA03(EXT1 pin4) to GND in SAM L21/L22 Xplained Pro to trigger it.
* Connect the PA03_ADC_DAC_VREF(J701 pin4) to GND in SAM C21 Xplained Pro to trigger it.
* Connect the PA04(EXT1 pin14) to GND in SAM R30 Xplained Pro to trigger it.
*
* If debugging it is also possible to start a debug session and place a
* breakpoint in the window callback that will trigger whenever the voltage
* has gone below the defined threshold.
*
* The application can easily be modified to monitor other voltages by changing
* the input source and threshold values in the \c adc_setup function.
*
* \section appdoc_sam0_adc_sleepwalking_imp Implementation Details
*
* \subsection appdoc_sam0_adc_sleepwalking_adc ADC Configuration
* The ADC is configured to perform hardware averaging on the measured value,
* where the ADC will sample the signal 16 times and find the average value in
* hardware. This smooths out high frequency noise.
*
* \subsection appdoc_sam0_adc_sleepwalking_rtc Sample timing
* To trigger the ADC conversions, the RTC is setup in 32-bit counter mode,
* clocked by the internal 32kHz oscillator divided by 32, giving a 1kHz input
* clock. A compare value of 1000 is used to trigger the ADC conversion every
* second.
*
* \subsection appdoc_sam0_adc_sleepwalking_events Event Routing
* The Event system is setup to route the compare match interrupt from the RTC
* to the ADC, and the ADC is configured to start a new conversion on an event
* input. The event channel is configured as an asynchronous channel, so no
* clock is needed for the event channel during sleep.
*
* \section appdoc_sam0_adc_sleepwalking_compinfo Compilation Info
* This software was written for the GNU GCC and IAR for ARM.
* Other compilers may or may not work.
*
* \section appdoc_sam0_adc_sleepwalking_contactinfo Contact Information
* For further information, visit
* <a href="http://www.atmel.com">http://www.atmel.com</a>.
*/
/*
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#include <asf.h>
/* ADC module instance */
struct adc_module module_inst;
/* RTC module instance */
struct rtc_module rtc_instance;
/* Flag to indicate if a low-voltage situation has occurred */
volatile bool low_voltage = false;
/* ADC Window callback; called whenever the supply voltage drops
* below ~0.5 Volts */
static void adc_window_callback(
struct adc_module *const module)
{
/* Signal the application that the voltage has gone below the
threshold */
low_voltage = true;
}
/* Setup RTC as ADC sample timer */
static void rtc_setup(void)
{
struct rtc_count_config conf;
rtc_count_get_config_defaults(&conf);
conf.prescaler = RTC_COUNT_PRESCALER_DIV_1;
conf.mode = RTC_COUNT_MODE_32BIT;
conf.clear_on_match = true;
conf.compare_values[0] = 1000;
struct rtc_count_events evconfig;
evconfig.generate_event_on_compare[0] = true;
rtc_count_init(&rtc_instance, RTC, &conf);
rtc_count_enable_events(&rtc_instance, &evconfig);
rtc_count_enable(&rtc_instance);
}
/* Setup the event system to route RTC events to the ADC */
static void event_setup(struct events_resource *event)
{
struct events_config config;
events_get_config_defaults(&config);
/* Setup a event channel with RTC compare 0 as input */
config.generator = EVSYS_ID_GEN_RTC_CMP_0;
config.path = EVENTS_PATH_ASYNCHRONOUS;
#if (SAML21) || (SAML22) || (SAMC21) || (SAMR30)
config.run_in_standby = true;
config.on_demand = true;
#endif
events_allocate(event, &config);
/* Setup ADC to listen to the event channel */
#if (SAMC21)
events_attach_user(event, EVSYS_ID_USER_ADC0_START);
#else
events_attach_user(event, EVSYS_ID_USER_ADC_START);
#endif
}
/* Setup the ADC to sample the internal scaled VCC */
static void adc_setup(void)
{
struct adc_config config;
adc_get_config_defaults(&config);
#if (!SAML21) && (!SAML22) && (!SAMC21) && (!SAMR30)
config.gain_factor = ADC_GAIN_FACTOR_1X;
#endif
/* Use GCLK generator 4 as clock source */
config.clock_source = GCLK_GENERATOR_4;
/* Divide input clock by 4 */
config.clock_prescaler = ADC_CLOCK_PRESCALER_DIV4;
#if (SAML21) || (SAML22) || (SAMC21) || (SAMR30)
/* Use internal bandgap reference */
config.reference = ADC_REFERENCE_INTREF;
#else
/* Use internal 1V band-gap reference */
config.reference = ADC_REFERENCE_INT1V;
#endif
/* Start new conversion on event */
config.event_action = ADC_EVENT_ACTION_START_CONV;
/* Enable the ADC to run in standby sleep mode */
config.run_in_standby = true;
/* Average 16 samples in hardware (accumulate 16 samples and divide by 16) */
config.resolution = ADC_RESOLUTION_CUSTOM;
config.accumulate_samples = ADC_ACCUMULATE_SAMPLES_16;
config.divide_result = ADC_DIVIDE_RESULT_16;
#if SAMR30
config.positive_input = ADC_POSITIVE_INPUT_PIN4;
#endif
/* Configure window */
config.window.window_mode = ADC_WINDOW_MODE_BELOW_UPPER;
#if (SAML21) || (SAML22) || (SAMC21) || (SAMR30)
config.on_demand = true;
#endif
config.window.window_upper_value = 2048;
/* Apply configuration to ADC module */
#if (SAMC21)
adc_init(&module_inst, ADC0, &config);
#else
adc_init(&module_inst, ADC, &config);
#endif
/* Enable ADC */
adc_enable(&module_inst);
/* Register and enable the window callback */
adc_register_callback(&module_inst, adc_window_callback,
ADC_CALLBACK_WINDOW);
adc_enable_callback(&module_inst, ADC_CALLBACK_WINDOW);
}
int main(void)
{
struct events_resource event;
/* Initialize clock system */
system_init();
/* Initialize delay service */
delay_init();
rtc_setup();
event_setup(&event);
adc_setup();
system_interrupt_enable_global();
/* Set sleep mode to STANDBY */
system_set_sleepmode(SYSTEM_SLEEPMODE_STANDBY);
#if (SAML21) || (SAMR30)
/* Configure standby mode */
struct system_standby_config config;
system_standby_get_config_defaults(&config);
config.enable_dpgpd0 = true;
config.power_domain = SYSTEM_POWER_DOMAIN_PD0;
system_standby_set_config(&config);
#elif (SAML22)
/* Configure standby mode */
struct system_standby_config config;
system_standby_get_config_defaults(&config);
system_standby_set_config(&config);
#endif
/* Stay in STANDBY sleep until low voltage is detected */
system_sleep();
while (true) {
if (low_voltage == true) {
while (true) {
/* Toggle pin ad-infinitum */
port_pin_toggle_output_level(LED0_PIN);
delay_ms(500);
}
}
}
}