Alternative to Delay() within Interrupts?

Hi there!

I am completely new to this, so I’m not sure if there is an easy way forward.

Basically I am controlling an AC incandescent lamp with the help of a dimmer circuit that claims to be arduino compatible.

It works by triggering an interrupt which dims the bulb for a portion of the sine wave.

Unfortunately the code seems to require using the delayMicroseconds() function WITHIN the interrupt function. According to the spark documentation, this is a no-no.

Here is the code. It is unchanged from the testing sketch I was provided, except for the pins.


unsigned char AC_LOAD = D7;    // Output to Opto Triac pin
unsigned char dimming = 3;  // Dimming level (0-100)
unsigned char i;


void setup() {
  // put your setup code here, to run once:
  pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
  attachInterrupt(D3, zero_crosss_int, RISING);

  // Serial.begin(9600);

}

void zero_crosss_int()  // function to be fired at the zero crossing to dim the light
{
  // Firing angle calculation : 1 full 50Hz wave =1/50=20ms 
  // Every zerocrossing : (50Hz)-> 10ms (1/2 Cycle) For 60Hz (1/2 Cycle) => 8.33ms 
  // 10ms=10000us
  
  int dimtime = (100*dimming);    // For 60Hz =>65    
  delayMicroseconds(dimtime);    // Off cycle
  digitalWrite(AC_LOAD, HIGH);   // triac firing
  delayMicroseconds(10);         // triac On propogation delay (for 60Hz use 8.33)
  digitalWrite(AC_LOAD, LOW);    // triac Off
}
   
void loop() {
          // Serial.println(pulseIn(8, HIGH));
          
          for (i=5;i<85;i++)
          {
            dimming=i;
            delay(20);
          }
        
          for (i=85;i>5;i--)
          {
            dimming=i;
            delay(20);
          }
}

Hope somebody can shed some light on an alternative, or perhaps I am doing something else wrong.

Joel

You could use hardware timers (e.g. by use of @peekay123’s ingenious SparkIntervalTimer library) which you start inside your zero_cross_int() along with setting your pin HIGH and in a second interrupt triggered by this HW timer you set your pin LOW again and stop the timer again.

you could try using your code but eliminating the blocking code in your loop() function and just set a flag during your ISR:

something like this (untested):

unsigned char AC_LOAD = D7;    // Output to Opto Triac pin

byte dimming = 5;
byte increment = 1;
volatile bool flag = false;

void setup() 
{
  pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
  attachInterrupt(D3, myISR, RISING);
}

void myISR()
{
    flag = true;
}

void zero_crosss_int()  // function to be fired at the zero crossing to dim the light
{
  // Firing angle calculation : 1 full 50Hz wave =1/50=20ms 
  // Every zerocrossing : (50Hz)-> 10ms (1/2 Cycle) For 60Hz (1/2 Cycle) => 8.33ms 
  // 10ms=10000us
  
  int dimtime = (100*dimming);    // For 60Hz =>65    
  delayMicroseconds(dimtime);    // Off cycle
  digitalWrite(AC_LOAD, HIGH);   // triac firing
  delayMicroseconds(10);         // triac On propogation delay (for 60Hz use 8.33)
  digitalWrite(AC_LOAD, LOW);    // triac Off
}
   
void loop() 
{
    static unsigned long myTimer = 0;
    if (millis() - myTimer >= 20UL)
    {
        dimming += increment;
        if (dimming >= 85 || dimming <= 5)
        {
            increment *= -1;
        }
        myTimer += 20UL;
    }
    if (flag)
    {
        zero_crosss_int();
        flag = false;
    }
}

Hmm, how does this get rid of the line delayMicroseconds(dimtime); in the ISR?
And how exact could you control the iteration time of loop()?

For phase cut dimming you need an exact timing between the zero-cross and the moment to switch off your valve. I don’t think you can get the needed precision with loop() alone.

????

attachInterrupt(D3, myISR, RISING);

As in my edit above, it’s not only the time to detect the zero-cross, but to get the switch off time.

And I understood the OPs original question as: “How to get rid of the “long” delay inside the ISR.”

Sure it’s a delay of max 8ms (full brightness), but with an ISR triggering every 8.3ms this time could be put to better use for other things.

there is no delay in the ISR

still don’t understand your point, but that for the OP, i guess.

Yes but your ISR does not fire the triac in sync with the mains sine wave, which it has to do to be a phase cut dimmer!

I didn’t say I solved his problem, I showed him how to get the delay out of the ISR

At the cost of impairing the general idea of the project?

in your opinion?

but again I leave this for the OP to review.

@BulldogLowell, though your suggestion is generally a great one, it is not specifically applicable to the OP’s stated problem as @ScruffR pointed out. The timing issue is critical so decoupling the ISR from the triac firing code will introduce unacceptable jitter. The use of SparkIntervalTimer as suggested by @ScruffR is a viable solution IMO. When I finally get around to updating the library, there is actually a one-shot mode for the hardware timer so applications like these will be simpler to implement. :smile:

1 Like

@peekay123

Dude, I completely understand, is my point.

All I did was give the OP a little advice for his/her benefit only to be admonished.

As it is, there are many ways to solve a problem, and degrees of ‘right-ness’ will be determined by @ScruffR.

I get it. I’ll climb back under my rock now.

I don't think this is fair - right-ness is factual in this case, but since opinions about it differ, reality should show who's opinion matches up better.
And pointing out of the subjective divergence between your opinion and mine was not an admonishment but a statement of surprise, how this would bring the OP closer to a working solution.

I was prepared to wait for @supercosm's verdict, as you suggested and I'll be prepared to fully repent and walk in sack-cloth and ashes if he'd accept your code as valid solution which actually works and solves the OPs problem.

@ScruffR, @BulldogLowell, both of you are righteous for making a suggestion in the first place! You both took the time to think about a solution and proposed it so thank you for that! The beauty of open source is the ability to have a constructive discourse. My comments were based on experience. Ultimately, the OP will need to chose and test an approach that they are comfortable in coding and testing. After all, how many suggestions have we all made that an OP didn’t follow and still solved their own problem (or not)!

So dudes, I and hopefully mostly the OP should be glad to just have folks like you to help in the first place :wink:

3 Likes

Word

1 Like

Thanks for the responses everyone. I’m going to give @peekay123 's library a whirl.

Also I think we already closed the book on this, but we should be mindful of constructive criticism when somebody contributes a solution. I appreciate you taking the time to make a contribution, @BulldogLowell

Cheers

Hi Guys,

I have put some (potentially dodgy) code together.

It seems to fail the compile. Here is my code:

// This #include statement was automatically added by the Particle IDE.
#include "SparkIntervalTimer.h"

unsigned char AC_LOAD = D7;    // Output to Opto Triac pin
unsigned char dimming = 50;  // Dimming level (0-100)
unsigned char i;
//volatile int interrupt = false;
volatile unsigned long dimtime = 100*dimming;
int ctr == 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output

  IntervalTimer myOnTimer;
  IntervalTimer myOffTimer;
  
  while (D3==LOW)
  {
      delayMicroseconds(10);
  }
  if (D3==HIGH)
  {
      myOnTimer.begin(triacOn, dimming*100, uSec, TIMER2);
      delayMicroseconds(10)
      myOffTimer.begin(triacOff, dimming*100, uSec, TIMER3)
  }

  // Serial.begin(9600);

}

void triacOn()
{
    digitalWrite(AC_LOAD, HIGH);
}


void triacLow()
{
    digitalWrite(AC_LOAD,LOW);
}

void zero_crosss_int()  // function to be fired at the zero crossing to dim the light
{
  // Firing angle calculation : 1 full 50Hz wave =1/50=20ms 
  // Every zerocrossing : (50Hz)-> 10ms (1/2 Cycle) For 60Hz (1/2 Cycle) => 8.33ms 
  // 10ms=10000us
  
  int dimtime = (100*dimming);    // For 60Hz =>65    
  delayMicroseconds(dimtime);    // Off cycle
  digitalWrite(AC_LOAD, HIGH);   // triac firing
  delayMicroseconds(10);         // triac On propogation delay (for 60Hz use 8.33)
  digitalWrite(AC_LOAD, LOW);    // triac Off
}



void loop() {
    
        
        while (ctr>50){
            delay(1);
            ctr++;
        }
        
        myOnTimer.end();
        myOffTimer.end();
          
          // Serial.println(pulseIn(8, HIGH));
          
                     

}

And…

Here is the compile errors I am getting. Appreciating any guidance!

    In file included from SparkIntervalTimer.cpp:24:0:
SparkIntervalTimer.h:55:2: warning: #warning "CORE NEW" [-Wcpp]
 #warning "CORE NEW"
  ^
SparkIntervalTimer.cpp: In member function 'void IntervalTimer::interrupt_SIT(action)':
SparkIntervalTimer.cpp:360:15: warning: variable 'TIMx' set but not used [-Wunused-but-set-variable]
  TIM_TypeDef* TIMx;
               ^
SparkIntervalTimer.cpp: In member function 'IntervalTimer::start_SIT(unsigned short, bool)':
SparkIntervalTimer.cpp:276:23: warning: 'TIMx' may be used uninitialized in this function [-Wmaybe-uninitialized]
  TIM_Cmd(TIMx, ENABLE);
                       ^
SparkIntervalTimer.cpp: In member function 'IntervalTimer::stop_SIT()':
SparkIntervalTimer.cpp:346:18: warning: 'TIMx' may be used uninitialized in this function [-Wmaybe-uninitialized]
  TIM_DeInit(TIMx);
                  ^
SparkIntervalTimer.cpp: In member function 'IntervalTimer::resetPeriod_SIT(unsigned short, bool)':
SparkIntervalTimer.cpp:476:23: warning: 'TIMx' may be used uninitialized in this function [-Wmaybe-uninitialized]
  TIMx->PSC = prescaler;
                       ^
In file included from dimmer.cpp:2:0:
SparkIntervalTimer.h:55:2: warning: #warning "CORE NEW" [-Wcpp]
 #warning "CORE NEW"
  ^
dimmer.cpp:9:9: error: expected initializer before '==' token
 
         ^

dimmer.cpp: In function 'void setup()':
dimmer.cpp:28:7: error: expected ';' before 'myOffTimer'
   while (D3==LOW)
       ^

dimmer.cpp: In function 'void loop()':
dimmer.cpp:64:16: error: 'ctr' was not declared in this scope
   digitalWrite(AC_LOAD, LOW);    // triac Off
                ^

dimmer.cpp:69:9: error: 'myOnTimer' was not declared in this scope
 void loop() {
         ^

dimmer.cpp:70:9: error: 'myOffTimer' was not declared in this scope
     
         ^

make[2]: *** [../build/target/user/platform-0-ldimmer.o] Error 1
make[1]: *** [user] Error 2
make: *** [main] Error 2

Thanks so much
Joel

1 Like

@supercosm, you can ignore the warnings to start with and focus on the errors.
And you need to look at the line number rather than the code excerpt, since the excerpt is sometimes off by a few lines, due to preprocessor quirks.

e.g.

int ctr == 0; // this should be int crt = 0;
// and 
IntervalTimer myOnTimer; // these need to be global to be 
IntervalTimer myOffTimer; // accessible outside of setup()
// and 
while (D3==LOW) // this should be (digitalRead(D3) == LOW)
// and
if (D3==HIGH) // this should be (digitalRead(D3) == HIGH)
// and
delayMicroseconds(10) // here you are missing a semicolon ";"
// other errors are result of above mistakes

But I’d guess the whole logic won’t properly sync to the zero-cross. Maybe you reread my proposed aproach.

1 Like

Thanks! I’ll give it a crack.