Thursday, April 25, 2013

Reading RC Channels with Arduino Due

Update 27/04/2013

The following code was originally presented as a test sketch to demonstrate a glitch in the Arduino Due micros function which is being resolved. See the following post for details of the glitch and resolution.

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

With this resolution in place the code presented below can be used to read 8 RC Channels and output them to a combination of upto 8 RC Servos or ESCs.

The code can be operated in two configurations -

1) Loop Back Test - Here 8 servo outputs are created and given fixed values from 1100 to 1800, these pulse values are output on pins 10 to 17 and can be read back in through the interrupts attached to pins 2 to 9. This is intended to give the user confidence that the code is able to read multiple RC Channels before moving to configuration 2 - Pass Through

2) Pass Through -  This is similar to 1) above however the input is now connected to the output such that a change in an incoming signal will cause a corresponding change in the servo output signal. To implement pass  through on any channel, simply remove the comment form the start of the servo.writeMicrosecond command for that channel for example

Change this -

  if(bUpdateFlags & CHANNEL1_FLAG)
  {
      // remove the // from the line below to implement pass through updates to the servo on this channel -
      //servoChannel1.writeMicroseconds(unChannel1In);
      Serial.print(unChannel1In);
      Serial.print(",");
  }
to this -

  if(bUpdateFlags & CHANNEL1_FLAG)
  {
      // remove the // from the line below to implement pass through updates to the servo on this channel -
      servoChannel1.writeMicroseconds(unChannel1In);
      Serial.print(unChannel1In);
      Serial.print(",");
  }




The code has been ported from an original project based on the Arduino UNO, follow the links in the comments for the background and detailed explanation.

// MultiChannels
//
// rcarduino.blogspot.com
//
// A simple approach for reading and writing eight RC Channels using Arduino Due interrupts
//
// See related posts -
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html
// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html
//
// rcarduino.blogspot.com
//

#include "Servo.h"

// Assign your channel in pins
#define CHANNEL1_IN_PIN 2
#define CHANNEL2_IN_PIN 3
#define CHANNEL3_IN_PIN 4
#define CHANNEL4_IN_PIN 5
#define CHANNEL5_IN_PIN 6
#define CHANNEL6_IN_PIN 7
#define CHANNEL7_IN_PIN 8
#define CHANNEL8_IN_PIN 9

// Assign your channel out pins
#define CHANNEL1_OUT_PIN 10
#define CHANNEL2_OUT_PIN 11
#define CHANNEL3_OUT_PIN 12
#define CHANNEL4_OUT_PIN 13
#define CHANNEL5_OUT_PIN 14
#define CHANNEL6_OUT_PIN 15
#define CHANNEL7_OUT_PIN 16
#define CHANNEL8_OUT_PIN 17

// Servo objects generate the signals expected by Electronic Speed Controllers and Servos
// We will use the objects to output the signals we read in
// this example code provides a straight pass through of the signal with no custom processing
Servo servoChannel1;
Servo servoChannel2;
Servo servoChannel3;
Servo servoChannel4;
Servo servoChannel5;
Servo servoChannel6;
Servo servoChannel7;
Servo servoChannel8;

// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define CHANNEL1_FLAG 1
#define CHANNEL2_FLAG 2
#define CHANNEL3_FLAG 4
#define CHANNEL4_FLAG 8
#define CHANNEL5_FLAG 16
#define CHANNEL6_FLAG 32
#define CHANNEL7_FLAG 64
#define CHANNEL8_FLAG 128

// holds the update flags defined above
volatile uint32_t bUpdateFlagsShared;

// 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 uint32_t unChannel1InShared;
volatile uint32_t unChannel2InShared;
volatile uint32_t unChannel3InShared;
volatile uint32_t unChannel4InShared;
volatile uint32_t unChannel5InShared;
volatile uint32_t unChannel6InShared;
volatile uint32_t unChannel7InShared;
volatile uint32_t unChannel8InShared;

void setup()
{
  Serial.begin(115200);

  Serial.println("multiChannels");

  // attach servo objects, these will generate the correct
  // pulses for driving Electronic speed controllers, servos or other devices
  // designed to interface directly with RC Receivers
  servoChannel1.attach(CHANNEL1_OUT_PIN);
  servoChannel2.attach(CHANNEL2_OUT_PIN);
  servoChannel3.attach(CHANNEL3_OUT_PIN);
  servoChannel4.attach(CHANNEL4_OUT_PIN);
  servoChannel5.attach(CHANNEL5_OUT_PIN);
  servoChannel6.attach(CHANNEL6_OUT_PIN);
  servoChannel7.attach(CHANNEL7_OUT_PIN);
  servoChannel8.attach(CHANNEL8_OUT_PIN);

  // attach the interrupts used to read the channels
  attachInterrupt(CHANNEL1_IN_PIN, calcChannel1,CHANGE);
  attachInterrupt(CHANNEL2_IN_PIN, calcChannel2,CHANGE);
  attachInterrupt(CHANNEL3_IN_PIN, calcChannel3,CHANGE);
  attachInterrupt(CHANNEL4_IN_PIN, calcChannel4,CHANGE);
  attachInterrupt(CHANNEL5_IN_PIN, calcChannel5,CHANGE);
  attachInterrupt(CHANNEL6_IN_PIN, calcChannel6,CHANGE);
  attachInterrupt(CHANNEL7_IN_PIN, calcChannel7,CHANGE);
  attachInterrupt(CHANNEL8_IN_PIN, calcChannel8,CHANGE);

  // for loop back test only, lets set each channel to a known value
  servoChannel1.writeMicroseconds(1100);
  servoChannel2.writeMicroseconds(1200);
  servoChannel3.writeMicroseconds(1300);
  servoChannel4.writeMicroseconds(1400);
  servoChannel5.writeMicroseconds(1500);
  servoChannel6.writeMicroseconds(1600);
  servoChannel7.writeMicroseconds(1700);
  servoChannel8.writeMicroseconds(1800);
}

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 uint32_t unChannel1In;
  static uint32_t unChannel2In;
  static uint32_t unChannel3In;
  static uint32_t unChannel4In;
  static uint32_t unChannel5In;
  static uint32_t unChannel6In;
  static uint32_t unChannel7In;
  static uint32_t unChannel8In;
 
  // local copy of update flags
  static uint32_t bUpdateFlags;

  // check shared update flags to see if any channels have a new signal
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;
  
    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.
  
    if(bUpdateFlags & CHANNEL1_FLAG)
    {
      unChannel1In = unChannel1InShared;
    }
  
    if(bUpdateFlags & CHANNEL2_FLAG)
    {
      unChannel2In = unChannel2InShared;
    }
  
    if(bUpdateFlags & CHANNEL3_FLAG)
    {
      unChannel3In = unChannel3InShared;
    }
   
    if(bUpdateFlags & CHANNEL4_FLAG)
    {
      unChannel4In = unChannel4InShared;
    }
  
    if(bUpdateFlags & CHANNEL5_FLAG)
    {
      unChannel5In = unChannel5InShared;
    }
  
    if(bUpdateFlags & CHANNEL6_FLAG)
    {
      unChannel6In = unChannel6InShared;
    }
   
    if(bUpdateFlags & CHANNEL7_FLAG)
    {
      unChannel7In = unChannel7InShared;
    }
  
    if(bUpdateFlags & CHANNEL8_FLAG)
    {
      unChannel8In = unChannel8InShared;
    }
    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;
  
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }
 
  // do any processing from here onwards
  // only use the local values unChannel1, unChannel2, unChannel3, unChannel4, unChannel5, unChannel6, unChannel7, unChannel8
  // variables unChannel1InShared, unChannel2InShared, etc are always owned by the
  // the interrupt routines and should not be used in loop
 
  if(bUpdateFlags & CHANNEL1_FLAG)
  {
      // remove the // from the line below to implement pass through updates to the servo on this channel -
      //servoChannel1.writeMicroseconds(unChannel1In);
      Serial.println();
      Serial.print(unChannel1In);
      Serial.print(",");
  }

  if(bUpdateFlags & CHANNEL2_FLAG)
  {
      // remove the // from the line below to implement pass through updates to the servo on this channel -
      //servoChannel2.writeMicroseconds(unChannel2In);
      Serial.print(unChannel2In);
      Serial.print(",");
  }

  if(bUpdateFlags & CHANNEL3_FLAG)
  {
      // remove the // from the line below to implement pass through updates to the servo on this channel -
      //servoChannel3.writeMicroseconds(unChannel3In);
      Serial.print(unChannel3In);
      Serial.print(",");
  }

  if(bUpdateFlags & CHANNEL4_FLAG)
  {
    // remove the // from the line below to implement pass through updates to the servo on this channel -
    // servoChannel4.writeMicroseconds(unChannel4In);
    Serial.print(unChannel4In);
    Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL5_FLAG)
  {
    // remove the // from the line below to implement pass through updates to the servo on this channel -
    // servoChannel5.writeMicroseconds(unChannel5In);
    Serial.print(unChannel5In);
    Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL6_FLAG)
  {
    // remove the // from the line below to implement pass through updates to the servo on this channel -
    // servoChannel6.writeMicroseconds(unChannel6In);
    Serial.print(unChannel6In);
  }
 
  if(bUpdateFlags & CHANNEL7_FLAG)
  {
    // remove the // from the line below to implement pass through updates to the servo on this channel -
    // servoChannel7.writeMicroseconds(unChannel7In);
    Serial.print(unChannel7In);
    Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL8_FLAG)
  {
    // remove the // from the line below to implement pass through updates to the servo on this channel -
    // servoChannel8.writeMicroseconds(unChannel8In);
    Serial.print(unChannel8In);
  }
 
  bUpdateFlags = 0;
}

void calcChannel1()
{
  static uint32_t ulStart;
 
  if(digitalRead(CHANNEL1_IN_PIN))
  {
    ulStart = micros();
  }
  else
  {
    unChannel1InShared = (uint32_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL1_FLAG;
  }
}

void calcChannel2()
{
  static uint32_t ulStart;
 
  if(digitalRead(CHANNEL2_IN_PIN))
  {
    ulStart = micros();
  }
  else
  {
    unChannel2InShared = (uint32_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL2_FLAG;
  }
}

void calcChannel3()
{
  static uint32_t ulStart;
 
  if(digitalRead(CHANNEL3_IN_PIN))
  {
    ulStart = micros();
  }
  else
  {
    unChannel3InShared = (uint32_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL3_FLAG;
  }
}

void calcChannel4()
{
  static uint32_t ulStart;
 
  if(digitalRead(CHANNEL4_IN_PIN))
  {
    ulStart = micros();
  }
  else
  {
    unChannel4InShared = (uint32_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL4_FLAG;
  }
}

void calcChannel5()
{
  static uint32_t ulStart;
 
  if(digitalRead(CHANNEL5_IN_PIN))
  {
    ulStart = micros();
  }
  else
  {
    unChannel5InShared = (uint32_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL5_FLAG;
  }
}

void calcChannel6()
{
  static uint32_t ulStart;
 
  if(digitalRead(CHANNEL6_IN_PIN))
  {
    ulStart = micros();
  }
  else
  {
    unChannel6InShared = (uint32_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL6_FLAG;
  }
}

void calcChannel7()
{
  static uint32_t ulStart;
 
  if(digitalRead(CHANNEL7_IN_PIN))
  {
    ulStart = micros();
  }
  else
  {
    unChannel7InShared = (uint32_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL7_FLAG;
  }
}

void calcChannel8()
{
  static uint32_t ulStart;
 
  if(digitalRead(CHANNEL8_IN_PIN))
  {
    ulStart = micros();
  }
  else
  {
    unChannel8InShared = (uint32_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL8_FLAG;
  }
}


Wednesday, April 24, 2013

Problem reading RC Channels - The RCArduino Loop Back Test

Many people experience problems adding additional channels to the RCArduino test sketches or porting them to new devices, its usually down to mistakes in the way the additional channels are coded

This post presents an example of managing 8 input/output channels, its a very simple sketch which outputs 8 RC Channels and reads them right back in again.

The sketch can be used in three steps -

1) Initially the sketch outputs fixed values for the servos and reads them back in as a loop back test.

2) Once you are comfortable with the code, you can replace one or more of the loop back connections to start reading RC Channels. 

3) Once you are confident that the sketch is reading your channels correctly you can connect your servos and ESCs to the outputs and your done, 8 channels in, 8 channels out in three easy steps.


Step 1 - Loop back testing
To use the loop back test in step 1, connect pins 2 to 9 (the channel inputs) to pins 10 to 13 and A0 to A3 (the channel outputs).

Each channel is set to a fixed output from 1100 to 1800, if the code works on your board, your should see these values being output in the serial monitor.

Step 2 - RC Channel reading

Once you have this up and running, you can start swapping the connections to an RC receiver one by one. You should now see the channel values for the receiver channels updating in your serial monitor. If the values are within a range of around 1000 to 2000, your ready to move to step 3.

Step 3 - Full Control
To start outputting the values that you have read in, remove the comments from the servoName.writeMicroseconds functions to have full control of upto 8 servos/escs using 8 RC Receiver channels.

If it does not work, let me know which board you have, the code is easily adapted to work on any of the 8-bit Arduino boards. If you have a 32 bit Arduino Due, there is a dedicated post here -

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

For more performance and smoother output on the 8-bit Arduinos an RCArduinoFastLib versions will be added in separate in the coming days.

// RCArduino MultiChannel Loop back and servo ESC control for upto 8 RC channels
//
// rcarduino.blogspot.com
//
// A simple approach for reading three RC Channels using pin change interrupts
//
// See related posts -
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html
// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html
//
// rcarduino.blogspot.com
//

// include the pinchangeint library - see the links in the related topics section above for details
#include <PinChangeInt.h>

#include <Servo.h>

// Assign your channel in pins
#define CHANNEL1_IN_PIN 2
#define CHANNEL2_IN_PIN 3
#define CHANNEL3_IN_PIN 4
#define CHANNEL4_IN_PIN 5
#define CHANNEL5_IN_PIN 6
#define CHANNEL6_IN_PIN 7
#define CHANNEL7_IN_PIN 8
#define CHANNEL8_IN_PIN 9

// Assign your channel out pins
#define CHANNEL1_OUT_PIN 10
#define CHANNEL2_OUT_PIN 11
#define CHANNEL3_OUT_PIN 12
#define CHANNEL4_OUT_PIN 13
#define CHANNEL5_OUT_PIN A0
#define CHANNEL6_OUT_PIN A1
#define CHANNEL7_OUT_PIN A2
#define CHANNEL8_OUT_PIN A3

// Servo objects generate the signals expected by Electronic Speed Controllers and Servos
// We will use the objects to output the signals we read in
// this example code provides a straight pass through of the signal with no custom processing
Servo servoChannel1;
Servo servoChannel2;
Servo servoChannel3;
Servo servoChannel4;
Servo servoChannel5;
Servo servoChannel6;
Servo servoChannel7;
Servo servoChannel8;

// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define CHANNEL1_FLAG 1
#define CHANNEL2_FLAG 2
#define CHANNEL3_FLAG 4
#define CHANNEL4_FLAG 8
#define CHANNEL5_FLAG 16
#define CHANNEL6_FLAG 32
#define CHANNEL7_FLAG 64
#define CHANNEL8_FLAG 128

// holds the update flags defined above
volatile uint8_t bUpdateFlagsShared;

// 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 unChannel1InShared;
volatile uint16_t unChannel2InShared;
volatile uint16_t unChannel3InShared;
volatile uint16_t unChannel4InShared;
volatile uint16_t unChannel5InShared;
volatile uint16_t unChannel6InShared;
volatile uint16_t unChannel7InShared;
volatile uint16_t unChannel8InShared;

void setup()
{
  Serial.begin(115200);

  Serial.println("multiChannels");

  // attach servo objects, these will generate the correct
  // pulses for driving Electronic speed controllers, servos or other devices
  // designed to interface directly with RC Receivers
  servoChannel1.attach(CHANNEL1_OUT_PIN);
  servoChannel2.attach(CHANNEL2_OUT_PIN);
  servoChannel3.attach(CHANNEL3_OUT_PIN);
  servoChannel4.attach(CHANNEL4_OUT_PIN);
  servoChannel5.attach(CHANNEL5_OUT_PIN);
  servoChannel6.attach(CHANNEL6_OUT_PIN);
  servoChannel7.attach(CHANNEL7_OUT_PIN);
  servoChannel8.attach(CHANNEL8_OUT_PIN);

  // using the PinChangeInt library, attach the interrupts
  // used to read the channels
  PCintPort::attachInterrupt(CHANNEL1_IN_PIN, calcChannel1,CHANGE);
  PCintPort::attachInterrupt(CHANNEL2_IN_PIN, calcChannel2,CHANGE);
  PCintPort::attachInterrupt(CHANNEL3_IN_PIN, calcChannel3,CHANGE);
  PCintPort::attachInterrupt(CHANNEL4_IN_PIN, calcChannel4,CHANGE);
  PCintPort::attachInterrupt(CHANNEL5_IN_PIN, calcChannel5,CHANGE);
  PCintPort::attachInterrupt(CHANNEL6_IN_PIN, calcChannel6,CHANGE);
  PCintPort::attachInterrupt(CHANNEL7_IN_PIN, calcChannel7,CHANGE);
  PCintPort::attachInterrupt(CHANNEL8_IN_PIN, calcChannel8,CHANGE);

  // for loop back test only, lets set each channel to a known value
  servoChannel1.writeMicroseconds(1100);
  servoChannel2.writeMicroseconds(1200);
  servoChannel3.writeMicroseconds(1300);
  servoChannel4.writeMicroseconds(1400);
  servoChannel5.writeMicroseconds(1500);
  servoChannel6.writeMicroseconds(1600);
  servoChannel7.writeMicroseconds(1700);
  servoChannel8.writeMicroseconds(1800);
}

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 unChannel1In;
  static uint16_t unChannel2In;
  static uint16_t unChannel3In;
  static uint16_t unChannel4In;
  static uint16_t unChannel5In;
  static uint16_t unChannel6In;
  static uint16_t unChannel7In;
  static uint16_t unChannel8In;

  uint8_t bUpdateFlags = 0;
  // check shared update flags to see if any channels have a new signal
  // for nicely formatted serial output use this
  if(bUpdateFlagsShared == 0xFF)
  // for more responsive projects update any channels whenever a new signal is available using this
  // if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;
  
    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.
  

    if(bUpdateFlags & CHANNEL1_FLAG)
    {
      unChannel1In = unChannel1InShared;
    }
  
    if(bUpdateFlags & CHANNEL2_FLAG)
    {
      unChannel2In = unChannel2InShared;
    }
  
    if(bUpdateFlags & CHANNEL3_FLAG)
    {
      unChannel3In = unChannel3InShared;
    }

    if(bUpdateFlags & CHANNEL4_FLAG)
    {
      unChannel4In = unChannel4InShared;
    }
  
    if(bUpdateFlags & CHANNEL5_FLAG)
    {
      unChannel5In = unChannel5InShared;
    }
  
    if(bUpdateFlags & CHANNEL6_FLAG)
    {
      unChannel6In = unChannel6InShared;
    }
   
    if(bUpdateFlags & CHANNEL7_FLAG)
    {
      unChannel7In = unChannel7InShared;
    }
  
    if(bUpdateFlags & CHANNEL8_FLAG)
    {
      unChannel8In = unChannel8InShared;
    }
  
    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;
  
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }
 
  // do any processing from here onwards
  // only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared
  // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
  // the interrupt routines and should not be used in loop
  if(bUpdateFlags & CHANNEL1_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel1.writeMicroseconds(unChannel1In);
      Serial.println("");
      Serial.print(bUpdateFlags);
      Serial.print(",");
      Serial.print(unChannel1In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL2_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel2.writeMicroseconds(unChannel2In);
      Serial.print(unChannel2In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL3_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel3.writeMicroseconds(unChannel3In);
      Serial.print(unChannel3In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL4_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel4.writeMicroseconds(unChannel4In);
      Serial.print(unChannel4In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL5_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel5.writeMicroseconds(unChannel5In);
      Serial.print(unChannel5In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL6_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel6.writeMicroseconds(unChannel6In);
      Serial.print(unChannel6In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL7_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel7.writeMicroseconds(unChannel7In);
      Serial.print(unChannel7In);
      Serial.print(",");
  }
 
  if(bUpdateFlags & CHANNEL8_FLAG)
  {
      // when you are ready to add a servo or esc, remove the // from the following line to use the channel input to control it
      // servoChannel8.writeMicroseconds(unChannel8In);
      Serial.print(unChannel8In);
      Serial.print(",");
  }
}


void calcChannel1()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState) // this is equivalent to digitalRead(CHANNEL1_IN_PIN) but about 10 times faster
  {
    ulStart = micros();
  }
  else
  {
    unChannel1InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL1_FLAG;
  }
}

void calcChannel2()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel2InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL2_FLAG;
  }
}

void calcChannel3()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel3InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL3_FLAG;
  }
}

void calcChannel4()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel4InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL4_FLAG;
  }
}

void calcChannel5()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel5InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL5_FLAG;
  }
}

void calcChannel6()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel6InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL6_FLAG;
  }
}

void calcChannel7()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel7InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL7_FLAG;
  }
}

void calcChannel8()
{
  static uint32_t ulStart;
 
  if(PCintPort::pinState)
  {
    ulStart = micros();
  }
  else
  {
    unChannel8InShared = (uint16_t)(micros() - ulStart);
    bUpdateFlagsShared |= CHANNEL8_FLAG;
  }
}
Duane B


Thursday, April 11, 2013

The Problem ( and Solutions ) With Arduino Interrupts

If you have ever tried to port an Arduino project that uses interrupts from one board type to another, you have probably experienced frustration, this includes the sample code throughout RCArduino.


The following post examines the differences in interrupts between the popular boards, the Arduino UNO, Micro, Mega, Mini and Leonardo. Based on this information we can modify sketches to run on the full range of 8-bit Arduinos.

The 8-bitArduino boards are based on one of three related chips -

ATMega328 - UNO and Mini
ATMega32u4 - Leonardo and Micro
ATMega2560 - Mega

Each of these chips supports two types of interrupts -

1) External Interrupts
These are flexible easy to use interrupts which can be triggered by rising, falling or changing signals. The disadvantage is that there are a limited number available on each chip type.

If we want to access more interrupts we need to look at the next type -

2) Pin Change Interrupts
The underlying chip in your Arduino supports a second type of interrupt however these interrupts are not directly supported by Arduino and need to be accessed through an additional library.

Differences In External Interrupts

The external interrupts are associated with specific digital pins on each chip type, the following table taken from the attachInterrupt reference page lists the available external interrupts and the associated Arduino pin on each chip -


Boardint.0int.1int.2int.3int.4int.5
Uno, Ethernet,
Mini
23
Mega25602321201918
Leonardo, Micro3201


The Arduino team have hidden some of the differences between the ATMega328 and ATM2560 so that attaching INT0 using the attachInterrupt function will attach an interrupt to digital pin 2 on both chips even though on the Mega digital pin2 is actually INT4.

The same logic has not been carried across to the ATMega32u4 Based leonardo. Notice how int0 and int1 are actually reversed on the Leonardo, this will be a major trap for people who are porting code from the UNO.


Does the Leonardo have four external interrupts ?
While the Leonardo appears to have 4 external interrupts, int2 and int3 are attached to digital pins 0 and 1 which are almost always reserved for serial input/output. So yes there are four interrupts, but two of them are only available by disabling serial functionality.

Differences In Pin Change Interrupts
On the Arduino UNO, pin change interrupts can be used to enable interrupts on any of the Arduino PINs to give access to a total of 19 interrupts (13 digital pins and 6 Analog pins).

I initially assumed that this was also possible on the Mega, Micro and Leonardo as well. It isn't.

Pin change interrupts are supported on the following Leonardo/Micro pins - 8,9,10 and 11.

Pin change interrupts are supported on Arduino Mega pins 10,11,12,13,14,15 and analog pins 6 to 15

Interrupts and RCArduino
These differences between the Arduino platforms will have been responsible for some of the difficulty that users have had in porting RCArduino code to Minis, Micros, Leonardos and Megas.

The good news is that now we have a full understanding of the inconsistencies between the different devices there should be no problem in modifying the sample sketches to run on them.

If your having trouble with a sample sketch, get in touch, in the meantime I will be updating some of the sketches to work across multiple boards.

Stay tuned

Duane B