Go Down

Topic: Changing signal input pins with a signal value from another pin (R/C) (Read 341 times) previous topic - next topic

thomas3120

Hello,
Using Duane's excellent 'Read multiple RC channel' code and need to make a few changes.
The default input pins shown here in the beginning of the code:
Code: [Select]

#define THROTTLE_IN_PIN 5  //Manual input throttle from receiver
#define STEERING_IN_PIN 6  //Manual input steering from receiver
#define AUX_IN_PIN 7       //Manual toggle between autonomous and manual mode from receiver

...are pins 5, 6 and 7.

Pin 7 or AUX_IN is a 2 position toggle on my R/C transmitter.  In one position it reads about 1080 and the other position it reads about 1900.

I would like to use this toggle switch to change the input pins above by adding two more input pins for Throttle_In and Steering_In.
Doing this with an 'if' statement:
Code: [Select]

if(AUX_IN < 1350)
//keep THROTTLE_IN_PIN 5 (manual mode)
//keep STEERING_IN_PIN 6 (manual mode)
//....


Here is what I changed at the beginning of the code:
Code: [Select]

#include <PinChangeInt.h>

#include <Servo.h>

// Assign your channel in pins
#define Aut_THROTTLE_IN_PIN 3  //Autonomous input throttle from PC  (ADDED this)
#define Aut_STEERING_IN_PIN 4  //Autonomous input steering from PC   (ADDED this)
#define THROTTLE_IN_PIN 5  //Manual input throttle from receiver
#define STEERING_IN_PIN 6  //Manual input steering from receiver
#define AUX_IN_PIN 7       //Manual toggle between autonomous and manual mode from receiver



And where I added the 'if' statement:
Code: [Select]

void setup()
{
 Serial.begin(9600);
 
 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  
 servoThrottle.attach(THROTTLE_OUT_PIN);
 servoSteering.attach(STEERING_OUT_PIN);
 servoAux.attach(AUX_OUT_PIN);

 // using the PinChangeInt library, attach the interrupts
 // used to read the channels

//Here is where I made some changes using the 'if' statement

PCintPort::attachInterrupt(AUX_IN_PIN, calcAux,CHANGE);
 if(AUX_IN_PIN < 1350)
 {
   PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
   PCintPort::attachInterrupt(STEERING_IN_PIN, calcSteering,CHANGE);
 }
 if(AUX_IN_PIN > 1750)
 {
   PCintPort::attachInterrupt(Aut_THROTTLE_IN_PIN, calcThrottle,CHANGE);
   PCintPort::attachInterrupt(Aut_STEERING_IN_PIN, calcSteering,CHANGE);  
 }
}


*My coding problem:
In the serial monitor I still get values (don't have input from the PC yet) no matter what position the AUX toggle switch is in.


Any suggestions or help appreciated,

t

thomas3120

Here is the entire code:
Code: [Select]

// 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 Aut_THROTTLE_IN_PIN 3  //Autonomous input throttle from PC
#define Aut_STEERING_IN_PIN 4  //Autonomous input steering from PC
#define THROTTLE_IN_PIN 5  //Manual input throttle from receiver
#define STEERING_IN_PIN 6  //Manual input steering from receiver
#define AUX_IN_PIN 7       //Manual toggle between autonomous and manual mode from receiver

// Assign your channel out pins
#define THROTTLE_OUT_PIN 8  //Will always be the output pin (to the motor driver pin S1)
#define STEERING_OUT_PIN 9  //Will always be the output pin (to the motor driver pin S2)
#define AUX_OUT_PIN 10      // AUX_OUT is not needed

// 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 servoThrottle;
Servo servoSteering;
Servo servoAux;

// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define THROTTLE_FLAG 1
#define STEERING_FLAG 2
#define AUX_FLAG 4

// 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 unThrottleInShared;
volatile uint16_t unSteeringInShared;
volatile uint16_t unAuxInShared;

// 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 ulThrottleStart;
uint32_t ulSteeringStart;
uint32_t ulAuxStart;

void setup()
{
 Serial.begin(9600);
 
 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  
 servoThrottle.attach(THROTTLE_OUT_PIN);
 servoSteering.attach(STEERING_OUT_PIN);
 servoAux.attach(AUX_OUT_PIN);

 // using the PinChangeInt library, attach the interrupts
 // used to read the channels
 PCintPort::attachInterrupt(AUX_IN_PIN, calcAux,CHANGE);
 if(AUX_IN_PIN < 1350)
 {
   PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
   PCintPort::attachInterrupt(STEERING_IN_PIN, calcSteering,CHANGE);
 }
 if(AUX_IN_PIN > 1750)
 {
   PCintPort::attachInterrupt(Aut_THROTTLE_IN_PIN, calcThrottle,CHANGE);
   PCintPort::attachInterrupt(Aut_STEERING_IN_PIN, calcSteering,CHANGE);  
 }
}
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;
 // local copy of update flags
 static uint8_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 & THROTTLE_FLAG)
   {
     unThrottleIn = unThrottleInShared;
   }
   
   if(bUpdateFlags & STEERING_FLAG)
   {
     unSteeringIn = unSteeringInShared;
   }
   
   if(bUpdateFlags & AUX_FLAG)
   {
     unAuxIn = unAuxInShared;
   }
   
   // 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
   
   
   Serial.print("ThrottleIn");
   Serial.println(unThrottleIn);
   Serial.print("SteerIn");
   Serial.println(unSteeringIn);
   Serial.print("AuxIn");
   Serial.println(unAuxIn);
   Serial.println("");
   delay(1000);
 
 
 // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
 // the interrupt routines and should not be used in loop
 
 // the following code provides simple pass through
 // this is a good initial test, the Arduino will pass through
 // receiver input as if the Arduino is not there.
 // This should be used to confirm the circuit and power
 // before attempting any custom processing in a project.
 
 // we are checking to see if the channel value has changed, this is indicated  
 // by the flags. For the simple pass through we don't really need this check,
 // but for a more complex project where a new signal requires significant processing
 // this allows us to only calculate new values when we have new inputs, rather than
 // on every cycle.
 if(bUpdateFlags & THROTTLE_FLAG)
 {
   if(servoThrottle.readMicroseconds() != unThrottleIn)
   {
     servoThrottle.writeMicroseconds(unThrottleIn);
   }
 }
 
 if(bUpdateFlags & STEERING_FLAG)
 {
   if(servoSteering.readMicroseconds() != unSteeringIn)
   {
     servoSteering.writeMicroseconds(unSteeringIn);
   }
 }
 
 if(bUpdateFlags & AUX_FLAG)
 {
   if(servoAux.readMicroseconds() != unAuxIn)
   {
     servoAux.writeMicroseconds(unAuxIn);
   }
 }
 
 bUpdateFlags = 0;
}


// simple interrupt service routine
void calcThrottle()
{
 // if the pin is high, its a rising edge of the signal pulse, so lets record its value
 if(digitalRead(THROTTLE_IN_PIN) == HIGH)
 {
   ulThrottleStart = 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.
   unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
   // use set the throttle flag to indicate that a new throttle signal has been received
   bUpdateFlagsShared |= THROTTLE_FLAG;
 }
}

void calcSteering()
{
 if(digitalRead(STEERING_IN_PIN) == HIGH)
 {
   ulSteeringStart = micros();
 }
 else
 {
   unSteeringInShared = (uint16_t)(micros() - ulSteeringStart);
   bUpdateFlagsShared |= STEERING_FLAG;
 }
}

void calcAux()
{
 if(digitalRead(AUX_IN_PIN) == HIGH)
 {
   ulAuxStart = micros();
 }
 else
 {
   unAuxInShared = (uint16_t)(micros() - ulAuxStart);
   bUpdateFlagsShared |= AUX_FLAG;
 }
}

thomas3120

#2
Nov 10, 2012, 10:31 pm Last Edit: Nov 10, 2012, 10:33 pm by thomas3120 Reason: 1
Hmm, looking at the code a bit more (toward the bottom) I may have to add/change something here (as it has THROTTLE_IN_PIN, STEERING_IN_PIN and AUX_IN_PIN).

Code: [Select]

// simple interrupt service routine
void calcThrottle()
{
 // if the pin is high, its a rising edge of the signal pulse, so lets record its value
 if(digitalRead(THROTTLE_IN_PIN) == HIGH)
 {
   ulThrottleStart = 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.
   unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
   // use set the throttle flag to indicate that a new throttle signal has been received
   bUpdateFlagsShared |= THROTTLE_FLAG;
 }
}

void calcSteering()
{
 if(digitalRead(STEERING_IN_PIN) == HIGH)
 {
   ulSteeringStart = micros();
 }
 else
 {
   unSteeringInShared = (uint16_t)(micros() - ulSteeringStart);
   bUpdateFlagsShared |= STEERING_FLAG;
 }
}

void calcAux()
{
 if(digitalRead(AUX_IN_PIN) == HIGH)
 {
   ulAuxStart = micros();
 }
 else
 {
   unAuxInShared = (uint16_t)(micros() - ulAuxStart);
   bUpdateFlagsShared |= AUX_FLAG;
 }
}


Possibly something along the lines of using an 'or' statement?:
Code: [Select]

if(digitalRead(THROTTLE_IN_PIN) || (Aut_THROTTLE_IN_PIN) == HIGH)
  //....

robtillaart

(don't understand the code but recognize some patterns in it :)
AUX_IN_PIN is a constant with the value of 7 so
Code: [Select]
  if(AUX_IN_PIN < 1350)
  {
    PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
    PCintPort::attachInterrupt(STEERING_IN_PIN, calcSteering,CHANGE);
  }
  if(AUX_IN_PIN > 1750)
  {
    PCintPort::attachInterrupt(Aut_THROTTLE_IN_PIN, calcThrottle,CHANGE);
    PCintPort::attachInterrupt(Aut_STEERING_IN_PIN, calcSteering,CHANGE); 
  }

will always be the same

what you might mean to do is using - unAuxInShared - which can be higher...

Code: [Select]
  if(unAuxInShared < 1350)
  {
    PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
    PCintPort::attachInterrupt(STEERING_IN_PIN, calcSteering,CHANGE);
  }
  if(unAuxInShared > 1750)
  {
    PCintPort::attachInterrupt(Aut_THROTTLE_IN_PIN, calcThrottle,CHANGE);
    PCintPort::attachInterrupt(Aut_STEERING_IN_PIN, calcSteering,CHANGE); 
  }
and you need to detach the irq somewhere too
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

DuaneB

Hi,
  I would not use attach and dettach interrupt, instead I would just choose which signal source to use at the point of reading the signal, this way you can toggle between the two sources as run time.

EDIT: Note - this approach depends on a big assumption which is that both signal sources are delivering a servo style signal, if your PC is sending instrutcions in some other format a slightly different approach will be required. I will address both cases in a blog post later today or tomorrow.

You gain -

The ability to change sources at run time,
Avoid memory leaks through attaching/detaching interrupts

You lose -
Some efficiency

Once you project is bedded down you can regain the efficiency by turning the interrupt sources on and off at the interrupt mask level to toggle modes. Don't do this yet though get the projected bedded down and then we will tackle enabling/disabling the interrupts at run time.

I will try and add a blog post tonight on the suggest approach your not the only one asking about this.

Duane B

Code: [Select]

 // set of clear the bUsePC flag to select which source we take our throttle and steering signals from

 // 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(bUsePC)
   {
    if(bUpdateFlags & THROTTLE_FLAG_PC)
    {
      unThrottleIn = unThrottleInSharedPC;
    }
   
    if(bUpdateFlags & STEERING_FLAG_PC)
    {
      unSteeringIn = unSteeringInSharedPC;
    }
   }
   else
   {
    if(bUpdateFlags & THROTTLE_FLAG)
    {
      unThrottleIn = unThrottleInShared;
    }
   
    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }
   }
...
   


Read this
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

DuaneB

Hi,

I am in Ethiopia at the moment, so no blog posts, but i did discuss the problems with using attach and detach in the pinchangeint library with greygnome who was able to confirm the issue. I have suggested a fix which is to enable or disable the interrupt masks at a hardware level so that no interrupts are generated on a disabled pin, but the same memory will be reused if the interrupt gets reattached.

You can follow the history and progress here -

[urlhttp://arduino.cc/forum/index.php/topic,87195.0.html][/url]

Duane B

rcarduino.blogspot.com

Read this
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

Go Up