Go Down

Topic: 4 channel RC reading jitter (Read 2 times) previous topic - next topic


DuaneB

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 -

http://arduino.cc/forum/index.php/topic,162787.0.html

Try the code in the post above with the fix to the micros function and let us know how you get on by posting in the topic linked above

Once you have this fix in place, the 4 channel code should also be fine

Duane B

rcarduino.blogspot.com

DuaneB

Hi,
    I have managed to replicate the situation -

Expected results -
1304 1402 0 1102 1600 1195
1304 1402 0 1102 1600 1195
1304 1402 0 1102 1599 1195
1304 1401 0 1102 1599 1194
1304 1401 0 1102 1599 1194
1305 1401 0 1102 1599 1194

At this point the final channel looses 1000 us

1305 1401 0 1101 1599 194
1305 1402 0 1101 1599 194
1304 1402 0 1101 1599 194
1304 1402 0 1101 1600 194

which soon turns up on the first channel

2304 1402 0 1102 1600 194
2304 1402 0 1102 1600 195
2304 1402 0 1102 1600 195
2304 1402 0 1102 1600 195
2304 1402 0 1102 1599 195

Finally everything gets back in synch

1304 1402 0 1101 1600 1194
1304 1402 0 1102 1600 1195
1304 1402 0 1102 1600 1195
1304 1402 0 1102 1599 1195

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.

http://rcarduino.blogspot.ae/2013/04/reading-rc-channels-with-arduino-due.html

I will continue to look into this tonight.

Duane B

rcarduino.blogspot.com

DuaneB

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 -

http://rcarduino.blogspot.ae/2013/04/problem-reading-rc-channels-rcarduino.html

Duane B

rcarduino.blogspot.com

weichoi83

hi Duane,

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:
Code: [Select]

#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

Go Up