Triac, zero cross, AC dimmer via interrupts

potros
Posts: 3
Joined: Sat Nov 14, 2020 9:58 am

Triac, zero cross, AC dimmer via interrupts

Postby potros » Sat Nov 14, 2020 10:34 am

Hi to all!

Im looking for any tip as im getting crazy.

Im trying to create a stable AC phase control, and to do it I use a interrupt on rising linked with the zero cross detection, and set up a timer, to delay the triac gate activation. Then, change the timer delay to maintain the high pulse for certain minimum time.

It works, but ocassionally it fails, and the light flicker, becasue the triac is triggered earlier that it should be.

Attached an image where I catched the fail. The right timer delay is 6300uS, the delay for the high state of the trigger pin to the triac is 200uS. The same behaviour with different timer loads.
20201114_105634.jpg
20201114_105634.jpg (67.11 KiB) Viewed 3970 times

Code is this. It include another hardware timer, to generate a interrupt each second. The problem persist even if I deactivate this "each second" interruption:

Code: Select all

//////////////// Pinout ////////////////
static const uint8_t Pin_BACS                 = 33;     // Pin,  ZeroCross
static const uint8_t Pin_PWMBACSESP           = 12;     // Pin, Triac Gate


//////////////// Interrupcion Tiempo SEGUNDOS //////////////// 
portMUX_TYPE timer0Mux = portMUX_INITIALIZER_UNLOCKED;
volatile int interruptCounter;
int totalInterruptCounter;
hw_timer_t * timer0 = NULL;

//////////////// Interrupcion Tiempo PASO POR CERO //////////////// 
portMUX_TYPE timer1Mux = portMUX_INITIALIZER_UNLOCKED;
hw_timer_t * timer1 = NULL;

//////////////// Paso Por Cero - AC  ////////////////
portMUX_TYPE muxBACS = portMUX_INITIALIZER_UNLOCKED;
const uint16_t DelayTriacMin = 1200;            // Tiempo min para disparo triac (max potencia)
const uint16_t DelayTriacMax = 6200;            // Tiempo max para disparo triac (min potencia)
volatile uint16_t DelayTriac;                   // Delay disparo triac (cant be float)
volatile bool FlagTriac=false;                         // 

uint8_t segundos=0;

// Interrupcion Zero Cross detection
void IRAM_ATTR int_BACS() {
  portENTER_CRITICAL_ISR(&muxBACS);
  	detachInterrupt(digitalPinToInterrupt(Pin_BACS));               // Desactivamos la interrupción (esta misma) de paso por cero
  	timerAlarmWrite(timer1, DelayTriac, false);       // El delay en el temporizador depende de la potencia que queramos en la bomba
  	timerRestart(timer1);
	timerAlarmEnable(timer1);
  portEXIT_CRITICAL_ISR(&muxBACS);
}

// Interrupción TIMER, cada SEGUNDO
void IRAM_ATTR onTimer() {
  portENTER_CRITICAL_ISR(&timer0Mux);
    interruptCounter++;
  portEXIT_CRITICAL_ISR(&timer0Mux);
}

// Interrupción TIMER, para el 
void IRAM_ATTR onTimerPASOCERO() {
  portENTER_CRITICAL_ISR(&timer1Mux);
  	if (!FlagTriac){
    		digitalWrite(Pin_PWMBACSESP, HIGH);
    		FlagTriac = true;
    		timerAlarmWrite(timer1, 200, false);       // El delay en el temporizador depende de la potencia que queramos en la bomba
    		timerRestart(timer1);
    		timerAlarmEnable(timer1);
  	}else{
    		digitalWrite(Pin_PWMBACSESP, LOW);
    		attachInterrupt(digitalPinToInterrupt(Pin_BACS), int_BACS, RISING);  // Activamos la interrupción captura paso por cero 220AC
    		FlagTriac = false;
	 }
  portEXIT_CRITICAL_ISR(&timer1Mux);
}


void setup() {
  delay(10);

  // Configura Pines, entrada/salida/ADC
  pinMode(Pin_BACS, INPUT_PULLUP);		// In, Zero Cross detection
  pinMode(Pin_PWMBACSESP, OUTPUT);		// Out, Triac Gate


  //////////// INTERRUPCIONES //////////// 
  // Configuramos el La interrupción del Timer de los SEGUNDOS
  timer0 = timerBegin(0, 80, true);                         // Prescaler
  timerAttachInterrupt(timer0, &onTimer, true);             // Función a llamar en la interrupción
  timerAlarmWrite(timer0, 1000000, true);                   // cada 1sg
  timerAlarmEnable(timer0);                                 // Activamos la interrupción

  // Configuramos el La interrupción del Timer del PASO POR CERO
  DelayTriac = DelayTriacMax;
  timer1 = timerBegin(1, 80, true);                         // Prescaler
  timerAttachInterrupt(timer1, &onTimerPASOCERO, true);     // Función a llamar en la interrupción
  timerAlarmWrite(timer1, DelayTriac, false);            // El valor inicial del disparo del triac

  // Enlazamos las interrupciones a los pines  
    attachInterrupt(digitalPinToInterrupt(Pin_BACS), int_BACS, RISING);               // Interrupción captura paso por cero 220AC 
}


void loop() {  
  if (DelayTriac<1200){
    DelayTriac=8200;
  }

  /////////// Interrupción cada segundo ///////////
  if (interruptCounter > 0) {   // Solo lo ejecutamos si el timer ha desbordado
    DelayTriac=DelayTriac - 100;
    timerAlarmWrite(timer1, DelayTriac, false);       // El delay en el temporizador depende de la potencia que queramos en la bomba
    
    portENTER_CRITICAL(&timer0Mux);
    interruptCounter--;
    segundos++;
    portEXIT_CRITICAL(&timer0Mux);

    if (segundos >= 2){
      segundos = 0;
    }
  }
}
Any tip will be welcome.

Thanks in advance.

Kind regards

potros
Posts: 3
Joined: Sat Nov 14, 2020 9:58 am

Re: Triac, zero cross, AC dimmer via interrupts

Postby potros » Mon Nov 16, 2020 7:13 pm

Hi there!

Well.. seems to be solved after many hours. A brief explanation. Next picture shows a pulse on external interrupt on pin, that was defined like:

Code: Select all

attachInterrupt(digitalPinToInterrupt(Pin_BACS), int_BACS, RISING);
IMG-20201113-WA0012.jpg
IMG-20201113-WA0012.jpg (41.99 KiB) Viewed 3869 times
So, it detects it twice... Not sure if this can be justified by an undefined pin state/noise, or its becasue the RISING property is not correctly defined and instead CHANGE is compiled.

In any case, what I was not aware about the interruption stack, that it keeps this second (undesired) interruption to be attended later, (even if the interrupt was detached previusly) when the interruption is attached to the pin again.

So, as this "old" interruption is handeled once I attached the ISR again to the pin, it triggers inmediatly the timer interruption

Im not sure what is more correct, if clear the flag of the interruption or just use

Code: Select all

gpio_intr_disable(GPIO_NUM_33);
as I have used right now.

Any advice/comment?

Thanks in advance.

JEREMIARG
Posts: 1
Joined: Mon Mar 27, 2023 4:57 pm

Re: Triac, zero cross, AC dimmer via interrupts

Postby JEREMIARG » Mon Mar 27, 2023 5:13 pm

In my case I built my zero crossing detector, it happened to me that there were rising edges that were not detected or that the interrupt was executed during the falling edge.
It worked for me to make a debounce routine in the interrupt routine so that this code is not executed twice in a row.


im just did a toogle rutine in GP_2_pin when zero cross is detected to see it in the osciloscope.
  1.  
  2. //configurations in setup rutine  
  3.     pinMode(ZERO_CROSSING_DETECTOR_GPIO, INPUT_PULLUP);
  4.     attachInterrupt(digitalPinToInterrupt(ZERO_CROSSING_DETECTOR_GPIO), zero_crossing_detector, RISING);
  5.  
  6. bool aux = false;
  7. long aux2;
  8.  
  9. //external interrupt function attached
  10. void IRAM_ATTR zero_crossing_detector(void){
  11.    if((millis() - aux2) > 5){ //5ms of debounce
  12.      if(aux == true){
  13.       digitalWrite(GP_2_pin, LOW);      
  14.       aux = false;
  15.      }
  16.      else{
  17.       digitalWrite(GP_2_pin, HIGH);
  18.       aux=true;
  19.      }
  20.    }
  21.    aux2 = millis();
  22. }
I hope this helps :)

Who is online

Users browsing this forum: No registered users and 54 guests