Scenario
I've plugged 6 pins from my RC receiver into my Arduino in order to read the incoming values.
Problem
I'm using Digital PWM~ pins 2-6. Oddly the Arduino only reads odd channels:
1480, 0, 1175, 0, 991, 0
I've that if I physically reverse the cables (2-7, 3-6, 4-5) going into the Arduino I get ALL values:
988, 983, 1511, 1174, 1490, 1487
I've found that if I go back to my initial pin setup and merely flip neighbors (2-3, 4-5, 6-7) that works as well.
Question
What could possibly be causing this?
Code
Here is the current code I've written
int ch1, ch2, ch3, ch4, ch5, ch6;
int ch1Pin = 2;
int ch2Pin = 3;
int ch3Pin = 4;
int ch4Pin = 5;
int ch5Pin = 6;
int ch6Pin = 7;
void setup() {
pinMode(ch1Pin, INPUT);
pinMode(ch2Pin, INPUT);
pinMode(ch3Pin, INPUT);
pinMode(ch4Pin, INPUT);
pinMode(ch5Pin, INPUT);
pinMode(ch6Pin, INPUT);
Serial.begin(9600);
}
void loop() {
ch1 = pulseIn(ch1Pin, HIGH, 25000);
ch2 = pulseIn(ch2Pin, HIGH, 25000);
ch3 = pulseIn(ch3Pin, HIGH, 25000);
ch4 = pulseIn(ch4Pin, HIGH, 25000);
ch5 = pulseIn(ch5Pin, HIGH, 25000);
ch6 = pulseIn(ch6Pin, HIGH, 25000);
String p = (String)ch1;
p += ", " + (String)ch2;
p += ", " + (String)ch3;
p += ", " + (String)ch4;
p += ", " + (String)ch5;
p += ", " + (String)ch6;
Serial.println(p);
// delay(100);
}
Photo
transmitter pwm pins to arduino digital input
EDIT
Since posting this question I've found that reversing the chXpin
values accomplishes the same effect as reversing the physical pins. This makes me thing it's a software issue so I'm digging in further.
TEMPORARY SOLUTION
I'm not sure why this works but flipping the neighbor order of pulseIn
fixed the issue:
/* OLD
ch1 = pulseIn(ch1Pin, HIGH, 25000);
ch2 = pulseIn(ch2Pin, HIGH, 25000);
ch3 = pulseIn(ch3Pin, HIGH, 25000);
ch4 = pulseIn(ch4Pin, HIGH, 25000);
ch5 = pulseIn(ch5Pin, HIGH, 25000);
ch6 = pulseIn(ch6Pin, HIGH, 25000);
*/
ch2 = pulseIn(ch2Pin, HIGH, 25000);
ch1 = pulseIn(ch1Pin, HIGH, 25000);
ch4 = pulseIn(ch4Pin, HIGH, 25000);
ch3 = pulseIn(ch3Pin, HIGH, 25000);
ch6 = pulseIn(ch6Pin, HIGH, 25000);
ch5 = pulseIn(ch5Pin, HIGH, 25000);
Why did that work? It makes no sense to me.
-
As a matter of course, not that it's related to your current issue, but your use of the String class at the end of loop is terribly wasteful and the over-use of the + operator on memory limited systems like an Arduino is known to lead to crashes. There's no reason to assemble that all into a single string to send it. You could just use back to back Serial.print lines and you would use a lot less memory. Probably not an issue on this little test code, but as you advance that one will get you into trouble later.Delta_G– Delta_G2020年05月27日 21:32:49 +00:00Commented May 27, 2020 at 21:32
1 Answer 1
pulseIn()
is a blocking function: it waits until it gets a complete
pulse, or until it times out. While it's waiting, the Arduino cannot do
anything else, and it will miss pulses on other channels.
The RC receiver sends the pulses sequentially. Then, if you try to read
them in the right sequence, it may work. If you read them out of
sequence, you will miss some pulses, and the corresponding pulseIn()
calls can timeout, returning zeros.
For this kind of job I would use the pin change interrupt on pins 2 – 7. This should allow reading the six pulses continuously without blocking the whole program. But it's low-level (AVR-level) programming, probably not doable with the standard Arduino libraries.
Edit: Here is what I had in mind when suggesting the use of the pin change interrupt. Notice that the pin numbers are "hardwired" into the code and cannot be changed easily. This is because:
- Pins 2 – 7 share a single pin change interrupt request (PCINT2_vect), thus a single ISR is needed to time the six channels.
- They also share a port (port D), meaning they can all be read by the ISR in a single instruction.
The other possible choices having these nice properties would be pins 8 – 13 (PCINT0_vect, port B) or A0 – A5 (PCINT1_vect, port C).
/*
* Read 6 PWM signals from an RC receiver.
*
* Channels 0-5 are on digital pins 2-7 (i.e. PD2-PD7 = PCINT18-23).
*/
// This always contains the last length measured on each channel.
volatile uint16_t last_pulse_lengths[6];
// Interrupt triggered on every change in pins 2-7.
ISR(PCINT2_vect)
{
static uint8_t last_pin_states;
static uint16_t start_times[6]; // start time for each channel
uint8_t pin_states = PIND >> 2; // shifted to bits 0-5
uint16_t now = micros();
// Check every changed bit of the port.
for (int i = 0; i < 6; i++) {
uint8_t mask = _BV(i);
// Record start time on rising edge.
if ((pin_states & mask) && !(last_pin_states & mask))
start_times[i] = now;
// Record pulse length on falling edge.
if (!(pin_states & mask) && (last_pin_states & mask))
last_pulse_lengths[i] = now - start_times[i];
}
last_pin_states = pin_states;
}
// Return the pulse lengths avoiding race conditions.
static void get_pulse_lengths(uint16_t lengths[6])
{
for (int i = 0; i < 6; i++) {
noInterrupts();
uint16_t tmp = last_pulse_lengths[i];
interrupts();
lengths[i] = tmp;
}
}
void setup()
{
// Setup the pin change interrupt.
PCIFR = _BV(PCIF2); // clear pin change interrupt flag
PCICR = _BV(PCIE2); // enable pin change interrupt
PCMSK2 = 0xfc; // sense changes on pins 2-7 (i.e. PD2-PD7)
Serial.begin(9600);
}
void loop()
{
// Report the pulse lengths on the serial console.
uint16_t pulse_lengths[6];
get_pulse_lengths(pulse_lengths);
for (int i = 0; i < 6; i++) {
Serial.print(pulse_lengths[i]);
if (i < 5) Serial.print(", ");
else Serial.println();
}
delay(1000);
}
-
Would this sort of integration count? playground.arduino.cc/Main/PinChangeInterruptJacksonkr– Jacksonkr2016年06月26日 13:38:30 +00:00Commented Jun 26, 2016 at 13:38
-
1The code you linked to sets individual bits in
PCMSKn
,PCIFR
andPCICR
, and it defines theISR(PCINTn_vect)
. This is exactly the kind of "low-level" programming I was talking about. Note that if using pins 2 – 7, you only need to set onePCMSK
(namelyPCMSK2
) and only defineISR(PCINT2_vect)
.Edgar Bonet– Edgar Bonet2016年06月26日 17:40:05 +00:00Commented Jun 26, 2016 at 17:40 -
I see you added example code afterward, I must have missed it the first time. I plugged it in and it worked with my RC flawlessly the first time. I modified the
delay
from1000
to10
and that's it. This answer deserves more than the +1 I already gave it.Jacksonkr– Jacksonkr2016年07月06日 22:58:05 +00:00Commented Jul 6, 2016 at 22:58