Zero Crossing Detector Using on-chip analog comparator atmega328p
Arduino Zero Crossing Detector #2 - 1

On-Chip Analog Comparator

Continuing from my previous post, I will now present the second experiment which is a Zero Crossing Detector using the On-Chip Analog Comparator in ATmega328.

What is On-Chip Analog Comparator

The On-Chip Analog Comparator is a device that compares two analog input signals and determines which is greater. The output is a digital signal indicating whether the input signal is greater or less than the reference.

The On-Chip Analog Comparator in the ATmega328 chip is already integrated into the chip. It can be activated and configured through certain registers and bits in the chip’s microcontroller.

In addition to comparing two input signals, it can also trigger an interrupt or function as a Timer/Counter input capture. It is important to consult the ATmega328 datasheet for more detailed information on how to use the On-Chip Analog Comparator.

How to Use On-Chip Analog Comparator

Below is the Block Diagram of the On-Chip Analog Comparator

On-Chip Analog Comparator block diagram

The On-Chip Analog Comparator can be used to build a zero-crossing detector. Which is a circuit that detects when an AC waveform crosses the zero voltage level. The applications such as phase control, where the load needs to be synchronized with the AC waveform.

To use the On-Chip Analog Comparator as a zero crossing detector, you will need to follow these steps:

  1. Set up the circuit:
    Connect the AC waveform to the positive input of the comparator (pin D6 or AIN0) and a reference voltage (such as 0V) to the negative input (pin D7 or AIN1).
  2. Configure the interrupt:
    Enable the interrupt on the ACIE bit in the ACSR register
  3. Set up the Timer/Counter1 input capture:
    If you want to use the On-Chip Analog Comparator as an input capture for Timer/Counter1, set the ICES1 bit in the TCCR1B register to trigger the capture on the rising edge of the AC waveform.
  4. Write the interrupt handler:
    In the interrupt handler, you can read the ACO output from the ACSR register to determine when the AC waveform has crossed the zero voltage level. You can then perform the necessary actions, such as switching a load, based on this information.

The above statements are for activating the on-chip analog comparator by accessing registers on the ATmega3284. The Arduino platform already has a library for using this function in a more understandable way.
And I will discuss it later in the coding section.

Zero Crossing Detector Circuit

I need to add several other components to make it a Zero Crossing Detector.

ATmega 328P On-Chip Analog Comparator

Using a diode clipper circuit and resistor to limit the current, and as usual, I use a 220V to 12V transformer for safety when working.
Connect the diode clipper circuit to the positive input, which is pin 6 (D6/AIN0) of the Arduino. Connect the negative input (D7/AIN1) to the ground. The comparator compares the input voltage, which is an AC signal, to the zero voltage, resulting in the output changing as it passes through the zero voltage

ZCD with On-Chip Analog Comparator Coding

I included the following lines of code in this Arduino sketch:

#include <avr/interrupt.h>
#include <analogComp.h>

Setup()

The #include directive tells the compiler to include the contents of the specified file in the source code. The file contains declarations for interrupt service routines and the file is a library for using the analog comparator of the Atmega328p microcontroller.

This code executed in the setup() function:

pinMode(13,OUTPUT);
analogComparator.setOn(AIN0, AIN1); 
analogComparator.enableInterrupt(interruptZCD,CHANGE);

The pinMode() function sets the specified pin (in this case, pin 13) as an output.
The setOn() function of the analogComparator object instructs the library to use the voltages on the specified pins (in this case, AIN0 and AIN1) as the inputs for the analog comparator.
The ‘setOn’ function here is equivalent to setting the ACD bit in the ACSR register. By writing a logic 0 to the ACD, the On-Chip Comparator is activated.

The enableInterrupt() function of the analogComparator object enables the interrupt for the comparator and specifies the function to be called when the interrupt occurs (in this case, interruptZCD) and the type of interrupt (in this case, CHANGE).
The enableInterrupt function here is used to set the ACIE bit in the ACSR register to a logic 1, enabling interrupts. A logic 0 disables interrupts.
And interruptZCD is a function that we have created to serve an interrupt when a certain event triggers it. The behavior of this function is also customized according to the needs.
Finally, the event that triggers the interrupt is specified. Here, I am using the CHANGE event, which means that the interrupt occurs whenever there is a change in logic on the analog comparator’s output. The interrupt will occur when the output changes from high to low or vice versa.

Interupt Service Routine

The interruptZCD() function is an interrupt service routine (ISR) that is called when the interrupt for the analog comparator occurs. In this function, the following line of code is executed:

digitalWrite(13, !digitalRead(13));   

The digitalWrite() function sets the specified pin (in this case, pin 13) to either HIGH or LOW. The digitalRead() function reads the value of the specified pin (in this case, pin 13) and returns HIGH or LOW. The ! operator negates the value, so if the value is HIGH, it becomes LOW, and if the value is LOW, it becomes HIGH. This effectively toggles the value of the pin, turning it on if it is off and turning it off if it is on.
What I want here is to compare the pulse signal resulting from zero cross detection with the AC signal.

Loop()

The loop() function is the main loop of the Arduino sketch. It is executed repeatedly, and in this case, it is empty.

#include <avr/interrupt.h>
#include <analogComp.h>

void setup() {
  pinMode(13,OUTPUT);
  analogComparator.setOn(AIN0, AIN1); 
  analogComparator.enableInterrupt(interruptZCD,CHANGE);
}

void interruptZCD() {
    digitalWrite(13, !digitalRead(13));   // toggle pin 13
}

void loop() {
  // put your main code here, to run repeatedly:
}

Integration and results

After connecting all the circuits and uploading the code without errors, I observe the output on the oscilloscope as follows:

zero crossing detector output vs input with On-Chip Analog Comparator

The blue colour display indicates an input signal and the yellow colour is for ZCD output (pin 13). We can see that the input and output overlap almost when passing through zero volts.

I will make the oscilloscope reading look like this by enlarging it.

On-Chip Analog Comparator delay

There is a slight delay between when the AC input passes through zero volts and the ZCD output changes. The measured time difference is around 100uS as can be seen in the above picture.

Application example

I turned this application into a frequency counter that displays on the Arduino serial comm, for example.

#include <avr/interrupt.h>
#include <analogComp.h>

volatile unsigned long microsLast;

void setup() {
  pinMode(13,OUTPUT);
  analogComparator.setOn(AIN0, AIN1); 
  analogComparator.enableInterrupt(interruptZCD,CHANGE);
  Serial.begin(115200);
}

void interruptZCD() {
  unsigned long microsNow;
  unsigned long period;
  float freq;

  microsNow = micros();
  period = microsNow - microsLast ;    // period in micro second
  freq = 1000000.0/period;
  Serial.print("Freq = ");            // for test purpose 
  Serial.println(freq);               // for test purpose 
  microsLast=microsNow;
}

void loop() {
  // do nothing
}

interruptZCD is a function that is called whenever a zero crossing is detected. It calculates the period and frequency of the voltage changes by taking the difference between the current time (measured in microseconds) and the time of the last voltage change.

The function first declares three variables:

  • microsNow” which will store the current time in microseconds
  • period” which will store the difference between the current time and the time of the last voltage change
  • freq” which will store the frequency of the voltage changes, calculated by dividing 1,000,000 by the period in microseconds

Next, the function assigns the current time to the “microsNow” variable using the “micros()” function, which returns the number of microseconds since the Arduino board began running the current program.

The “period” variable is then calculated by taking the difference between the current time and the time of the last voltage change, which is stored in the “microsLast” variable.
The frequency of the voltage changes is then calculated by dividing 1,000,000 by the period and stored in the “freq” variable.

The function then prints the frequency to the serial monitor using the “Serial.print()” and “Serial.println()” functions, which send data to the serial port of the Arduino board so it can be displayed on a computer or other device.

Finally, the “microsLast” variable is updated to store the current time, so it can be used as the reference time for the next voltage change.

Conclusion

I used the On-Chip Analog Comparator in the ATmega328 microcontroller to build a zero-crossing detector circuit in this experiment.
The On-Chip Analog Comparator is a device that compares two analog input signals and produces a digital output indicating which signal is greater. It can also trigger an interrupt or function as a Timer/Counter input capture.
To use it as a zero-crossing detector, it is necessary to connect the AC waveform to the positive input of the comparator and a reference voltage (such as 0V) to the negative input.
The interrupt can then be enabled and the necessary actions, such as switching a load, can be performed in the interrupt handler when the AC waveform crosses the zero voltage level.
The Arduino platform provides a library for using the On-Chip Analog Comparator in a more intuitive way.

on chip analog comparator atmega328p as zero crossing detector
Watch on YouTube