Hi,
I'm testing the feasibility of using one digital waveform to both trigger an interrupt and encode a pulse duration. Waveform is as follows: high (1 ms), low(3 ms), high (1ms), low (1 sec). I connected this signal to both interrupt 0 (digital pin 2) and to an arbitrary input pin (digital pin 8). The interrupt handler runs pulseIn() on pin 8, looking for the signal to fall. So the initial 1 ms high triggers the interrupt, blocks further interrupts (by default if I understand that correctly), starts pulseIn(8, LOW), looking for the duration between the two high pulses. When I run the code it, for the most part, determines the duration correctly. It does not, however, seem to trigger on every waveform. I'm firing these high-low-high waveforms once per second (roughly). Arduino (Duemilanove) only responds to one in every 5 to 15 waveforms.
Here's the code:
int selectInputPin = 8;
volatile unsigned long selectInDuration;
void setup(){
Serial.begin(9600);
pinMode(selectInputPin, INPUT);
attachInterrupt(0, intrptHndlr, RISING);
}
void loop () {
//Do something useful here
noInterrupts(); //prevent reentrants
int selector = selectInDuration;
interrupts();
if (selector > 0) {
Serial.println(selector);
selector = 0;
}
}
void intrptHndlr(){
//interrupt routine to measure duration of low on selectInputPin
selectInDuration = pulseIn(selectInputPin, LOW, 1100000);
}
This seems like it should be a fairly simple task but I'm not sure where I went wrong.
Don't see why it wouldn't work, but maybe it's a timing issue on the interrupt?
Suggest disabling all the pulse-in logic and see whether the initial interrupt is actually triggering reliably. (For example just set a flag in the interrupt and test that in the main loop().
At any rate, after correcting the input waveform, output on originally posted code still only responds sporadically. I'm not sure where to go from here.
Just delaying within an interrupt handler won't stop it from recording the next interrupt.
I recommend that you follow normal best practice for interrupt handling and have your interrupt service routine do as little as possible and run for as short a time as possible. If you expect it to be triggered twice per waveform, that's fine; you can design your test sketch to confirm that it actually is doing. But my point was to try to prove that the waveform is actually triggering the interrupt reliably every time, and for that to work you need the sketch itself to be simple and clean.
I was under the impression that interrupts were implicitly disabled during interrupt routines. Is it possible that both delay() and pulseIn() have a side-effect of re-enabling interrupts?
sketchy2:
I was under the impression that interrupts were implicitly disabled during interrupt routines. Is it possible that both delay() and pulseIn() have a side-effect of re-enabling interrupts?
Before handling an interrupt, the interrupt (and all lower priority interrupts) are disabled which means their handlers won't be dispatched. But if any interrupt is triggered while it's disabled, that fact will be recorded as a pending interrupt and the corresponding handler will be dispatched as soon as it is re-enabled.
Is it possible that both delay() and pulseIn() have a side-effect of re-enabling interrupts?
No. But, delay() doesn't work in an ISR. It relies on millis() which relies on interrupts, but interrupts are disabled while an ISR runs.
What you need to do is record the value from millis() when an interrupt occurs, and determine if, when the interrupt fires again, sufficient time has elapsed to be considered unique. If so, perform the ISRs task. Otherwise, just return, ignoring the interrupt.
You should NEVER do a delay inside an ISR, or anything else that might take a while or block. What you should do instead is use the ISR to set a flag that says what happened and record the time (as returned by micros()) that it happened. Then either use a state variable to have the ISR set different variables depending on wheher it was the first or second pulse, or just monitor the variables in loop() and work it out from there.
As I understand it, you want to wait for a rising edge, then measure the time from that one to the next rising edge. Here is an outline of how do to that:
volatile uint8_t state = 0; // 0 = completed, 1 = primed, 2 =between pulses
unsigned long pulseStartTime;
volatile unsigned long pulseInterval = 0;
void setup()
{
Serial.begin(9600);
pinMode(selectInputPin, INPUT);
attachInterrupt(0, intrptHndlr, RISING);
}
// ISR executed on rising edge
void intrptHndlr()
{
unsigned long now = micros();
switch (state)
{
case 0:
// not primed so do nothing
break;
case 1:
// primed, so record start time
pulseStartTime = now;
++state;
break;
case 2:
// already had the 1st pulse so this must be the 2nd
pulseInterval = now - pulseStartTime
state = 0;
break;
}
}
loop()
{
state = 1; // tell the ISR to start timing on the next rising edge
while (state != 0) {} // wait for the 2 pulses
Serial.println(pulseInterval);
}
The pulseIn function is defined in wiring_pulse.c:
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
{
// cache the port and bit of the pin in order to speed up the
// pulse width measuring loop and achieve finer resolution. calling
// digitalRead() instead yields much coarser resolution.
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
uint8_t stateMask = (state ? bit : 0);
unsigned long width = 0; // keep initialization out of time critical area
// convert the timeout from microseconds to a number of times through
// the initial loop; it takes 16 clock cycles per iteration.
unsigned long numloops = 0;
unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;
// wait for any previous pulse to end
while ((*portInputRegister(port) & bit) == stateMask)
if (numloops++ == maxloops)
return 0;
// wait for the pulse to start
while ((*portInputRegister(port) & bit) != stateMask)
if (numloops++ == maxloops)
return 0;
// wait for the pulse to stop
while ((*portInputRegister(port) & bit) == stateMask) {
if (numloops++ == maxloops)
return 0;
width++;
}
// convert the reading to microseconds. The loop has been determined
// to be 20 clock cycles long and have about 16 clocks between the edge
// and the start of the loop. There will be some error introduced by
// the interrupt handlers.
return clockCyclesToMicroseconds(width * 21 + 16);
}