Pulse generation in an interrupt routine

Hi.

I have a programming question. My Arduino Uno is getting an input pulse every 8.3ms (int0, pin2). When it gets that interrupt, I want to do two things. One is to read a temperature from a MAX31855 thermocouple device (SPI) and then generate a pulse of a specific width that will be passed into the Arduino. The time it takes to read from the thermocouple is repeatable enough so it is OK to occur before the pulse is generated. (The pulse needed to lag the interrupt a bit anyway.)

Delays are no-nos in interrupts but I did read that delayMicroseconds() is OK in interrupt routines. Is this the best way to generate the pulse?

Thanks.

Not sure what you’re trying to do here… The way I read it you’re generating a clock pulse that’s getting read back in by the same chip. If that’s the case, you don’t need an ISR. A simple state machine design and a processing loop, or a timer interrupt would probably do the job better. You could also use the SPI read complete ISR and a software buffer if your goal is to read from the thermocouple as quickly as possible.

You are right that delays are a bad idea inside interrupts. The technical reason is that if your interrupt could be raised again before the servicing routine is complete. If that happens, your processor spends 100% of its cycle time processing interrupts, and has no time left for your application code. (Ok, so there’s more to it than that, and it depends on a lot of factors, but that’s a discussion for another day.)

I was just guessing about what you’re doing, so my information may be off a bit. What device is sending the pulse, and what device is reading the pulse? (In other words: Are they the same device?) What is the end goal here, to read data as quickly as possible, or to read at regular intervals?

I agree, more info would be useful. You can use a delay() in the ISR, a lot depends on what the background processing is trying to do. If it’s sitting idle, then a delay is OK. If it’s trying to get something done in a fixed amount of time … then you need to be careful. How long is this “pulse of a specific width” ? Presumably it’s less than 8.3 ms. How accurate must the PW be ? How much is the Uno doing besides what you’ve mentioned.

One “slick” way would be to do your SPI read and then set output pin high/low. Then initialize a timer with the proper PW value and return from the ISR. Set the timer to trigger another interrupt when it goes off and use that ISR to reset the output pin. Very little MCU cycles are needed to do this but you do have to go off the Arduino IDE playground to do it and you need to be careful about what timer you choose. The Uno has 3 timers and the IDE uses each to varying degrees to implement higher level functions like millis(), delay(), etc, etc.

I may be asking too much of the little Arduino but I am making a heater control that has more fidelity than just bang-bang on-off. I have a zero-crossing detector that creates a pulse every time the AC crosses zero (8.3ms). I want to use that edge to then have the Arduino generate a pulse to trigger a triac. The triac pulse will determine the power to the heater. Something just short of 8.3ms is full on.

I was preferring to turn things on at or near the AC zero-crossing so as to not create big currents through the triac but I may have to abandon that because of the Arduino processing times. In 8ms but not necessarily in this order, the Arduino needs to receive a pulse value from the PC, generate a pulse of that value, read a temperature value from the thermocouple, and pass that back to the PC.

I did a quick experiment and it seems that reading the MAX31855 takes way longer than 8ms. Even at 56700 baud, I’m not sure it can report back the temp in enough time either.

I could go with a half wave rectifier for the AC and get a pulse every 16ms to give everything more time. Even dividing that in half again to 32ms wouldn’t be the end of the world. Worst case, I may just have to go with on-off or with a PWM drive for the triac that isn’t synced to the zero-crossing.

Another option is to just have the interrupt routine create the proper triac pulse. A counter can be used to determine how many times the ISR was run. At some set number, say equivalent to every one or two seconds, just deal with the short time that the heater is turned off (a small part of a second) disable the ISR, do the temp read, report it, and get a new pulse value. Then go back to dealing sending the right pulse width to the heater in the ISR.

So you’re making a dimmer to control the heater. One thing I think you should do is read the following link. You’ve mentioned “preferring to turn things on at or near the AC zero-crossing so as to not create big currents through the triac”, the problem being that triacs are used in leading edge dimmers, not the trailing edge I think you want.

http://sound.westhost.com/lamps/dimmers.html

While I think it odd that the thermocouple can’t be read in 8.3 ms (I’ve not looked at the datasheet), given the time constants involved in most heaters you might get away with a cycle skipping control scheme. That would follow the general principle that you don’t chop the AC waveform to change it’s duty cycle on a per period (or half period) basis

but rather allow a certain number of full periods in every N periods. For example you might turn the triac on to allow 100 full periods (16.666 ms) every 1.6666 sec. That’s 100 % on. For 50% on you might control via triac so that only 50 full periods of AC are on every 1.6666 sec. The least amount of power would be 1%, where you’d allow 16.6 ms of on time every 1.6666 secs.

Obviously the drawback is the control action happens at the slower rate (1.66 secs) but if the time constant of the oven is already some amount of seconds, does that really matter ? The good part is you have lot’s of time to decide what the next control action should be and the triac can be used to switch on power just after the zero crossing so as to avoid large surge currents.

EDIT : seems you were thinking along similar lines as I was typing this post. :mrgreen: