Go Down

Topic: Decoding Radio Control signal pulses (Read 73955 times) previous topic - next topic

mem

Quote
The power consumption is very low (60mA) and servos small (5g) ...


I thought it was the digital servo that caused the problem, can you post a link to the servos specifications

I don't know of any servo that only draws 60mA when moving.

BTW, did you try using decoupling capacitors on the power supply?


loris

60mA is when servos are idle. It is very small servos (http://www.hobbycity.com/hobbycity/store/uh_viewItem.asp?idProduct=9904&Product_Name=Blue_Arrow__3.9g_/_.18kg_/_.10sec_High_Speed_Micro_Servo)

I thought they were digital but they look to be analog. (I am not sure of the reference because the label had been erased).

I don't have a big capacitor, I will try to get one this week end.

mem

That servo seems to be designed for 3.7 volt nominal opertion. According to one reviewer on that site, the servo should not be connected to 5 volts.

I suggest you try some tests to verify if the problem is caused by those servos running on 5 volts. either try using standard servos or try them with a 3.3v power supply.

I suggest you create a new thread to continue this discussion so that this thread can focus on providing  general adice on the library.
Post a link to your thread here so I and others interested can find it.

walterwangus

mem,

Thanks for the ServoDecode work. It works great for me. I was reading the old post on the evolution/development of the servodecode.c to get a better understanding (I'm new to AVR programming). There was a point where the toggling code went away :

         TCCR1B ^= _BV(ICES1);         // toggle bit value to trigger on the other edge.

I can't figure out how  the current code resets TCNT1 to 0 when a rising edge(start of next channel pulse) occurs. I appreciate your help, thanks.



mem

#124
Jun 09, 2010, 10:05 am Last Edit: Jun 09, 2010, 10:08 am by mem Reason: 1
Hi Bebee,

The code was changed to make it easy to select between pulses that start on rising or falling edges.

The  pulseEnd variable is defined by the user to determine the pulse start (it will be set to 0 if starting on a rising edge, or  (1<<ICES1) if starting on a falling edge

Te following line in the ISR determines if the interrupt was caused by a pulse start or pulse end and if it's the end then TCNT1 is set to 0 ready to start measuring the next pulse duration.
Code: [Select]
if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == pulseEnd ){         
   TCNT1 = 0;       // reset the counter


the code is slightly more cryptic than the previous version but has the advantage that inverted pulses can be handled.

walterwangus

Mem,

Thanks for the response. I'm still a bit confused. I'm comparing two "ISR(TIMER1_CAPT_vect)" functions that you had posted previously one after the other. The first (11-06-08) :

Code: [Select]

ISR(TIMER1_CAPT_vect)
{
 // we want to measure the time to the end of the pulse
 if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == pulseEnd ){
   TCNT1 = 0;               // reset the counter
     if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
     processSync();
   }    
   else if(Channel <= MAX_CHANNELS) {  // else its channel pulse so save it
      Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
   }
 }
 else {        // the start of the pulse was detected  
   TCNT1 = 0;        // so reset the counter to start timing
     if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC    
     processSync();
     }
   if( State != READY_state && (ICR1 >= MIN_IN_PULSE_WIDTH) ){   // if the 'off' pulse is greater than minimum pulse, we need to invert the signal test
     pulseEnd ^= 1; // invert the pulse mode
     State = NOT_SYNCHED_state; //reset the state to start measuring from channel 1
   }
 }
 TCCR1B ^= _BV(ICES1);         // toggle bit value to trigger on the other edge
}


This code was modified next to (21-07-08):
Code: [Select]

ISR(TIMER1_CAPT_vect)
{
 // we want to measure the time to the end of the pulse
 if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == pulseEnd ){  
   TCNT1 = 0;       // reset the counter
   if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
     processSync();
   }    
   else if(Channel < MAX_CHANNELS) {  // check if its a valid channel pulse and save it
     if( (ICR1 >= MIN_IN_PULSE_WIDTH)  && (ICR1 <= MAX_IN_PULSE_WIDTH) ){ // check for valid channel data
       Pulses[++Channel] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
     }
     else if(State == READY_state){
       State = FAILSAFE_state;  // use fail safe values if input data invalid  
       Channel = 0; // reset the channel count
     }
   }
 }
}




Both versions began with :
Code: [Select]

Code:
if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == pulseEnd ){  
   TCNT1 = 0;       // reset the counter


However only the earlier version had the line :
Code: [Select]

TCCR1B ^= _BV(ICES1);         // toggle bit value to trigger on the other edge.


Looks like this line is needed to reset TCNT1 to 0 every time a falling edge and leading edge is detected so that it can set TCNT1 to 0 at the start of a pulse and get ICR1 at the end of a pulse.


From my understanding, the ICES1 in TCCR1B never changes once initialized in the begin(). Therefore (in default case) the beginning if-statement that tests for pulseEnd will only enter if a falling edge is detected.  And at that point, set TCNT1 = 0.  But i'm still confused as to where is TCNT1 set to 0 when a rising edge is detected? It looks like there is only 1 line in the entire code where TCNT1 is reset  to 0(or set to anything).

Thanks again mem.

mem

#126
Jun 09, 2010, 06:12 pm Last Edit: Jun 09, 2010, 06:13 pm by mem Reason: 1
The  older code was incorrectly resetting TCNT1 on both the mark (the length of the high pulse) and the space (the length of the low pulse). This worked on receivers that used a very short space interval.  But correct PPM decoding measures the time between transitions of the same edge - the sum of the mark and space. So TCNT1 only needs to be reset on one edge, as in the later code.

Here is an example of four PPM chanels as received followed by the four decoded servo pulses

Code: [Select]
/^^^^^^\_/^^^^^^\_/^^^^\_/^^^^^^^\_________________

/^^^^^^^^\______________________________________
_________/^^^^^^^^^\______________________________
___________________/^^^^^\_________________________
_________________________/^^^^^^^^^\________________

walterwangus

That clarifies everything for me. Thanks mem.

gmv

@mem,

Hi mem, just a dumb question....is there any chance that you could add some kind of F/S feature in to the "ServoDecode.cpp " by pressing a button  connected to the decoder that could store the selected servos positions in to the Atmega eeprom  and when there was no PPM frame sync it would load these pre-selected positions back to the servos again ?  ::)

thanks - gmv

mem

@gmv

The library has functions to set failsafe values to the current servo positions or to any given position.  You could add functionality in your sketch to detect a button press and read the values for all the channels and save these in EEPROM as default failsafe values.
At startup you set the failsafe for all the channels from the EEPROM values.

AlexanderB

Thank you mem, for the great routines.

I am new to it and I expect to get my Nano Board V3 very soon.

Will your Servo Decoder work on the Nano Board?
Is there a newer version available?

Thank you

Alexander

mem

The code in the start of this thread is the latest version for the 168 and 328 chips and should work fine with the Nano.

Please tell us about your project when you have the nano board and have connected everthing up.

AlexanderB

#132
Jul 05, 2010, 07:42 pm Last Edit: Jul 05, 2010, 07:43 pm by AlexanderB Reason: 1
Thank you. I will post information about this project. Also because it's my first and I sure will have tons of questions.

So much for here: I will program am motor controller for my Douglas DC-6 model (100" span / 2,5m). I want to use two channels to control the four motor ESC: first start all motors one after the other in the order 3-4-2-1 then use channel 1 to control all four ESC like a V-cable. Then - after landing - use only motors 2 and 3 to taxi to the park position. I hope that I get this done. It will save me two precious channels on my TX.

Cheers Alexander

mem

Hi Alexander, that sounds like an interesting and very do-able project. I will look forward to following your progress - the DC-6 was the first airliner that I flew in, although I don't remember much about it, I was six months old.

AlexanderB

Thank you mem

One thing you may give me some advice about before I start the project:

What of course concerns me most is the reliability. I heard a lot about brown outs, watchdogs and stuff. What I have to make sure ist that the motor controller will work flawless. And if it quits working it must recover.

Is stability an issue with Arduino or can I rely on that (given a flawless working software)? Is there a situation possible where I cannot control my motors anymore?

Cheers Alexander

Go Up