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
ServoDecode.cpp we can find out that the servo are counted from 1 to a number defined by MAX_CHANNELS:
for(byte chan = 1; chan <= MAX_CHANNELS; chan++)...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):
else if(Channel <= MAX_CHANNELS) { ... The second (21-07-08):
else if(Channel < MAX_CHANNELS) { ...The declaration of "Channel" :
static volatile byte Channel; // number of channels detected so far in the frame (first channel is 1),
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