Go Down

Topic: RC radio interface troubles... (Read 4530 times) previous topic - next topic

RyanVegh

Hi, I've been trying to get arduino to seamlessly receive signals from a 2.4ghz RC receiver, and  pass them through to RC servo's, trying to diminish latency as much as possible. my first few attempts were effective, but not good enough. I'm trying to fly an rc heli with it, and the latency was just too much to be able to still fly withing reasonable controll.
so, I found a piece of code to significantly increase the program's speed using interrupts to receive the radio signal, and it works fantastically. once I remember where I got the code and who the author was I will give bunches of credit. there's one problem though, every few seconds all servo's freeze up and do something erratic, when arduino is powered by usb. when powered with ANY external power supply, nothing works and the servo's jitter about whilst not responding to radio signals. please help!
the code below is just the part that does the radio and servo's, in between goes some other stuff, and I would like to be able to tell arduino to go ahead and update the servo's every 30ms, and do everything else in between. but I don't know how. also, any explanation on the code that reads the radio signal will be greatly appreciated, because I don't understand most of it. it looks rather assemblieish....(that's probably not a word).
thanks everyone!!

code:
Code: [Select]

#define icpPin            8         // this interrupt handler must use pin 8
#define TICKS_PER_uS      2          // number of timer ticks per microsecond
#define MAX_CHANNELS    8         // maximum number of channels we can store  
#define SYNC_GAP_LEN      (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)
volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)
volatile uint8_t State;         // this will be one of the following states:
#define NOT_SYNCHED_state  0    // the system is not synched so the data is random
#define ACQUIRING_state  1      // one sync pulse detected but not all channels have been received
#define READY_state     2       // synched and all channel data is valid


int servo[] = {2,3,4,5,6,7};
int lastPulse[6][6];

int pulsewidth;
int time = 0;

ISR(TIMER1_CAPT_vect){
  if(! bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
      TCNT1 = 0;               // reset the counter
      if(Channel <= MAX_CHANNELS) {
          Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
       }
  }
  else {                          // rising  edge was detected  
       TCNT1 = 0;               // reset the counter
       if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
           Channel = 1;       // if so, reset the channel counter to 1
             if(State == NOT_SYNCHED_state)
                 State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
             else if( State == ACQUIRING_state)    
                  State = READY_state;           // this is the second sync so flag that channel data is valid
       }    
  }    
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge    
}

void setup()                    // run once, when the sketch starts
{

//
 pinMode(icpPin,INPUT);
 Channel = 1;
 State = NOT_SYNCHED_state;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
 
 


 for ( int i =1; i <=6; i++ ){
     pinMode(servo[i], OUTPUT); // Set servo pin as an output pin
}
}

int GetChannelPulseWidth( uint8_t channel) {
 // this is the access function for channel data
 int result;  
 if( (State == READY_state)  && (channel > 0) && (channel <=  MAX_CHANNELS)  ) {
    cli();             //disable interrupts
    result =  Pulses[channel] ;
    sei();             // enable interrupts
 }
 else
    result = 0;        // return 0 if no valid pulse is available  

 return result;

}


void loop()                     // run over and over again
{
 for ( int i =1; i <=6; i++ ){
   pulsewidth = GetChannelPulseWidth(i);
   digitalWrite(servo[i], HIGH); // Turn the motor on
   delayMicroseconds(pulsewidth); // Length of the pulse sets the motor position
   digitalWrite(servo[i], LOW); // Turn the motor off
  }
 
}

Grumpy_Mike

Quote
though, every few seconds all servo's freeze up and do something erratic, when arduino is powered by usb. when powered with ANY external power supply, nothing works and the servo's jitter about whilst not responding to radio signals.


So it works but a bit iffy on USB power but doesn't work at all on any other power source. I think you need to fix this first. There is no software that can distinguish between USB power and external power so there has to be a problem with your external supply. Things that could be wrong:-
1) Ground not connected
2) Unregulated power being f4ed into 5V and not Vin
3) Insufficient current from external power supply.
4) Too much ripple on the external supply.
5) Insufficient voltage on external supply.

When you have got the USB power and external power acting the same then you can look at tackling you main problem of intermittent operation. Here i would look towards you power supply decoupling first, especially as you have motors in the mix.
http://www.thebox.myzen.co.uk/Tutorial/De-coupling.html

retrolefty

#2
Mar 24, 2010, 10:25 am Last Edit: Mar 24, 2010, 10:27 am by retrolefty Reason: 1
How many servos are you operating through the Arduino? I too think it's most likely a power capacity problem. The USB can supply about 500ma max, and external power if going through the on-board +5vdc regulator might be even having trouble matching the USB's current capacity.

It's best to power servos with an independent source of regulated +5vdc rather then using the +5vdc pin on the Arduino, and at I like to have lots of reserve current capacity when powering servos, one amp per servo is not really overkill.

Lefty

RyanVegh

I have written several other chunks of code using the exact same setup to make the radio + servo's work, and with the other bit's of code it works just fine.

it's just the above code that doesn't work on external power.
I have tried a number of supplies and nothing works.

the servo's are powered always from a separate  source, and share only ground and signal with arduino. there's 4 servo's to be controlled.

I'm thinking (but what do I know) that the problem might have something to do with that arduino is connected with and communicating with a pc, when hooked up to usb, and this ofcourse stops when unplugging the usb. but nothing as far as I understand in the code is communicating with anything but servo's and radio.

and to further explain the problem, the code works great for about the first 10 seconds, then all servo's freak out, then I get 8 seconds(approximately), then like 4 before they freak out, and that interval keeps on varying continously. but never get's smaller then about 4 seconds, sometimes longer as in 20 seconds or so. but usually not.

Grumpy_Mike

Quote
it's just the above code that doesn't work on external power.


Sorry but that is just a nonsense, you can't get code that runs with USB power and not with external power, even if you wanted that to happen, so getting it accidentally is plane impossible.

It could be that you can't get the system to work with external power but this then has nothing to do with the code and everything to do with how you wire it up, mainly down to grounds or the problems I have talked about already.

RyanVegh

I will try and do some power supply decoupling and see if that works. I really hope that's the problem.

in the mean time, (i'm at work and won't have a chance to try this untill after), it would be fantastic if someone could explain a bit more of the code that handles the interrupt for the radio, I really want to understand it...

GrooveFlotilla

Quote
it would be fantastic if someone could explain a bit more of the code that handles the interrupt for the radio


Do you understand how the servos signals are muxed?
Some people are like Slinkies.

Not really good for anything, but they bring a smile to your face when pushed down the stairs.

RyanVegh

I understand this part:

Code: [Select]

void loop()                     // run over and over again
{
 for ( int i =1; i <=6; i++ ){
   pulsewidth = GetChannelPulseWidth(i);
   digitalWrite(servo[i], HIGH); // Turn the motor on
   delayMicroseconds(pulsewidth); // Length of the pulse sets the motor position
   digitalWrite(servo[i], LOW); // Turn the motor off
  }

}




The rest is all a bit hazy to me...the interrupt stuff...

also, and this is an extension of the topic, when the servo's are directly hooked up to the radio receiver, they all move super smooth without any jitter. when arduino forwards the signals, all of the sudden there's a +/-7uS fluctuation in the signals, which causes jittery servo movement.

Anybody know why that is and if it's fixable? I tried to smooth the signal by averaging a number of signals, which gets rid of the jitter, but causes too much latency.

p.s. this has happened with every bit of code I've tried to read the radio signals with, and with every transmitter/receiver combination I've tried. I have tried 2.4ghz and 72mhz gear of several manufacturers, the problem's Identical with all...

GrooveFlotilla

#8
Mar 25, 2010, 10:29 am Last Edit: Mar 25, 2010, 10:31 am by GrooveFlotilla Reason: 1
The interrupt section simply uses the timer capture to time individual positive pulses from the receiver.
The pulse length ("ICR1/TICKS_PER_uS" is stored to the "Pulses" array when the capture module detects the falling edge of a pulse.
On a rising edge, if the counter (ICR1) is greater than the sync length, then the "Pulses" index "Channel" is reset (oddly, to one, not zero, hmmm).
The capture module interrupt alternates (toggles) between rising and falling edges.

If you want to know more about the capture module, see the AVR datasheet.

Your jitter is due to quantisation in "delayMicroseconds", I think.
I doubt it is easily fixable.
Some people are like Slinkies.

Not really good for anything, but they bring a smile to your face when pushed down the stairs.

Go Up