Pages

Thursday, April 25, 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.

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.

// 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 processingServo servoChannel1;Servo servoChannel2;Servo servoChannel3;Servo servoChannel4;Servo servoChannel5;Servo servoChannel6;Servo servoChannel7;Servo servoChannel8;

// 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 signalsvolatile 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;

// 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 & 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); }

Wednesday, April 24, 2013

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 -

// 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 processingServo servoChannel1;Servo servoChannel2;Servo servoChannel3;Servo servoChannel4;Servo servoChannel5;Servo servoChannel6;Servo servoChannel7;Servo servoChannel8;

// 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 signalsvolatile 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;

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.

// 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(","); }}

Thursday, April 11, 2013

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.

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 -

Board

int.0

int.1

int.2

int.3

int.4

int.5

Uno, Ethernet,
Mini

2

3

Mega2560

2

3

21

20

19

18

Leonardo, Micro

3

2

0

1

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.

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.