Zero Crossing Detection Code. (Interruopt Problem)

Hello everybody,
I am trying to find the zero cross signal from a AC voltage by utilizing a interrupt on digital pin 11 on the arduino mega 2560, although it does not seem to be working. I believe I am commiting mistakes on the lines that are on red.

//Inputs and outputs
int firing_pin = 5;


//Variables
int last_CH1_state = 0;
bool zero_cross_detected = false;


int zero_cross = 11;

void setup() {
  //Define the pins
  pinMode (firing_pin,OUTPUT); 
  pinMode (zero_cross,INPUT); 



   [color=red]PCICR |= (1 << PCIE0);    //enable PCMSK0 scan                                                 
   PCMSK0 |= (1 << PCINT5);  //Set pin 11 (zero cross input) trigger an interrupt on state change.
[/color]
}

void loop() {    
       
  if (zero_cross_detected)     
    {      
      delayMicroseconds(maximum_firing_delay - PID_value); //This delay controls the power
      digitalWrite(firing_pin,HIGH);
      delayMicroseconds(100);
      digitalWrite(firing_pin,LOW);
      zero_cross_detected = false;
    } 



}//VOID LOOP FINISHES. 


//Interruption routines
//-----------------------------------------------------------------------------

[color=red]ISR(PCINT0_vect)
{
  if(PINB & B00010000){            
    if(last_CH1_state == 0){      
      zero_cross_detected = true;  
    }
  }
  else if(last_CH1_state == 1){       
    zero_cross_detected = true;    
    last_CH1_state = 0;            
    }
}
[/color]

why don't you use attachInterrupt function?
https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

Also, here is my full code with all the components in.

//MAX6675 config
#include “max6675.h”
//LCD config
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,20,4); //The address may change, if problem is found instead of 0x27 use 0x3f.

//Inputs and outputs
int firing_pin = 5;

int thermoDO = 9;
int thermoCS = 10;
int thermoCLK = 13;

//Start a MAX6675 communication with the selected pins
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);

//Variables
int last_CH1_state = 0;
bool zero_cross_detected = false;
int firing_delay = 7500;

//////////////////////////////////////////////////////
int maximum_firing_delay = 7500;
/*

  • The 7500 number comes from the frequency given by the power line which is about 50Hz to 60Hz depending on the country
  • therefore we find the period for these values which are 20ms and 16.6ms. Now with those values we can then control the
  • firing delay of each half period, having 10ms or 8ms. As a starting point in order not to surpass these values a total
  • of 7.4ms was choosen. (This value can be changed after testing and data collection of the exact frenquency value.
    */
    //////////////////////////////////////////////////////

unsigned long previousMillis = 0; //In mili-seconds.
unsigned long currentMillis = 0; //In mili-seconds.
int temp_read_Delay = 500; //In mili-seconds.
int real_temperature = 0;
int setpoint = 65; //In Farenheit.

//PID variables
float PID_error = 0;
float previous_error = 0;
float elapsedTime, Time, timePrev;
int PID_value = 0;
//PID constants
// The followign values of Kp, Ki and Kd can be changed according to the quality of the results after testing.
//int kp = 200; int ki= 0; int kd = 5;
int kp = 100; int ki= 7.2; int kd = 10.4;
int PID_p = 0; int PID_i = 0; int PID_d = 0;

// Used for generating interrupts using CLK signal
const int PinCLK = 3; // Must be on pin 3 because it is a interrupt.
// Used for the push button switch
const int PinSW = 40;
// Used for reading DT signal
const int PinDT = 41;
// Keep track of last rotary value
int lastCount = 0;
// Updated by the ISR (Interrupt Service Routine)
volatile int virtualPosition = 0;

int zero_cross = 11;

void setup() {
//Define the pins
pinMode (firing_pin,OUTPUT);
pinMode (zero_cross,INPUT);

// Interrupt Initialization.
// attachInterrupt(digitalPinToInterrupt(zero_cross), testing, CHANGE);

// LCD Initialization.
lcd.init(); //LCD communication.
lcd.backlight(); //Backlight for LCD.

// Rotary pulses are INPUTs.
pinMode(PinCLK, INPUT);
pinMode(PinDT, INPUT);
// Switch is floating so use the in-built PULLUP, therefore no need for a resistor.
pinMode(PinSW, INPUT_PULLUP);
// Attach the routine to service the interrupts
attachInterrupt(digitalPinToInterrupt(PinCLK), setPointControl, LOW);

PCICR |= (1 << PCIE2); //enable PCMSK0 scan
PCMSK2 |= (1 << PCINT16); //Set pin A8 (zero cross input) trigger an interrupt on state change.

}

void loop() {
currentMillis = millis(); //Save the value of time before the loop
/*

  • This if statement was needed to read the values of the current temperature and re-write the values of each
  • “temp_read_Delay” value(can be changed as testing goes).
  • That will directly affect the PID control. 500ms was chosen as the start value(Based on the MAX6675 Datasheet).
    */
    if(currentMillis - previousMillis >= temp_read_Delay){
    previousMillis += temp_read_Delay; //Increase the previous time for next loop.
    real_temperature = thermocouple.readFarenheit(); //Acquiring the real temperature in Farenheit degrees.

PID_error = setpoint - real_temperature; //Finding the current PID ERROR.

PID_p = kp * PID_error; //Calculate the P(proportional) value
PID_i = PID_i + (ki * PID_error); //Calculate the I(integral) value
if(PID_error > 30) //Integral constant must have an impact only on errors below 30ºF.
{PID_i = 0;}

timePrev = Time; //The previous time is stored before the actual time read
Time = millis(); //Actual time read
elapsedTime = (Time - timePrev) / 1000; //Must be divided by 1000 to convert from seconds to miliseconds.
Serial.println(elapsedTime);
PID_d = kd*((PID_error - previous_error)/elapsedTime); //Calculate the D value
PID_value = PID_p + PID_i + PID_d; //Calculate total PID value

//Defining firing delay range between 0 and 7500.
if(PID_value < 0)
{ PID_value = 0; }
if(PID_value > 7500)
{ PID_value = 7500; }

//Printe the values on the LCD
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Set: ");
lcd.setCursor(5,0);
lcd.print(setpoint);
lcd.setCursor(0,1);
lcd.print("Temp: ");
lcd.setCursor(6,1);
lcd.print(real_temperature);

lcd.setCursor(9,0);
lcd.print(PID_value);
lcd.setCursor(11,1);
lcd.print(maximum_firing_delay - PID_value);

previous_error = PID_error; //Remember to store the previous error.
}

//If the zero cross interruption was detected we create the 100us firing pulse
if (zero_cross_detected)
{
delayMicroseconds(maximum_firing_delay - PID_value); //This delay controls the power
digitalWrite(firing_pin,HIGH);
delayMicroseconds(100);
digitalWrite(firing_pin,LOW);
zero_cross_detected = false;
}

}//VOID LOOP FINISHES.

//Interruption routines
//-----------------------------------------------------------------------------

ISR(PCINT0_vect)
{
///////////////////////////////////////Input from optocoupler
if(PINB & B00010000){ //We make an AND with the state register, We verify if pin D11 is HIGH???
if(last_CH1_state == 0){ //If the last state was 0, then we have a state change…
zero_cross_detected = true; //We have detected a state change! We need both falling and rising edges
}
}
else if(last_CH1_state == 1){ //If pin 8 is LOW and the last state was HIGH then we have a state change
zero_cross_detected = true; //We haev detected a state change! We need both falling and rising edges.
last_CH1_state = 0; //Store the current state into the last state for the next loop
}
}

// void testing()
// {
// if (digitalRead(zero_cross) == HIGH)
// {
// if(last_CH1_state == 0)
// { //If the last state was 0, thenx we have a state change.
// zero_cross_detected = true; //Detected a state change.
// }
// }
// else if(last_CH1_state == 1)
// { //If pin 8 is LOW and the last state was HIGH then we have a state change.
// zero_cross_detected = true; //Detected a state change. Need both falling and rising edges.
// last_CH1_state = 0; //Store the current state into the last state for the next loop.
// }
// }

void setPointControl () {
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();

// If interrupts come faster than 5ms, assume it’s a bounce and ignore
if (interruptTime - lastInterruptTime > 5) {
// identify on the direction on wich the nob is being rotated.
if (digitalRead(PinDT) == LOW)
{
// if clockwise increase one
setpoint = setpoint + 5 ;
}
else {
// if counter-clockwise decrease one
setpoint = setpoint - 5 ;
}

// Restrict value from 0 to +500
virtualPosition = min(500, max(0, virtualPosition));
}
// Keep track of when the last interrupt time (no more than every 5ms)
// This is needed in order to count slowly and also in order to prevent noises
lastInterruptTime = interruptTime;
}

is it a AC phase cutting code? I have some here

and here

Hello Juraj, I am working on a phase angle control for heat temperature I was able to find a video tutorial on youtube and I jus tmade some changes on the code, but on the video the arduino uno was used and not the mega, and I believe the main problem I am facing is to correctly assign the ports and pins to the interrupt, if you don't mind helping me utilizing the code I have that would be amazing.

By using a hardware timer,you won't need the delayMicroseconds() - see: Arduino Playground - ACPhaseControl

All variables accessed in both ISR and non-ISR code must be declared volatile.

You don't need to use 'int' variables for 'firing_pin' and 'last_CH1_state'. Neither need to hold values greater than 255 nor negative values. Use an 8-bit unsigned type (i.e. uint8_t).

by utilizing a interrupt on digital pin 11 on the arduino mega 2560

int zero_cross = 11;

PCICR |= (1 << PCIE0);                                
PCMSK0 |= (1 << PCINT5);  //Set pin 11 

ISR(PCINT0_vect)
{
  if(PINB & B00010000)
  {           
    if(last_CH1_state == 0)
    {     
      zero_cross_detected = true; 
    }
  }
  else if(last_CH1_state == 1)
    {       
    zero_cross_detected = true;   
    last_CH1_state = 0;           
    }
}

if(PINB & B00010000) is reading PB4 (pin 10). Pin 11 is PB5. Bit positions are zero indexed.

Hello cattledog,
I just updated the code and it still seems like it is not finding the zero cross.
Thanks for the response and it is true what you said therefore you found one of my mistakes but there is still some more. :confused:

LucasVasconcelos:
Hello Juraj, I am working on a phase angle control for heat temperature I was able to find a video tutorial on youtube and I jus tmade some changes on the code, but on the video the arduino uno was used and not the mega, and I believe the main problem I am facing is to correctly assign the ports and pins to the interrupt, if you don't mind helping me utilizing the code I have that would be amazing.

attachInterrupt works on Mega too. you only need

void zeroCrossing() {
// if you get here zero crossing happened
 zero_cross_detected = true; 
}

void setup() {
 attachInterrupt(digitalPinToInterrupt(zcPin), zeroCrossing, RISING); // or FALLING
...

CHANGE would fire twice

attachInterrupt works on Mega too. you only need in setup

External interrupt pins on Mega

Mega, Mega2560, MegaADK
2, 3, 18, 19, 20, 21