Hi, I was really happy to find a lot of hints in this thread to decoding the PPM-Stream of my receiver. I thank you all and would like to give some experience back to the community...
My first observation was some inconsistency of numbering the channels. From [font=Courier]ServoDecode.cpp[/font] we can find out that the servo are counted from 1 to a number defined by MAX_CHANNELS:
[font=Courier]for(byte chan = 1; chan <= MAX_CHANNELS; chan++)[/font]
...but, in the test sketch on page 3 the first for-statement start's at zero. So watch out and use the right numbering!
Moreover, the comparison of an older and a newer version by Beebee on page 9 of the "ISR(TIMER1_CAPT_vect)" function indicates an issue with the "else if"-statement.
The first (11-06-08):
[font=Courier]else if(Channel <= MAX_CHANNELS) { ... [/font]
The second (21-07-08):
[font=Courier]else if(Channel < MAX_CHANNELS) { ...[/font]
The declaration of "Channel" :
[font=Courier]static volatile byte Channel; // number of channels detected so far in the frame (first channel is 1)[/font],
indicates it goes from one to MAX, but at some points its reset to zero. Due to the pre-increment (Pulses[++Channel] =...) it seems not to make a difference, but it may be misleading this way... And I'm not sure about the "else if"-statemant (from above). I selected to let Channel start from zero (because I'm more used to it) and changed the code accordingly...
I'm using a DIY-Drones ARDUIMU to decode the PPM-stream. This board has only the digital-I/Os 10...13 (=PB2...PB5) accessible. Therefore, I have to use on of these pins. Instead of using the TIMER0 CAPT ISR the PCINT0 is used to measure the puls width. It's my first ISR so I hope I did it right, at least - it works for me ;-)
static volatile unsigned int tics; // holds tics from Timer1
...
ISR(PCINT0_vect)
{
tics = TCNT1; /* Read TCNT1 */
if ( LOW == digitalRead(PPM_PIN) ) //End of channel
{
TCNT1 = 0; //Reset Timer1
if(tics >= 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( (tics >= MIN_IN_PULSE_WIDTH) && (tics <= MAX_IN_PULSE_WIDTH) ){ // check for valid channel data
Pulses[Channel++] = tics / 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
} //ELSE IF
} //ELSE IF
} //IF
} //ISR
The PPM-signal is connected to PIN 10. The PORTB pin change interrupt is activated by
PCMSK0 = (1<<PCINT2); //Un-mask PCINT2
PCICR = (1<<PCIE0); //Enable the PORTB pin change interrupt
Last minor issue: Sometimes, when I connect the receiver and without activating the transmitter first, the state stays in "no_sync". Then, the ppm-signal remains at zero. But, this is not valid (e.g. when directly given to a servo). I like to have signals between MIN_IN_PULSE_WIDTH and MAX_IN_PULSE_WIDTH. Therefore, I've changed the "GetChannelPulseWidth( )"-method to give the failsafe values...
int ServoDecodeClass::GetChannelPulseWidth( uint8_t channel)
{
// this is the access function for channel data
int result = 0; // default value
if( channel < MAX_CHANNELS) {
if( ((State == FAILSAFE_state) || (State == NOT_SYNCHED_state)) && (Failsafe[channel] > 0 ) )
result = Failsafe[channel]; // return the channels failsafe value if set and State is Failsafe
else if( (State == READY_state) || (State == FAILSAFE_state) )
{
cli(); //disable interrupts
result = Pulses[channel] ; // return the last valid pulse width for this channel
sei(); // enable interrupts
}
}
return result;
}
I hope this helps somebody!? Best regards,
Sven