Go Down

Topic: Arduino Helicopter Autopilot (Read 10 times) previous topic - next topic

aercam

So am I. MEM, your library has been working very well for me.

Jim

Ed Simmons

Hi,

I wrote the following code because I had looked (very briefly) for something similar and found nothing of any use so I coded a system myself.

The code I wrote for reading the pulses in PCM is as follows:

/*
PCM reading code for Arduino, by Ed Simmons
The aim of this code is to capture all the channels from an RC receiver on one pin.

Inside your TX, the control positions are coded together into a sequence of pulses(PCM), the rising edge of each pulse being the parts of the signal that convey the information.
This is the modulated with the carrier signal for transmission, when received by the RX it is de-modulated (the signal at this stage is the same PCM as in the TX - this is what we are interested in!)
before being sent to a some demux circuitry (usually very basic)

Find the pcm in your RX with a scope, verify you have the right place by adjusting the controls on the TX and looking for the changes in the pulse timings, solder a wire onto this point,
connect this to the arduino and off we go.

Theoretically, this code should be able to use any number of channels available on your radio gear, ie you need not specify that your 7ch set is 7ch if you only need 5ch... the
extra pulses will simply be ignored in the process of finding the framing period. However, the pulses must be sequential and starting immediately after the frame period.
*/

# define NUM_CHANNELS 4 // the number of channels we are receiving
# define FRAME 4000 // the length of the framing pause from the RC rx
int rcPin = 2;      // the pin that the pcm is connected to


extern volatile unsigned long timer0_overflow_count = 0; // variable to store timer overflows...
unsigned long lastReadRC = 0;  
unsigned long hptime = 0;
unsigned long time = 0;
unsigned long channel[NUM_CHANNELS] = {};
unsigned long on =0;
unsigned long lastWriteServos = 0;

unsigned long hpticks (void)
{
 return (timer0_overflow_count << 8) + TCNT0;
// return TCNT0;
}

void setup()
{
 Serial.begin(9600);
 pinMode(rcPin, INPUT);
 pinMode(19, OUTPUT);
}

void loop()
{

 if(millis() - lastReadRC >= 100)
  {
//     Serial.print("hpticks*4 = ");Serial.println(hpticks()*4); // debug - test timers (will print time in microseconds since switched on every 100ms)
   
//   mstime = millis();
  while(!digitalRead(rcPin) == HIGH) // waits for signal to go high
   {
     continue;
   }
    hptime = hpticks()*4; //When the signal arrives, record the start time (in microseconds)
  // mstime = millis();
 
   while(!digitalRead(rcPin) == LOW) //Waits for signal to go low
   {
     continue;
   }  
   while(!digitalRead(rcPin) == HIGH) // waits for signal to go high
   {
     continue;
   }
    time = (hpticks()*4) - hptime;  //Here takes the differences of the start and finish times, the result is the length of the last off period.
   
   if(time >= 5000) // if the last off period was the frame period - we are at the start of the sequence
  {
   //    Serial.println(time);
     for(int i = 0; i < NUM_CHANNELS;i++){    // step through the channels from 0 to 1 less than you specify in NUM_CHANNELS
      channel = hpticks()*4;         // record the start time for this channel
 
      while(!digitalRead(rcPin) == LOW) //Waits for signal to go low
       {
        continue;
       }
       
      while(!digitalRead(rcPin) == HIGH) //Waits for signal to go high  
       {
        continue;
       }
     
       channel = hpticks()*4 - channel; // subtract the start time from the end time to get the duration
    }
   hptime = 0;
   lastReadRC = millis();
    time=0;
   Serial.print("Results! 1= ");Serial.print(channel[0]);Serial.print(" 2= ");Serial.print(channel[1]);Serial.print(" 3= ");Serial.print(channel[2]);Serial.print(" 4= ");Serial.println(channel[3]);
   
  }
 }
}



I hope this proves to be of some use to people, I'm looking into making the needed changes to enable a failsafe action.

mem

#12
Jan 03, 2009, 12:28 pm Last Edit: Jan 03, 2009, 12:36 pm by mem Reason: 1
Hi Ed, thanks for posting.

The advantage of your implementation over the ServoDecode library linked  in post #9 above is that it doesn't grab timer1, so PWM outputs on pins 9 and 10 are available.  But your code has some drawbacks that may limit its use.

Your decoding is done in software  so the application can't do anything else while waiting for the channel pulses. There may only be three or four milliseconds between frames so 80% of the available processing time is not available for running application code.  ServoDecode does all the pulse counting in hardware using Timer1 so practically all of the available processing power is available to run the application.

Also, it appears that the code above is measuring to an accuracy of 4 hpticks. I think each hptick is 4 microseconds so your accuracy is to 16 microseconds, which would give around 64 discrete steps between 1ms and 2ms, this is less than the resolution of the rc systems I have looked at. TheServoDecode library clocks timer1 at 0.5microseconds which is 8 times faster than an hptick, 64 times higher resolution than the 4xhptick code.

Other advantages of ServoDecode are that it automatically detects positive or negative going pulses and the number of channels. It has failsafe supporting preset or last good value. And it s library that is easy to integrate into an application.

I don't want to discourage you in enhancing your code, but knowing what else is out there may help you decide where best to apply your efforts.

Have fun!

Ed Simmons

Re timings etc, the RC pulse train is only read every 100ms, which is fine for the moment (it's causing no problems in testing) and this bit of code runs plenty fast enough for the moment.

I would like to look into using the other timer - this has proved perfectly reliable so far, but the increase in resolution would be nice.

I'll look into it.

Cheers,
Ed

jordi

#14
Jan 06, 2009, 09:05 pm Last Edit: Jan 06, 2009, 09:10 pm by jordi Reason: 1
Nice job,  i didn't have time to read all the comments, but i like your work =), please keep going...

check this out:
http://diydrones.com/profiles/blogs/705844:BlogPost:38393

But i suggest to use external interrupts with a hardware timer to count the pulses... Without wasting CPU power, the secret is never use pulseIn and delays, NEVER!  ::)

I been working on ArduCopter V2, using better Kalman Filters and a real accelerometer (no more Wii).. It uses zero delays and is very efficient... I also rewrite the Labview grounds station.. I want to use it also to mount it in the new helicopters without fly bar...

But i'm busy here doing other tasks...  

Take care.

Go Up