I am building a quad-copter, the problem is of stability and for better stability lesser sampling time is required. For lesser sampling time I need to have faster loop() function. But the Pulsein() command takes very large time which in turn expands the sampling time. Is there any method to read the rc channels in background and read them from an integer.
-
Why not have a look at some of the open-source ATMEGA-based quadcopter firmwares, at least to examine for ideas? I don't know if you will find some that are based on the Arduino libraries, but you should find some that will run on the hardware. That said, fundamentally you cannot measure pulses faster than they occur, which is typically a repetition rate of only around 50 times a second. You can write code which accomplishes other things while waiting for the pulses, but you can't speed them up unless you change the receiver, for example directly talking to the 2.4 GHz chip.– Chris StrattonOct 11, 2015 at 21:25
2 Answers
The keyword you are looking for here is interrupt. What is a pulse but a signal that goes from low to high followed by going from high to low, with some period of time between them?
Instead of sitting there waiting for the pulse to arrive then measuring how long it is, which is essentially what pulseIn()
does, you need to attach the signal to an interrupt pin. Then you write an interrupt routine that triggers when the signal changes from one level to another which looks to see what the level actually is.
If the signal has risen from low to high then you record the time it happened. If the signal has fallen from high to low then you look what the time is, and subtract the time you remembered from when the signal rose. That difference is then the width of the pulse, which you can store in an integer to read in your main loop.
If you have a number of signals you want to monitor you might want to look into using the pin change interrupt which allows you to use any pin as an interrupt source.
-
While you can use interrupts to allow potentially accomplishing other things while waiting for the pulses, they won't permit an input-based control loop to run at a higher rate than the inputs themselves arrive at - for a legacy PWM output, that is typically in the neighborhood of 50 Hz repetition rate. Of course it is possible to run the on-board stabilization algorithm at some multiple of the radio data rate, if desired. Oct 11, 2015 at 23:20
-
@ChrisStratton I think you are misinterpreting the question. When he talks about "sampling" I am assuming (and I think quite rightly) that he is talking about sampling something else - accelerometers maybe - since he says waiting for the pulses slows down the sampling that he is doing, and it's the sampling he needs to do faster, not the reading of the pulses.– Majenko ♦Oct 11, 2015 at 23:22
-
I agree with Majenko here.
pulseIn
blocks, which is what I gather he is trying to avoid. Using interrupts would help in this situation.– Nick Gammon ♦Oct 12, 2015 at 6:54 -
yes i am using accelerometer, for faster data reading. can i use interupts for all of 6 channels in RC reciever Oct 12, 2015 at 8:16
-
@Majenko I have read your answer and got information from it. but Now the problem i am asking in other quesstion is how to read multiple channels with one interupt. thats the headache now, Oct 12, 2015 at 16:10
You may find my answer to another question – Read RC receiver channels using Interrupt instead of PulseIn – useful. It includes the sketch shown below, which is intended for background timing of four RC channels attached to pins A0-A3. See the other answer for further comments about the code, example output, etc.
/* rcTiming.ino -- JW, 30 November 2015 --
* Uses pin-change interrupts on A0-A4 to time RC pulses
*
* Ref: https://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
}