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.

Note: For each use case described in this document, there are two code examples: One bare metal developed on ATmega4809, and one generated with MPLAB® Code Configurator (MCC) developed on AVR128DA48.

3.2 Overview

The AC is a module that compares two analog input voltages and outputs a signal level indicating when one of the inputs is higher than the other. An AC is basically an amplifier without feedback and thus has very high gain.
Figure 3-20. Analog Comparator Block Diagram

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).

Figure 3-21. Analog Comparator as Voltage Monitor

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:

Figure 3-22. 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.

Figure 3-23. VREF Block Diagram

The VREF provides control registers for selecting between multiple internal reference levels. The internal references are generated from an internal band gap.

Figure 3-24. VREF.CTRLA - AC Voltage Reference Selection

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:

Figure 3-25. VREF.CTRLB - Enable the AC0REFEN bit

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:

Figure 3-26. AC Input Selection

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;
The analog value used by AC (VDACREF) is derived from internal reference using the DACREF register, and the output voltage is defined by:
V D A C R E F = D A C R E F 256 × V R E F
The DACREF value can be calculated using the following macro:
#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).
Important: Configure the DACREF register to select 0.8V on the negative input to implement this application. The user must select a proper combination of R1 and R2 resistors (see Figure 3-21) in such a way that when battery voltage low threshold is reached, voltage V1 will be 0.8V:
V 1 = 0.8 V = V B A T × R 2 ( R 1 + R 2 )
Assuming  V B A T = 3 V    and R2 = 10 k Ω , the value of R1 must be 27.5 k Ω

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:

Figure 3-27. AC0.CTRLA - Set AC, Enable Interrupts and Output

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:

Figure 3-28. AC0.INTCTRL - Enable AC Interrupt
Tip: The full code example is available in the Appendix section.

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.

Figure 3-29. Analog Comparator Response With and Without Hysteresis Enabled
Configure the hysteresis by writing to the Hysteresis Mode Select (HYSMODE[1:0]) bit field in the Control A register:
Figure 3-30. AC0.CTRLA - Configure Hysteresis Mode
A medium hysteresis is used (25 mV), which translates in the following sequence of code:
AC0.CTRLA |= AC_HYSMODE_25mV_gc;
Tip: The full code example is also available in the Appendix section.

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.

Figure 3-31. Analog Signal Duration/Frequency Measurement Block Diagram

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:

Figure 3-32. EVSYS.CHANNEL - Set 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:

Figure 3-33. TCB - Input Capture Frequency and Pulse-Width Measurement

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;			

}
Tip: The full code example is also available in the Appendix section.

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

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;
        }
    }
}