Hi!
I have made a combined servo tester and receiver servo signal monitor. The servo tester part work like a dream and I can choose either analog input from potmeter or digital input from buttons. If there is a receiver servo signal present during start-up it will automatically switch to servo signal monitor mode, so that I can read the true signal from my receivers on two channels.
The device has two inputs (from receiver) and two servo outputs that moves in opposite direction. The idea is that this could be usefull when I set up models with multiple wing servos. Both inputs and outputs are connected to D2 and D3. I use the servo libery for servo tester mode and external interrupt/Timer1 for read receiver signal mode.
The problem: (read receiver signal mode)
If I connect only one of the inputs to a receiver everything is fine. When I connect both inputs to a receiver, one of the channel readings will "loose" 9-10us. I don't understand why. Do you have any idea?
// Read receiver signal
if(bNewCh1Signal == true) {
lastSignCh1 = millis(); // refresh "time last signal counter"
if((x1 > 1000) || (x1 < 5000)) { // if signal is within range (Timer1 with prescaler 8, 1 tick = 0,5?s)
if ((nCh1In*2) < (x1 - 3) || (nCh1In*2) > (x1 + 3)) { // and if signal has changed by more than 1,5?s
nCh1In = x1/2; } } // update nCh1In
bNewCh1Signal = false; // set this back to false when finished, while true, calcInput will not update nThrottleIn
}
if(bNewCh2Signal == true) {
lastSignCh2 = millis();
if((x2 > 1000) || (x2 < 5000)) {
if ((nCh2In*2) < (x2 - 3) || (nCh2In*2) > (x2 + 3)) {
nCh2In = x2/2; } }
bNewCh2Signal = false;
}
if ((lastPrint + 500) < millis()) {
printLcd(); // if a new servo signal has been measured, print the value to LCD
}
}
// interrupt routine; read signal from receiver ch 1
void calcInputCh1()
{
// if the pin is high, its the start of an interrupt
if(digitalRead(CH1_SIGNAL_IN_PIN) == HIGH) {
TCNT1 = 0; } // restart Timer1
else {
// if the pin is low, its the falling edge of the pulse so now we can calculate the pulse duration
x1 = TCNT1; // read Timer1 = pulse, store value in volatile long x1
bNewCh1Signal = true;
// tell loop there is a new signal on the throttle channel
// nCh1In will not be updated until loop sets
// bNewCh1Signal back to false
}
}
// interrupt routine; read signal from receiver ch 2
void calcInputCh2() {
if(digitalRead(CH2_SIGNAL_IN_PIN) == HIGH) {
TCNT1 = 0; }
else {
x2 = TCNT1;
bNewCh2Signal = true;
}
}
Thank you so much with your post but unfortunately I'm also looking for any ideas about this stuff. I'm looking forward that someone can answer this. Thanks!
I have now discovered that the issue only occurs when I try to read two adjacent receiver channels; if I connect ch1 and ch2 on the receiver, the measured value value from ch2 will miss 9-10 microseconds. If I connect ch1 and ch3 or any other receiver outputs that is not adjacent to each other, then both readings are correct.
I am beginning to think that when the Arduino has read the first signal from ch1, the next signal on ch2 starts to fast for the Arduino to register in time. I read somewhere that one interrupt routine executing will prevent another from trigger?
The missing micros are probably due to one of two things -
The chip is servicing the falling pin and cannot get the time of the rising pin until this is finished
Something in your main code turns interrupts off briefly and so you pin change cannot be detected
If its consistent (case 1 would be), just add a trim value - your description suggests that its case 1.
As for you code I would suggest that you don't reset TCNT1, the servo library uses this, it also resets it, your code will clash with the servo library.
Yes, only one interrupt will execute at a time. If your first channel's interrupt is still processing when the interrupt for the second channel arrives, it won't get processed until the existing interrupt has completed. Can you reduce the amount done in your interrupt handler to get it completed sooner?
Duane; Yes I used part of the code in your excellent tutorial "How To Read an RC Receiver With A Microcontroller - Part 1". However, I wanted to read two channels and I figured I could use the same principle by adding external interrupt on pin3 for the second channel. I'm quite new to this, and didn't wanted use Pinchange interrupts just yet, as it seems more complicated.
The complete code is attached in the first post, it was to large to include as text.
I agree with you, it seems like the "Case 1" you are describing is the issue here. I guess that it could be the digtalRead function that takes up time in the ISR. And yes, it is consistent. As you suggest I can get around it by adding some trim in the code if there are two signals present, but I'm curious if there is another way.
The error is very small, 9-10us is not more than one click on my transmitter trim, but still....
Regarding your comment on resetting Timer1 in ISR; I'm not using servo library when reading receiver signals , the servo library is included, but no servos are attached in the "read receiver signal mode" so it doesn't actually do anything. (or maybe I'm wrong...)
Anyway, I'm not sure how to do this without resetting Timer1, but I guess it's involving handling the Timer1 overflow in some way. I really enjoy this, but I have so much to learn!
PeterH: I was thinking the same, but I don't think I can reduce the amount of code in the ISR, it's only a couple of lines and I don't see anything that can be moved or removed.
Very cool video, impressive traction control you have made
Thanks for your advise, I will try out the pinchange method later when I have more time. For now I made a quick and dirty fix by measuring the time between ch1 falling edge and ch2 rising edge using micros() and adding some trim to ch2 if the time is less than 20 microseconds (which means that two ajacant receiver channels have been conneted and trim is needed)