Regarding the question, “Does PinChangeInt only work on digital pins?”, note that PCI's work on all the digital pins of an ATmega328, and on the first six analog pins. (32-pin '328s have eight analog pins, A0-A7; 28-pin '328s only six, A0-A5.)
Regarding the problem symptom that “pulsewidth
just seems to increase continuously”, that could happen if for some reason rising edges aren't processed. To debug, add a couple more volative byte variables, say nrises
and nfalls
; increment each of them in the appropriate interrupt handler; and report their values from time to time.
Regarding your #include <PinChangeInt.h>
statement, note that PinChangeInt.h
is deprecated (as of 3 April 2015). Consider using EnableInterrupt.h
instead.
It's possible you'll resolve your problem while changing libraries. If so, fine. If not, or if that library's performance isn't adequate, consider changing over to handling the pin change interrupts more directly. The ISR in the following sketch shown below runs several times faster than code that uses PinChangeInt
or EnableInterrupt
library calls.
[Edit: This edit encloses the ISR in a sketch that compiles. The code previously shown was missing two array length specifications. This code was tested with a rotary encoder attached to A2, A3, with A0 and A1 grounded; an output sample (using an Arduino Nano) follows the code.]
/* rcTiming.ino -- JW, 30 November 2015 --
* Uses pin-change interrupts on A0-A4 to time RC pulses
*
* Ref: http://arduino.stackexchange.com/questions/18183/read-rc-receiver-channels-using-interrupt-instead-of-pulsein
*
*/
#include <Streaming.h>
static byte rcOld; // Prev. states of inputs
volatile unsigned long rcRises[4]; // times of prev. rising edges
volatile unsigned long rcTimes[4]; // recent pulse lengths
volatile unsigned int rcChange=0; // Change-counter
// Be sure to call setup_rcTiming() from setup()
void setup_rcTiming() {
rcOld = 0;
pinMode(A0, INPUT); // pin 14, A0, PC0, for pin-change interrupt
pinMode(A1, INPUT); // pin 15, A1, PC1, for pin-change interrupt
pinMode(A2, INPUT);
pinMode(A3, INPUT);
PCMSK1 |= 0x0F; // Four-bit mask for four channels
PCIFR |= 0x02; // clear pin-change interrupts if any
PCICR |= 0x02; // enable pin-change interrupts
}
// Define the service routine for PCI vector 1
ISR(PCINT1_vect) {
byte rcNew = PINC & 15; // Get low 4 bits, A0-A3
byte changes = rcNew^rcOld; // Notice changed bits
byte channel = 0;
unsigned long now = micros(); // micros() is ok in int routine
while (changes) {
if ((changes & 1)) { // Did current channel change?
if ((rcNew & (1<<channel))) { // Check rising edge
rcRises[channel] = now; // Is rising edge
} else { // Is falling edge
rcTimes[channel] = now-rcRises[channel];
}
}
changes >>= 1; // shift out the done bit
++channel;
++rcChange;
}
rcOld = rcNew; // Save new state
}
void setup() {
Serial.begin(115200);
Serial.println("Starting RC Timing Test");
setup_rcTiming();
}
void loop() {
unsigned long rcT[4]; // copy of recent pulse lengths
unsigned int rcN;
if (rcChange) {
// Data is subject to races if interrupted, so off interrupts
cli(); // Disable interrupts
rcN = rcChange;
rcChange = 0; // Zero the change counter
rcT[0] = rcTimes[0];
rcT[1] = rcTimes[1];
rcT[2] = rcTimes[2];
rcT[3] = rcTimes[3];
sei(); // reenable interrupts
Serial << "t=" << millis() << " " << rcT[0] << " " << rcT[1]
<< " " << rcT[2] << " " << rcT[3] << " " << rcN << endl;
}
sei(); // reenable interrupts
}
The code shown above uses Streaming.h
, a contributed library that adds some “syntactic sugar” to Arduino C. That is, at compile time it converts C++-like <<
Serial stream operators to Serial.print
statements, without increasing code size. If you don't have Streaming.h
installed, either install it via Streaming5.zip
from arduiniana.org or manually translate the Serial << ...
statement to Serial.print
statements.
A sample of the sketch's output on serial monitor:
t=35773 0 0 14196 13456 4
t=35775 0 0 14196 13456 3
t=35778 0 0 14196 5748 4
t=35781 0 0 5244 5748 3
t=35783 0 0 5244 5748 4
t=35785 0 0 5244 5748 3
t=35786 0 0 5244 28 12
t=35788 0 0 3024 28 3
t=35789 0 0 3024 28 4
t=35792 0 0 3024 28 3
t=35796 0 0 3024 6396 4
t=35802 0 0 9316 6396 3
t=35809 0 0 9316 6396 4
In the output above, the two 0's are from A0, A1 being grounded and not changing. The next two numbers are from the DT and CLK lines of a KY-040 rotary encoder, which toggle alternately to encode step-direction. They should represent on-times in microseconds, and are always multiples of 4 because micros()
reports multiples of 4.
The last number is the number of times the ISR's change-checking loop ran since loop()
's previous print. An A2 change will show a count of 3 or more, and an A3 change 4 or more. Cases where times are the same may represent contact bounces shorter than the ISR's service time; ie, several falling edges in a row might be detected, or several rising edges. A storage scope or logic analyzer probably is necessary for finding out exactly what happens.