3 Getting Started with AC
3.1 Introduction
Author: Cristian Pop, Microchip Technology Inc. |
Microchip tinyAVR® 0- and 1-series, megaAVR® 0-series and AVR® Dx devices feature an Analog Comparator (AC) with flexible input selection, selectable hysteresis and configurable output (interrupts on rising/falling or both edges, event generation, and output inversion).
This technical brief explains the AC concepts and its implementation in tinyAVR® 0- and 1-series, megaAVR® 0-series and AVR® Dx devices with the following use cases:
- Level Crossing Detector:
This example shows how to use the AC to detect when a critical value of an analog signal is reached (for example, the battery level).
- Preventing False Spike Detection:
This example demonstrates how to use the hysteresis feature to minimize the number of false transitions in a noisy environment.
- Analog Signal Pulse Duration Frequency Measurements:
The example describes how to use the AC together with a timer to measure the pulse duration and/or the period of analog signals with minimal intervention of the AVR® core.
3.2 Overview
This figure shows the block diagram of the analog comparator in tinyAVR® 0- and 1-series, and megaAVR® 0-series devices. It compares the voltage levels on two inputs and gives a digital output based on this comparison.
The dynamic behavior of the AC can be adjusted by a hysteresis feature. The hysteresis can be customized to optimize the operation for each application. The input selection includes analog port pins and internally generated inputs.
The comparator has one positive input and one negative input. The positive
input may be chosen from a selection of analog input pins. The negative input may be
chosen from a selection of analog input pins or internal inputs, such as a band gap
reference voltage (DACREF). The digital output from the comparator is
‘1
’ when the difference between the positive and the negative
voltage is positive, and ‘0
’ otherwise.
The AC can be configured to generate interrupt requests and/or events upon several different combinations of input change. The AC output state can also be the output on a pin for use by external devices.
3.3 Level Crossing Detector
This example shows a basic initialization and setup for the AC peripheral. The application monitors an analog input signal, compares it to a fixed voltage, and notifies the user via interrupt and an output pin every time the input signal crosses the fixed voltage level. The comparator can be used to monitor battery voltage (or any other DC level).
To monitor an external voltage, the AC input must be connected to this voltage using an I/O pin. This pin needs to have the digital input buffer and the pull-up resistor disabled, to have the highest possible input impedance. For the ATmega4809, PORTD pin 2 (PD2/AINP0) is used as AC positive input:
This translates to the following code:
PORTD.PIN2CTRL = PORT_ISC_INPUT_DISABLE_gc;
In the selected application, the AC uses the analog pin as positive input and an internal reference for its negative input. The Voltage Reference (VREF) peripheral must be configured before using the internal voltage reference on negative input.
The VREF provides control registers for selecting between multiple internal reference levels. The internal references are generated from an internal band gap.
A value of 1.5V is selected as reference voltage:
VREF.CTRLA = VREF_AC0REFSEL_1V5_gc;
To enable VREF voltage generation, the output buffer must be enabled after the selection of reference voltage. Do this by setting the AC0REFEN bit from the VREF Control B register:
This translates to the following code:
VREF.CTRLB = VREF_ADC0REFEN_bm;
After configuring the peripherals and the modules required by the AC, the selection of inputs is done using the MUXCTRLA register as follows:
The positive input pin 0 (AINP0) and internal DAC reference are used as AC inputs:
AC0.MUXCTRLA = AC_MUXPOS_PIN0_gc | AC_MUXNEG_DACREF_gc;
#define DACREF_VALUE (VDACREF * 256 / VREF)where VDACREF represents the desired value on the analog comparator input and VREF represents the value selected as internal reference (1.5 Volts).
The output of the comparator is present on the external pin, and the interrupts are enabled on the negative edge to notify the application when the critical level is reached. That is done using the CTRLA register:
These settings translate to the following code:
AC0.CTRLA = AC_ENABLE_bm | AC_INTMODE_NEGEDGE_gc | AC_OUTEN_bm;
The AC peripheral interrupt must be enabled to complete the setup and make the selected interrupt available to the application. Do this by using the CMP bit from the INTCTRL register:
An MCC generated code example for AVR128DA48 with the same functionality as the one described in this section can be found here:
3.4 Preventing False Spike Detection
This example demonstrates the hysteresis feature of the AC peripheral that helps in avoiding frequent toggling of the AC when the positive input oscillates very close to the negative input level. This application is similar to the voltage level detector application, but additionally it has the Hysteresis mode enabled.
AC0.CTRLA |= AC_HYSMODE_25mV_gc;
An MCC generated code example for AVR128DA48 with the same functionality as the one described in this section can be found here:
3.5 Analog Signal Pulse Duration/Frequency Measurements
The tinyAVR® 0- and 1-series, megaAVR® 0-series and AVR® Dx devices feature Event System (EVSYS), a simple but powerful system that allows autonomous control of peripherals without any use of interrupts, CPU, or DMA resources. It allows a change in one peripheral (the event generator) to trigger actions in other peripherals (the event users) through event channels. It provides short and predictable response times between peripherals and can reduce the complexity, size, and execution time of the software, to save power.
The application example in this chapter shows an implementation of duration/frequency measurement for an analog input signal, with minimal usage of microcontroller power. It uses the Event System to route the signals from the AC output through an event channel to Timer Counter B (TCB) event input. Configure the Event System properly to do this.
The first step in the Event System configuration is to set the AC output as event generator for channel 0:
For EVSYS channel 0, the channel generator selection register must be loaded with
0x20
to enable analog comparator as event generator:
EVSYS.CHANNEL0 = EVSYS_GENERATOR_AC0_OUT_gc;
To trigger events on TCB input, the TCB event user must be connected to channel 0:
EVSYS.USERTCB0 = EVSYS_CHANNEL_CHANNEL0_gc;
To enable pulse and period measurements, the TCB is configured in Pulse-Width Measurement mode, having the Event System as input. The Event System is used to route AC output through event channel 0 to TCB event input.
In Pulse-Width Measurement mode, the TCB will start counting when a positive edge is detected on the event input signal. On the next falling edge, the count value is captured. The counter stops when the second rising edge of the event input signal is detected, and this will set the Interrupt flag. Reading the capture will clear the Interrupt flag. When the Capture register is read, or the Interrupt flag cleared, the TCB becomes ready for a new capture sequence. Therefore, it is recommended to read the Counter register before the Capture register since it is reset to zero at the next positive edge:
The following code provides basic initialization for TCB in Pulse-Width Measurement mode with Event System as input:
int8_t TIMER0_init()
{
TCB0.CTRLB = TCB_CNTMODE_FRQPW_gc;
TCB0.EVCTRL = TCB_CAPTEI_bm;
TCB0.INTCTRL = TCB_CAPT_bm;
TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc
| TCB_ENABLE_bm
| TCB_RUNSTDBY_bm;
}
An MCC generated code example for AVR128DA48 with the same functionality as the one described in this section can be found here:
3.6 References
- AVR128DA48 product page: www.microchip.com/wwwproducts/en/AVR128DA48
- AVR128DA48 Curiosity Nano Evaluation Kit product page: https://www.microchip.com/Developmenttools/ProductDetails/DM164151
- AVR128DA28/32/48/64 Data Sheet
- Getting Started with the AVR® DA Family
- ATmega4809 product page: www.microchip.com/wwwproducts/en/ATMEGA4809
- megaAVR® 0-series Family Data Sheet
- ATmega809/1609/3209/4809 – 48-Pin Data Sheet megaAVR® 0-series
- AN2451 - Getting Started with Core Independent Peripherals on AVR® Microcontrollers (DS00002451)
3.7 Appendix
Level Crossing Detector Source Code
#include <avr/io.h> #include <avr/interrupt.h> /* set DACREF to 0.8 Volts for Vref = 1.5Volts */ #define DACREF_VALUE (0.8 * 256 / 1.5) void PORT0_init (void); void AC0_init(void); ISR(AC0_AC_vect) { /* Insert AC interrupt handling code here */ /* The interrupt flag has to be cleared manually */ AC0.STATUS = AC_CMP_bm; } void PORT0_init (void) { /* Positive Input - Disable digital input buffer */ PORTD.PIN2CTRL = PORT_ISC_INPUT_DISABLE_gc; /*Enable output buffer on PA7*/ PORTA |= PIN7_bm; } void AC0_init(void) { /* Negative input uses internal reference - voltage reference should be enabled */ VREF.CTRLA = VREF_AC0REFSEL_1V5_gc; /* Voltage reference at 1.5V */ VREF.CTRLB = VREF_AC0REFEN_bm; /* AC0 DACREF reference enable: enabled */ AC0.DACREF = DACREF_VALUE; /* Set DAC voltage reference */ /*Select proper inputs for comparator*/ AC0.MUXCTRLA = AC_MUXPOS_PIN0_gc /* Positive Input - Analog Positive Pin 0 */ | AC_MUXNEG_DACREF_gc; /* Negative Input - DAC Voltage Reference */ AC0.CTRLA = AC_ENABLE_bm /* Enable analog comparator */ | AC_OUTEN_bm; /* Output Buffer Enable: enabled */ AC0.INTCTRL = AC_CMP_bm; /* Analog Comparator 0 Interrupt enabled */ } int main(void) { PORT0_init(); AC0_init(); sei(); /* Global interrupts enabled */ while (1) { ; } }
Preventing False Spike Detection Source Code
#include <avr/io.h> #include <avr/interrupt.h> /* set DACREF to 0.8 Volts for Vref = 1.5Volts */ #define DACREF_VALUE (0.8 * 256 / 1.5) void PORT0_init (void); void AC0_init (void); ISR(AC0_AC_vect) { /* Insert AC interrupt handling code here */ /* The interrupt flag has to be cleared manually */ AC0.STATUS = AC_CMP_bm; } void PORT0_init (void) { /* Positive Input - Disable digital input buffer */ PORTD.PIN2CTRL = PORT_ISC_INPUT_DISABLE_gc; /*Enable output buffer on PA7*/ PORTA |= PIN7_bm; } void AC0_init (void) { /* Negative input uses internal reference - voltage reference should be enabled */ VREF.CTRLA = VREF_AC0REFSEL_1V5_gc; /* Voltage reference at 1.5V */ VREF.CTRLB = VREF_AC0REFEN_bm; /* AC0 DACREF reference enable: enabled */ AC0.DACREF = DACREF_VALUE; /* Set DAC voltage reference */ /*Select proper inputs for comparator*/ AC0.MUXCTRLA = AC_MUXPOS_PIN0_gc /* Positive Input - Analog Positive Pin 0 */ | AC_MUXNEG_DACREF_gc; /* Negative Input - DAC Voltage Reference */ AC0.CTRLA = AC_ENABLE_bm /* Enable analog comparator */ | AC_HYSMODE_25mV_gc /* Enable hysteresis @25mV */ | AC_OUTEN_bm; /* Output Buffer Enable: enabled */ AC0.INTCTRL = AC_CMP_bm; /* Analog Comparator 0 Interrupt enabled */ } int main(void) { PORT0_init(); AC0_init(); sei(); /*Global interrupts enabled */ while (1) { ; } }
Analog Signal Pulse Duration/Frequency Measurement Source Code
#include <avr/io.h> /* set DACREF to 0.8 Volts for Vref = 1.5Volts */ #define DACREF_VALUE (0.8 * 256 / 1.5) void PORT0_init (void); void EVENT_SYSTEM_init (void); void AC0_init (void); void TIMER0_init (void); void PORT0_init (void) { /* Positive Input - Disable digital input buffer */ PORTD.PIN2CTRL = PORT_ISC_INPUT_DISABLE_gc; /*Enable output buffer on PA7*/ PORTA |= PIN7_bm; } void AC0_init (void) { /* Negative input uses internal reference - voltage reference should be enabled */ VREF.CTRLA = VREF_AC0REFSEL_1V5_gc; /* Voltage reference at 1.5V */ VREF.CTRLB = VREF_AC0REFEN_bm; /* AC0 DACREF reference enable: enabled */ AC0.DACREF = DACREF_VALUE; /* Set DAC voltage reference */ /*Select proper inputs for comparator*/ AC0.MUXCTRLA = AC_MUXPOS_PIN0_gc /* Positive Input - Pin 0 */ | AC_MUXNEG_DACREF_gc; /* Negative Input - DAC Voltage Reference */ AC0.CTRLA = AC_ENABLE_bm /* Enable analog comparator */ | AC_OUTEN_bm; /* Output Buffer Enable: enabled */ AC0.INTCTRL = 0; /* Analog Comparator 0 Interrupt disabled */ } /*Init TCB in pulse width-frequency measurement mode, input from Analog Comparator through Event System */ void TIMER0_init (void) { TCB0.CTRLB = TCB_CNTMODE_FRQPW_gc; /* Input Capture Frequency and Pulse-Width measurement */ TCB0.EVCTRL = TCB_CAPTEI_bm; /* Event Input Enable: enabled */ TCB0.INTCTRL = TCB_CAPT_bm; /* Capture or Timeout: enabled */ TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc /* CLK_PER/2 (From Prescaler) */ | TCB_ENABLE_bm /* Enable: enabled */ | TCB_RUNSTDBY_bm; /* Run Standby: enabled */ } /* Enable event generation from Analog comparator to TCB */ void EVENT_SYSTEM_init (void) { EVSYS.CHANNEL0 = EVSYS_GENERATOR_AC0_OUT_gc; /* Analog Comparator 0 out linked to Event Channel 0 */ EVSYS.USERTCB0 = EVSYS_CHANNEL_CHANNEL0_gc; /* TCB uses Event Channel 0 */ } int main(void) { uint16_t signal_pulse = 0, signal_period = 0; PORT0_init(); AC0_init(); EVENT_SYSTEM_init(); TIMER0_init(); while(1) { if (TCB0.INTFLAGS) { /** * First read the CNT register * The interrupt flag is cleared by writing 1 to it, or when the Capture register * is read in Capture mode */ signal_period = TCB0.CNT; signal_pulse = TCB0.CCMP; } } }