230v AC Triac Dimmer. Works, but flickers on "fade-up"

I am using a H11A4 and a mos3022 opto-isolators for a triac dimmer setup.

Right now I have it fading a light-bulb up and down.

And it works as expected, the only problem is that most of the time there is a flicker on the fade-up (but not always).
It occurs at random places in the fade-up phase, and I have never noticed it on the fade-down.

If I set it dimmed to a specific value, it doesn't flicker, so I don't think it is AC noise?

Any suggestion?

This is the code I'm currently using:

#define DETECT 0  // interrupt 0 = pin 2
#define GATE   8  // triac gate
#define PULSE  8  // triac gate trigger pulse width (timer counts)

#define MAX_LEVEL 550 // with 256 prescale, 625 will be 10ms (one half-wave on 50 hz)


volatile uint16_t dim_level = MAX_LEVEL; // start low (long pause)
boolean increment_pause = false;         // true = fade-down, false = fade-up
boolean start_low_fix = true;            // temp fix, because arduino power-on before main voltage is applied.



void setup() 
{
  attachInterrupt(DETECT, zero_cross_detect, RISING);   // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
  
  pinMode(GATE, OUTPUT);   // triac gate control
  digitalWrite(GATE, LOW); // turn off triac gate
  
 /* Timer Stuff */
 cli();
  TCCR1A = 0x00;  // clear
  TCCR1B = 0x00;  // clear
  
  TIMSK1 = 0x03;          // Enable the Output Compare A and Overflow interrupt
  OCR1A  = dim_level;     // starting low
 sei();
}



ISR(TIMER1_OVF_vect) {
  TCCR1B = 0x00;           // stop timer
  digitalWrite(GATE, LOW); // turn off triac gate
}

ISR(TIMER1_COMPA_vect) {
  digitalWrite(GATE, HIGH);  // set triac gate to high
  TCNT1 = 65536-PULSE;       // trigger pulse width
}

void zero_cross_detect() {
   start_low_fix = false;
   
   TCNT1  = 0;          //reset timer - count from zero
   TCCR1B = 0x04;       //start timer with divide by 256 input
}



void loop()
{
    if (start_low_fix)
      return;
    if(increment_pause) {
      dim_level++;
      if (dim_level > MAX_LEVEL)
          increment_pause = false;
    }
    else {
      dim_level--;
      if (dim_level < 50)
          increment_pause = true;
    }
    
    OCR1A = dim_level;     //set the compare register brightness desired.
      
    delay(10);
}

first of all why dont you use analogWrite() ? i had built same thing(dimmer) with my handmade optoisolator( led+ldr based) it worked fine.
Moreover flickering may be caused by aberrant triggering of triac.

Why would analogWrite make a difference? All I do is turning the MOC led on and off.

But what is causing the aberrant triggering of the triac?, it's only on fade-ups (from what i can see)

Figured it out.

It was caused by changing the timer comparator value while timer was active. :stuck_out_tongue:

Working code:

#define DETECT 0  // interrupt 0 = pin 2
#define GATE   8  // triac gate

#define PULSE  8  // triac gate trigger pulse width (timer counts)


// Values determines DARKNESS not brightness.
#define MAX_LEVEL 600 // with 256 prescale, 625 will be 10ms (one half-wave on 50 hz)
#define MIN_LEVEL 50

#define FADE_DELAY 10


volatile uint16_t dim_level = MAX_LEVEL;  // start low (long pause)
boolean increment_pause  = false;         // true = fade-down, false = fade-up
boolean start_low_fix    = true;          // temp fix, because arduino power-on before main voltage is applied.



void setup() 
{
  attachInterrupt(DETECT, zero_cross_detect, RISING);   // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
  
  //moc3022 setup:
  pinMode(GATE, OUTPUT);   // triac gate control
  digitalWrite(GATE, LOW); // turn off triac gate

  
 /* Timer Stuff */
 cli();
  TCCR1A = 0x00;  // clear
  TCCR1B = 0x00;  // clear
  
  TIMSK1 = 0x03;          // Enable the Output Compare A and Overflow interrupt
  OCR1A  = dim_level;     // starting with long pause
 sei();
}



ISR(TIMER1_OVF_vect) {
  TCCR1B = 0x00;           // stop timer
  digitalWrite(GATE, LOW); // turn off triac gate
}

ISR(TIMER1_COMPA_vect) {
  digitalWrite(GATE, HIGH);  // set triac gate to high
  TCNT1 = 65536-PULSE;       // trigger pulse width
}

void zero_cross_detect() {
   start_low_fix = false;
   
   TCNT1  = 0;          // reset timer - count from zero
   OCR1A  = dim_level;
   TCCR1B = 0x04;       // start timer with 256 prescale
}



void loop()
{
    if (start_low_fix) // waiting for first ZC
      return;
      
      if(increment_pause) { // Fade-down
        dim_level++;
        if (dim_level > MAX_LEVEL)
            increment_pause = false;
      }
      else { // Fade-up
        dim_level--;
        if (dim_level < MIN_LEVEL)
            increment_pause = true;
      }   
    delay(FADE_DELAY);
}

Today i tried adding a second triac, but now i am getting flickers again.

The code is a bit different now:

#define DETECT  0  // interrupt 0 = pin 2
#define GATE_1  8  // triac gate
#define GATE_2  9  // second triac gate



/***********************************
  This is used on 230 volt 50hz ac
***********************************/

// Values determines the level of dimming
#define MAX_LEVEL 125
#define MIN_LEVEL 5

#define FADE_DELAY 15


volatile uint16_t dim_level, light_1 = MAX_LEVEL , light_2 = MIN_LEVEL;


boolean fade_down_1        = false;         // true = fade-down, false = fade-up
boolean fade_down_2        = true;          // true = fade-down, false = fade-up
boolean start_low_fix      = true;          // temp fix, because arduino power-on before main voltage is applied.


void setup() 
{  
  attachInterrupt(DETECT, zero_cross_detect, RISING);   // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
  
  //moc3022 setup:
  pinMode(GATE_1, OUTPUT);   // triac gate control
  pinMode(GATE_2, OUTPUT);   // triac gate control
  digitalWrite(GATE_1, LOW); // turn off triac gate
  digitalWrite(GATE_2, LOW); // turn off triac gate
  
  
 /* Timer Stuff */
 cli();
  TCCR1A = 0x00;  // clear
  TCCR1B = 0x00;  // clear

  TIMSK1 = 0x02;  // Enable the Output Compare A
  OCR1A  = 1250;  // = 128 steps on 50hz with no timer prescale
 sei();
}


ISR(TIMER1_COMPA_vect) {
  dim_level++;
  if (light_1 == dim_level) // fire first triac
      digitalWrite(GATE_1, HIGH);
  if (light_2 == dim_level) // fire second triac
      digitalWrite(GATE_2, HIGH);
   if (dim_level == MAX_LEVEL + 1) { // Shut off gate before ZC
      digitalWrite(GATE_1, LOW);     // set triac gate to high 
      digitalWrite(GATE_2, LOW);     // turn off second triac gate
      TCCR1B = 0;
   }
}

void zero_cross_detect() {
   TCNT1  = 0;          // reset timer - count from zero
   TCCR1B = 0x09;       // start timer with no prescale and reset tcnt1 on compare
   
   start_low_fix = false;
   dim_level = 0;
}


void loop()
{
    if (start_low_fix) // waiting for first ZC
      return;
      
      // Fade triac 1:
      if(fade_down_1) { // Fade-down
        light_1++;
        if (light_1 == MAX_LEVEL)
            fade_down_1 = false;
      }
      else { // Fade-up
        light_1--;
        if (light_1 == MIN_LEVEL)
            fade_down_1 = true;
      }   
      
      // Fade triac 2:
      if(fade_down_2) { // Fade-down
        light_2++;
        if (light_2 == MAX_LEVEL)
            fade_down_2 = false;
      }
      else { // Fade-up
        light_2--;
        if (light_2 == MIN_LEVEL)
            fade_down_2 = true;
      } 
    
    delay(FADE_DELAY);
}

I have no idea what's causing it :-\

Again, i only se flicker on fade-up

Any improvements yet? I'm taking your same approach (or similtar) for dimming. You can find the code here

http://arduino.cc/forum/index.php/topic,106900.0.html

I get a random flickering while dimming (haven't tried steady values yet) and a lot of noise close to the zero cross (that happens even with steady values, I tried that). If I get close to maximum/minimum levels I might get full power no matter where I am. So, if you get any solutions yet just let me know, please!!

I get a random flickering while dimming (haven't tried steady values yet) and a lot of noise close to the zero cross (that happens even with steady values, I tried that). If I get close to maximum/minimum levels I might get full power no matter where I am. So, if you get any solutions yet just let me know, please!!

The zero-crossing detection could be your problem, depending on how you are doing it. It's not easy to directly detect the exact zero crossing. If you are running very-dim (firing near the end of the half cycle) and something goes wrong where you trigger late, or if you get multiple triggers, you can end-up triggering at the beginning of the next cycle. The opposite can happen if you are running bright and you trigger too soon (at the end of the previous half-cycle), if the trigger doesn't hold long enough.

The good news is, you don't need to detect the actual zero-crossing... You can use a different threshold and compensate. When I did this a million years ago with a different microcontroller, I was getting the "zero crossing" signal through a transformer & full-wave rectifer. So with the voltage drop across the rectifier, I was probably getting a rising-edge trigger pulse at about 20 degress. The transformer may have introduced a phase-lag too.

Since I didn't really know the exact timing difference between my threshold detection and the zero crossing, I just did some experiments to find the timing compensation that worked best.

If I'm remembering correctly I used a full-wave rectifier, but that's not really necessary. If you find the rising-edge zero-crossing, you know when falling-edge zero-crossing is comming.

If you are having trouble at the end-points (nearly full-dim or nearly full-bright) you can try making your ramp non-linear. For example, any brightness level less than 10 could be fully-off, and/or any brightness level greater than 250 could be fully-on. You'd have to experiment to see what you can get-away with without noticing the "jump", but I'm pretty sure you can get-away with throwing-away a few counts. And at full-brightness (i.e 255), there's no need to trigger every cycle, you can just trigger continuously (with DC into the optoisolator).

groundcontact:
Any improvements yet? I'm taking your same approach (or similtar) for dimming. You can find the code here

http://arduino.cc/forum/index.php/topic,106900.0.html

I get a random flickering while dimming (haven't tried steady values yet) and a lot of noise close to the zero cross (that happens even with steady values, I tried that). If I get close to maximum/minimum levels I might get full power no matter where I am. So, if you get any solutions yet just let me know, please!!

yes, i have it working perfectly now.

The problem was; I have two incremental variables(dim_level and light_1) so the "if" check in ISR would occasionally miss.(because of interrupt)
Fixed it by just checking for the occasionally extra count like this:

...
  if (light[0] == dim_level || light[0] == dim_level+1) {        // fire first triac
      digitalWrite(TRIAC_GATE_1, HIGH);
  }
  if (light[1] == dim_level || light[1] == dim_level+1) {        // fire second triac
      digitalWrite(TRIAC_GATE_2, HIGH);
...
  }

I just came in to tell you I had figured out the solution and share but I see you already did! Good news. This can happen anytime (and with this kind of logic) when you have two opposite ramps working at different rates. The counter inside the Timer1 interrupt checks the status of the variable assigned to dim level. If anytime in between updates of Timer1 counter the value of dim level rises one step, and you're just one step away in the values of both counters, you miss the Triac triggering. I'm going to use a different approach in the code and share it with you when I finish it. Thanks.

DVDdoug, thank you too! I need to get more in depth with the problems happening very close to the zero-cross detection. My solution right now is just as you said. I have put a safety "buffer" consisting in a 10% of number of total steps before and after the zero-cross that equals the state to max and min values. That works fine, but I think it's not the solution. With other logics the dimmer, with same electronics, worked just fine with no problems at all at the zero cross, no matter how close you where to the it. It has to be the logic!

So here it is, as promissed. Works smooth and fine. Still having problems in step values very close to the zero crossing (10%-8%of Max. dim value) but I'll dive into it later.

//This code dims up and down a lamp in a period of time

//initialize and declare variables

#include <TimerOne.h>	// Avaiable from http://www.arduino.cc/playground/Code/Timer1
#define FREQ 50 	// 50Hz power in these parts
#define AC1_Pin 10	// Output pin (TRIAC triggering)

const int DimMax = 127;                     // Max value in dimming scale
const int DimMin = 0;                       // Min value in dimming scale
int volatile Dim1 = 0;                 // Present dimming value
unsigned long int halfSineTime = 1000000 / (2 * FREQ);//The Timerone PWM period, 50Hz = 10000 uS
int rampInterval = halfSineTime/DimMax;                // Time of one step
int volatile rampCounter = DimMax;        //Down counter with present step
int rampPeriod = 5;// For the fading, time in seconds that takes for the half way-up dimming (the same downwards)
int wait = rampPeriod*1000/DimMax;//Delay between increments of dimming
int buffer = 0.1*DimMax;// allows a safety buffer of steps in which results are dirty
int flagCrossingRamp = 0;  //A flag raised a step before a crossing of dimming and step ramps happens
int volatile Dim_Interrupt;  //Stores the value of Dim1 before a change in Dim1 occurrs

void setup ()

{   //set the mode of the pins…
pinMode(AC1_Pin, OUTPUT);
attachInterrupt(0, light, FALLING );    //Zero-crossing detector
Timer1.initialize(halfSineTime);
Timer1.disablePwm(9);
Timer1.disablePwm(10);

} 

void loop ()
{//The main loop just raises and lowers the value of Dim1 in the time specified in wait variable
   int x = 1;
   for (Dim1 = DimMin+buffer; Dim1 > DimMin+buffer-1; Dim1 = Dim1 + x){
      if (Dim1 == (DimMax)) x = -1;             // switch direction at peak
      delay(wait);
   }
 
}//close void loop

void nowIsTheTime ()
{
  rampCounter = rampCounter--;//decrease the counter
    if ((rampCounter == Dim1)||((flagCrossingRamp ==1)&&(Dim1-Dim_Interrupt == 1))//In the interrupt we fire the TRIAC only when the actual 
    {                                                                             //step (counting backwards) equals the value of Dim1
       digitalWrite(AC1_Pin,HIGH);                                                //or if Dim1 slips through inadvertently ;)
       flagCrossingRamp = 0;
    }
    else {
 digitalWrite(AC1_Pin,LOW);//in the next step (and the rest) AC1_Pin remains low
    }
    if (Dim1 == (rampCounter - 1)) flagCrossingRamp = 1; 
    Dim_Interrupt = Dim1;

      
}

void light() //This function is triggerd at zero-cross
{
    Timer1.restart();//we set timer1 origin
    rampCounter = DimMax;//initialize the steps counter
    if (Dim1 < DimMin+buffer) 
    {                  //Turn TRIAC completely OFF if Dim1 is 0 or very close to 0
      digitalWrite(AC1_Pin, LOW);
    }
    else if (Dim1 > DimMax-1) 
    {                //Turn TRIAC completely ON if Dim1 is DimMax
      digitalWrite(AC1_Pin, HIGH);
    }
   else  
   {      
   //Dimming part, if Dim1 is not 0 and not DimMax
    
    Timer1.attachInterrupt(nowIsTheTime, rampInterval); //If we have a value to dim then we trigger a timer interruption
  }
}

This might solve all your "code" fixes for "Bad" hardware...
http://www.electronicspoint.com/zero-crossing-triac-t25458.html
Seems to me I used some of these things for an AC solenoid irrigation clock once... as an optional board.
The devices are common and inexpensive.

Bob

I've been fortunate enough to have good experience with phase control using microcontrollers.

Although I don't use AVR micro controllers in projects, the same general rules apply, so here are a couple of pointers for the uninitiated:

Hardware Component Selection (aimed at most hobbyist projects):
H11AA1's are a great choice for the zero cross detection. The emitter is bidirectional, which reduces the board component count.
MOC302x - series opto's are a great for the output. They are opto-triacs very well suited to triggering the gate of a larger triac.
I've found BTA20 and BTA40 series triacs to be hard-wearing, good for general use and difficult to damage. From memory they're also snubberless, which is great for inductive loads.

Hardware Safety Considerations:
MAKE SURE YOUR PCB TRACKS CAN HANDLE THE CURRENT YOU INTEND TO CONTROL. The current carrying capacity of a PCB track can be increased by leaving the solder mask off and bulking the track up with solder during assembly.
Place a fuse (resettable if possible) in series with the triac to protect against overcurrent.
Keep 5v and 240VAC areas on your board well separated. There's little risk of flash-over at 240VAC, but there's high risk of touching live areas when doing debugging and testing.
Place a "DANGER HIGH VOLTAGE" marking in a prominent position on the silk.
Mark high voltage connectors CLEARLY to reduce the risk of incorrect connections.
Make sure the resistors on the AC side are properly rated for the wattage they'll need to drive.
Make sure you stay within current limit specs of both optocouplers.
Pull down your firing optocoupler's emitter to ground to ensure the AC load is forced OFF if the microcontroller is malfunctioning, non operational, unprogrammed or otherwise not in control of the load. You don't want a motor spinning up while you program the microcontroller etc. etc. etc.

Hardware Design Considerations:
Make sure the output optocoupler delivers sufficient gate current for the triac to turn on.
Keep the capacitance of the zero-cross detection line and the output to the firing optocoupler as low as you possibly can - this will ensure that there's no detection and command delays that could spill over a zero-cross event.
Triac's have a holding current - keep this in mind when choosing a triac for your load impedance. Don't specify your triac holding current at the threshold. Factor in some operational margin to accommodate worst-case components in a batch - not all triacs in a series are the same. Manufacturing tolerances will cause them to differ slightly in their specifications.
Also keep in mind that the manufacturer's specs are at a given temperature. If your application's temperature differs significantly, things like current thresholds may be substantially lower or higher.

Firmware Design Considerations:
Do as little as possible in an interrupt handler. Keep them simple and to the point.

Remember: Triacs can be turned on anywhere during the AC cycle (except at the zero cross point exactly) but will only turn off when BOTH the following are true:
1.) The AC sine wive is at the zero cross.
2.) There is no gate current.
Unless BOTH the above conditions are fulfilled, the triac will stay on.

Don't turn the triac off too quickly after you've turned it on. Allow some time for the load current to reach the holding current threshold. For inductive loads, the current lag may require you to increase the the duration of the firing pulse to allow the current through the triac to ramp up beyond the holding threshold. This also implies that your minimum on-time (think minimum "dim" level or minimum motor speed) will be greater than the triac turn on time. Don't expect your phase control firmware to be able to "dim" down to 0%. Aim for 5% and simply turn off the triac completely beyond that. The same applies to low leading edge delays. Aim for 95% and simply turn the triac on manually at values higher than 95%. This will prevent zero-cross mismatch flickering.

If your leading edge delay plus your pulse duration exceeds half the period of the AC frequency, you will have gate current flowing in the triac across the zero cross and the triac will be fully on for at least the next half-cycle. This is the major cause of flashing, flickering and motor jittering. Falling edge detection works better than rising edge detection in mitigating this issue.

Make sure you understand your chosen timer well. Some microcontrollers have timer peripherals that only allow adjustment of counter, reload, overflow and prescalers when the timer is inactive. Others, like ARM Cortex based microcontrollers, can be updated on-the-fly. Make sure that timer updates are done atomically, otherwise your reload or counter updates may be interrupted by other parts of the system.

If you have more than one output to control, make sure your functions are re-entrant, as timer interrupts may cause a function to exit and resume later.

All the best

[quote author=qwerdy link=msg=835879 date=1340396766]
Figured it out.

It was caused by changing the timer comparator value while timer was active.  :p

Working code:
...

[/quote]

Hi!
I used that code and it works fantastic with all traditional non-LED bulbs. Is that dimmer supposed to work with new low-energy LED bulbs? Coz every single one of them flicker when on fade-up or fade-down. Few of them also make quiet, characteristic, high sound (alongside with flickering) like buzzing or squeaking.

kaslo:
Hi!
I used that code and it works fantastic with all traditional non-LED bulbs. Is that dimmer supposed to work with new low-energy LED bulbs? Coz every single one of them flicker when on fade-up or fade-down. Few of them also make quiet, characteristic, high sound (alongside with flickering) like buzzing or squeaking.

Exactly.

More to dimming AC Leds than meets the eye.