Zero Crossing Detector Arduino Uno.

I use this method from the ATmega328p application notes to make Zero Crossing Detector. Please check the following link for more details: https://www.microchip.com/en-us/application-notes/an2508

Simplest Zero Crossing DetectorCircuit

The circuit uses two 1Mohm resistors to limit the current to less than 1mA at the microcontroller input port. Then connect directly to a 220V AC network.

The ATmega328 input circuit has a clamping diode as protection against voltages greater than Vcc. But the current at the input is limited to a value of around 1mA.

atmega
Arduino Uno Zero Crossing Detector

I added a 220V to 12V transformer for safety reasons and changed the resistor to 330kOhm. The intention is that the current in the port is not more than 1 mA. So the circuit becomes like this:

Code for Arduino Uno Zero Crossing Detector

An AC signal that changes from positive to negative or vice versa will pass through 0V (Zero Crossing).
That event will trigger an interrupt so that it calls the interrupt service routine (ISR) which I named interruptZeroCross.

attachInterrupt(digitalPinToInterrupt(2),interruptZeroCross,CHANGE);

The above code is initializing the ISR INT0(pin D2) with the name interruptZeroCross for the CHANGE event. So that when the logic changes at pin D2, the routine interruptZeroCross will be called.

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

The routine function above is for toggling the output logic when an interrupt occurs.

The output on the built-in LED pin will change On/Off every time an interrupt occurs. I use this pin to measure the duty cycle and also the delay that occurs using an oscilloscope.

1. ZCD Test

The output on pin 13 will go high when the AC signal goes up so it is greater than 0V or it becomes positive and goes low when the AC signal drops lower than 0V so it becomes negative.

//Yopie DIY
//2022
#include <avr/interrupt.h>

void setup() {
  pinMode(13,OUTPUT);       // for testing only
  attachInterrupt(digitalPinToInterrupt(2),interruptZeroCross,CHANGE);
}
void interruptZeroCross(){
  digitalWrite(13, !digitalRead(13));   // toggle pin 13
}
void loop() {
  // do nothing 
}

The test I mean here is the response test from zero cross-detection. The time difference or delay between the occurrence of zero cross and the occurrence of Interrupt is what I measure here.

It can be seen that the interrupt occurs not at the zero cross, but when it passes the 2.5V voltage (Vcc/2). Thus there is a delay between the actual zero cross and the occurrence of an interrupt. As a result, the detected duty cycle is not as expected, which is 50%.

This is the result I use a 12V input signal. By increasing the input signal, the time difference between an interrupt and a zero cross becomes smaller. Thus the duty cycle will also be closer to 50%. In other words, the zero cross-detection results with this circuit are affected by the input voltage amplitude.

This can be a problem if we want to measure the phase angle or measure the duty cycle in this way.

2. Frequency Meter

One of the uses of this circuit is to measure the frequency of the grid. Phase angle delay and duty cycle error have no effect on this measurement.

This code will show the value of the AC signal frequency using the Serial Monitor in the Arduino IDE. The displayed frequency is the result of measuring the period of time between two interrupts in microseconds and is converted to a frequency.

f = 1000000/T (T in uS)

//Yopie DIY
//2022

#include <avr/interrupt.h>

volatile unsigned long microsLast;

void setup() {
//  pinMode(13,OUTPUT);       // for testing only
  Serial.begin(115200);     // for testing only
// attachInterrupt(digitalPinToInterrupt(2),interruptZeroCross,CHANGE);
  attachInterrupt(digitalPinToInterrupt(2),interruptZeroCross,RISING);
}

void interruptZeroCross(){
//  digitalWrite(13, !digitalRead(13));   // toggle pin 13
  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 
}

For frequency measurements, interrupt events are set to occur when pin D2 goes up (RISING).

When an interrupt occurs, the current microsecond value on the Arduino is read and then the difference in time is calculated with the previous reading. The result is the value for the period, then the period is converted to a frequency where F=1/T. The end result is a frequency value that can be read on the serial communication terminal. Before exiting the ISR, the micros() value is stored for use in the next interrupt.

3. Resume

Before ending this post I will write down the advantages and disadvantages of using this method.

Pros

  • Simple circuit
  • Simple coding

Cons

  • The detection isn’t really at 0V, it’s happening at 2.5V
  • Changes in amplitude affect detection, resulting in changes in delay and duty cycle

Thus the post about experiments using a simple ZCD from the Application Note ATmega328 series. See you again in posts about other projects, hopefully, this is useful.

Have a nice day…