I'm trying to read the 4 channel RC using attachInterrupt() at pin 9-12 and sending it through serial1 once all four channels duty cycle are read. The duty cycle is range from 1000us to 2000us. However some jitter happen in data collected, such as
It looks like interrupts clashes and latency happen. I read that it is common problem in arduino, and one of remedies is using RCArduinoFastLib, but that is only applied to Atmega controller. Is there examples to do hardware timer capture in ARM controller? Any idea are welcome.
// Assign your channel in pins
#define AILE_IN_PIN 9
#define AUX1_IN_PIN 10
#define ELEV_IN_PIN 11
#define RUDD_IN_PIN 12
// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define AILE_FLAG 1
#define AUX1_FLAG 2
#define ELEV_FLAG 4
#define RUDD_FLAG 8
// holds the update flags defined above
volatile uint8_t bUpdateFlagsShared;
volatile uint16_t unAileInShared;
volatile uint16_t unAux1InShared;
volatile uint16_t unElevInShared;
volatile uint16_t unRuddInShared;
uint32_t ulAileStart;
uint32_t ulAux1Start;
uint32_t ulElevStart;
uint32_t ulRuddStart;
//global variable
uint16_t AUX1time;
uint16_t RUDDtime;
uint16_t ELEVtime;
uint16_t AILEtime;
void setup()
{
Serial.begin(115200);
Serial1.begin(115200);
attachInterrupt(AILE_IN_PIN, calcAile,CHANGE);
attachInterrupt(AUX1_IN_PIN, calcAux1,CHANGE);
attachInterrupt(ELEV_IN_PIN, calcElev,CHANGE);
attachInterrupt(RUDD_IN_PIN, calcRudd,CHANGE);
}
void loop()
{
if(bUpdateFlagsShared==0x0F)
{
noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables
AILEtime = unAileInShared;
AUX1time = unAux1InShared;
ELEVtime = unElevInShared;
RUDDtime = unRuddInShared;
bUpdateFlagsShared = 0;
interrupts();
// send the bluetooth whenever all servos are ready
Serial1.write(MSB(AILEtime));
Serial1.write(LSB(AILEtime));
Serial1.write(MSB(AUX1time));
Serial1.write(LSB(AUX1time));
Serial1.write(MSB(ELEVtime));
Serial1.write(LSB(ELEVtime));
Serial1.write(MSB(RUDDtime));
Serial1.write(LSB(RUDDtime));
Serial1.print("\n");
}
}
// simple interrupt service routine
void calcAile()
{
// if the pin is high, its a rising edge of the signal pulse, so lets record its value
if(digitalRead(AILE_IN_PIN) == HIGH)
{
ulAileStart = micros();
}
else
{
// else it must be a falling edge, so lets get the time and subtract the time of the rising edge
// this gives use the time between the rising and falling edges i.e. the pulse duration.
unAileInShared = (uint16_t)(micros() - ulAileStart);
// use set the throttle flag to indicate that a new throttle signal has been received
bUpdateFlagsShared |= AILE_FLAG;
}
}
void calcAux1()
{
if(digitalRead(AUX1_IN_PIN)==HIGH) //PORTD&0x20
{
ulAux1Start = micros();
}
else
{
unAux1InShared = (uint16_t)(micros() - ulAux1Start);
bUpdateFlagsShared |= AUX1_FLAG;
}
}
void calcElev()
{
if(digitalRead(ELEV_IN_PIN)==HIGH) //PORTD&0x40
{
ulElevStart = micros();
}
else
{
unElevInShared = (uint16_t)(micros() - ulElevStart);
bUpdateFlagsShared |= ELEV_FLAG;
}
}
void calcRudd()
{
if(digitalRead(RUDD_IN_PIN)==HIGH)
{
ulRuddStart = micros();
}
else
{
unRuddInShared = (uint16_t)(micros() - ulRuddStart);
bUpdateFlagsShared |= RUDD_FLAG;
}
}
byte LSB(int val) {
return (byte) (val & 0x00FF);
}
byte MSB(int val) {
return (byte) ((val & 0xFF00) >> 8);
}
Sorry for late reply, I've tested with noInterrupts() , the function looks to disable all the interrupts no problem. I've also tested with another sketch that do rc relay, the code is as follow:
#include <Servo.h>
// Assign your channel in pins
#define AILE_IN_PIN 9
#define AUX1_IN_PIN 10
#define ELEV_IN_PIN 11
#define RUDD_IN_PIN 12
// Servo drive pin
#define SERVO_PIN1 4
#define SERVO_PIN2 5
#define SERVO_PIN3 6
#define SERVO_PIN4 7
// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define AILE_FLAG 1
#define AUX1_FLAG 2
#define ELEV_FLAG 4
#define RUDD_FLAG 8
// holds the update flags defined above
volatile uint8_t bUpdateFlagsShared;
volatile boolean bAileUpdate;
volatile boolean bAux1Update;
// shared variables are updated by the ISR and read by loop.
// In loop we immediatley take local copies so that the ISR can keep ownership of the
// shared ones. To access these in loop
// we first turn interrupts off with noInterrupts
// we take a copy to use in loop and the turn interrupts back on
// as quickly as possible, this ensures that we are always able to receive new signals
volatile uint16_t unAileInShared;
volatile uint16_t unAux1InShared;
volatile uint16_t unElevInShared;
volatile uint16_t unRuddInShared;
// These are used to record the rising edge of a pulse in the calcInput functions
// They do not need to be volatile as they are only used in the ISR. If we wanted
// to refer to these in loop and the ISR then they would need to be declared volatile
uint32_t ulAileStart;
uint32_t ulAux1Start;
uint32_t ulElevStart;
uint32_t ulRuddStart;
//global variable
uint16_t AUX1time;
uint16_t RUDDtime;
uint16_t ELEVtime;
uint16_t AILEtime;
Servo aileServo; //cannot declared as volatile
Servo aux1Servo;
void setup()
{
Serial.begin(115200);
Serial1.begin(115200);
attachInterrupt(AILE_IN_PIN, calcAile,CHANGE);
attachInterrupt(AUX1_IN_PIN, calcAux1,CHANGE);
attachInterrupt(ELEV_IN_PIN, calcElev,CHANGE);
attachInterrupt(RUDD_IN_PIN, calcRudd,CHANGE);
aileServo.attach(SERVO_PIN1);
aux1Servo.attach(SERVO_PIN2);
// trim servo input
aileServo.writeMicroseconds(1555);
aux1Servo.writeMicroseconds(1505);
}
void loop()
{
// create local variables to hold a local copies of the channel inputs
// these are declared static so that thier values will be retained
// between calls to loop.
static uint16_t unThrottleIn;
static uint16_t unSteeringIn;
static uint16_t unAuxIn;
//static uint8_t bUpdateServo;
// check shared update flags to see if any channels have a new signal
if(bUpdateFlagsShared==0x0F)
{
noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables
AILEtime = unAileInShared;
AUX1time = unAux1InShared;
ELEVtime = unElevInShared;
RUDDtime = unRuddInShared;
bUpdateFlagsShared = 0;
interrupts();
bAileUpdate = true;
bAux1Update = true;
Serial.println(AILEtime);
Serial.print("\t");
Serial.println(AUX1time);
Serial.print("\t");
Serial.println(ELEVtime);
Serial.print("\t");
Serial.println(bUpdateFlagsShared);
Serial.print("\t");
Serial.println(t1);
}
if(bAileUpdate)
{
// fluctuating Servo/ELEVtime
if(aileServo.readMicroseconds()!=AILEtime)
{
aileServo.writeMicroseconds(AILEtime);
}
bAileUpdate = false;
}
if(bAux1Update)//if(bUpdateServo&0x01)
{
if(aux1Servo.readMicroseconds()!=AUX1time)
{
aux1Servo.writeMicroseconds(AUX1time);
}
bAux1Update = false;
}
}
// simple interrupt service routine
void calcAile()
{
// if the pin is high, its a rising edge of the signal pulse, so lets record its value
if(digitalRead(AILE_IN_PIN) == HIGH) //optimizing tbd... digitalRead(AILE_IN_PIN) == HIGH
{
ulAileStart = micros();
}
else
{
// else it must be a falling edge, so lets get the time and subtract the time of the rising edge
// this gives use the time between the rising and falling edges i.e. the pulse duration.
unAileInShared = (uint16_t)(micros() - ulAileStart);
// use set the throttle flag to indicate that a new throttle signal has been received
bUpdateFlagsShared |= AILE_FLAG;
}
}
void calcAux1()
{
if(digitalRead(AUX1_IN_PIN)==HIGH)
{
ulAux1Start = micros();
}
else
{
unAux1InShared = (uint16_t)(micros() - ulAux1Start);
bUpdateFlagsShared |= AUX1_FLAG;
}
}
void calcElev()
{
if(digitalRead(ELEV_IN_PIN)==HIGH) //PORTD&0x40
{
ulElevStart = micros();
}
else
{
unElevInShared = (uint16_t)(micros() - ulElevStart);
bUpdateFlagsShared |= ELEV_FLAG;
}
}
void calcRudd()
{
if(digitalRead(RUDD_IN_PIN)==HIGH)
{
ulRuddStart = micros();
}
else
{
unRuddInShared = (uint16_t)(micros() - ulRuddStart);
bUpdateFlagsShared |= RUDD_FLAG;
}
}
}
The result of the above code is that the controlled servo jittering, due to some random jump in read in rc pulse width, for instance:
1588 1519
1585 1519
1587 1519
1584 1519
587 519
1584 1519
1588 1519
1584 1519
1587 1519
Hi,
I have just put together a loop back sketch which simply outputs six servos and reads them back in again as if they are RC Channels.
Right now it uses pinChangeInt on the Arduino UNO. I will convert it for Due this evening, in the meantime if you want to have a go at converting it you can find the post here -
Having used the same code pattern extensively I am wondering if this might be an internal error in the way that Arduino Due interrupts are implemented, its very similar to a synchronisation problem I have had in the past when multiplexing RC Channels into fewer pins (i.e. six channels read through two pins) the problem seems to effect adjacent channels i.e. 1 and 2 or 6 and 1 (wrap around) never 2 and 4.
The test code is here for anyone else that wants to try, its a simple loop back where a servo output is fed back in as a channel input.
Hi,
It looks as if the error comes from a combination of interrupts and the way in which micros is implemented, you can find more information and a suggested fix to the Due cores here -