Reading servo PCM

Hey guys,

I was looking for the best way to read servo pulses from an RC receiver, and it occurred to me that I should use pin change interrupts for that, and that led to me finding this lib which does that very thing:

http://arduino.cc/playground/Code/ReadReceiver

I have one question though... Why does this lib use the Timer1 lib? Timer 1 is used by the servo library, so using that, I think, would make this lib incompatible with that. So why doesn't it just use Micros() to do the timing?

Was this just an odd choice on the part of the library developer, or is there some advantage to using Timer 1? Perhaps the Timer 1 lib just provided a convenient way to track multiple events? But why would that lib even exist in the first place when you can do that using Micros() and it would be a waste of a very rare resource? And couldn't it have been implemented as part of the code that updates Micros() and Millis() if it was so important the code be in a timer interrupt?

And before anyone suggests I use just Timer2 instead, I need that for my sound circuitry.

Well I've looked through the Read Receiver code and the Timer1 code, and I can't see any reason for Read Receiver to be using the Timer1 lib. Also, Read Receiver calls restart() then start() at one point, but restart() has been deprecated and just calls start() now, so it's executing the same code twice to reset the timer.

It is also unclear to me how this code would work when reading multiple pins. It seems like what it does when it detects a pin has gone high is to reset timer 1 and when it goes low again record the time, but if another pin triggers in the middle of that period it seems like this would break. Implementing the same thing using micros() however, and storing the start and end times for each pin so you can calculate how long the pin was high seems like it would work just fine.

So I'm gonna go out on a limb and say Read Receiver may be buggy and could probably be implemented in a better way that doesn't take over the timer needed to drive the very servos its reading inputs for.

Hi,

The library is probably fine, lots or receivers are able to output the complete pulse train on a single output and if tnats available its actually more efficient to use than having to manage multiple channel pins.

The biggest problem with this approach and any others that use timer1, is that it prevents the servo library from using timer1.

Have a look at my blog form lots of RC projects including code and explanations

Duane B
rcarduino.blogspot.com

DuaneB: Hi,

The library is probably fine, lots or receivers are able to output the complete pulse train on a single output and if tnats available its actually more efficient to use than having to manage multiple channel pins.

I don't think the libary is designed to handle that. Also, I think I saw some posts about that pulse train you're talking about, and doesn't that require modding the receiver? And wouldn't the pulse train be unique to different types of radio?

If the receiver has to be modded, or you need special hard to find receivers, then that's not a suitable option for my board, which I'm trying to make as easy to use and flexible as possible. I haven't seen any receivers myself which have anything other than the standard servo outputs.

The biggest problem with this approach and any others that use timer1, is that it prevents the servo library from using timer1.

That's just what I said. And it appears there's no need to use Timer1 here at all. It looks like it's just being used like a stopwatch. And I don't know why someone would waste a timer in that manner when Micros() can give you the same data.

Hi,

The reason is because reading timer1 is much faster than calling micros, the faster you can get in and out of an ISR the better so using timer capture on timer1 makes a lot of sense, apart from not being able to use the servo library which lets be honest 99% of RC Receiver projects will need to.

There are lots of examples on my blog of using interrupts to read RC Equipment and interface with servos and ESCs, many people appear to be using the code without any problems, I have been using it for months myself in RC Cars and robots, see here -

http://rcarduino.blogspot.com/2012/04/how-to-read-multiple-rc-channels-draft.html

and with video and active control of an RC Race car

http://rcarduino.blogspot.com/2012/07/rcarduino-yaw-control-part-2.html

Duane B

rcarduino.blogspot.com

DuaneB:
The reason is because reading timer1 is much faster than calling micros,

Have you actually tested this?

Because I’m looking at the source for Micros() right now:

unsigned long micros() {
	unsigned long m;
	uint8_t oldSREG = SREG, t;
	
	cli();
	m = timer0_overflow_count;
#if defined(TCNT0)
	t = TCNT0;
#elif defined(TCNT0L)
	t = TCNT0L;
#else
	#error TIMER 0 not defined
#endif

  
#ifdef TIFR0
	if ((TIFR0 & _BV(TOV0)) && (t < 255))
		m++;
#else
	if ((TIFR & _BV(TOV0)) && (t < 255))
		m++;
#endif

	SREG = oldSREG;
	
	return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}

And it doesn’t look any more complex than the Timer1 code in that servo lib. In fact they look like they work in a very similar manner:

unsigned long readTimer1()        //returns the value of the timer in microseconds
{                                    //rember! phase and freq correct mode counts 
                                    //up to ICR1 then down again
    unsigned int tmp=TCNT1;
    char scale=0;
    switch (Timer1.clockSelectBits)
    {
    case 1:// no prescalse
        scale=0;
        break;
    case 2:// x8 prescale
        scale=3;
        break;
    case 3:// x64
        scale=6;
        break;
    case 4:// x256
        scale=8;
        break;
    case 5:// x1024
        scale=10;
        break;
    }
    while (TCNT1==tmp) //if the timer has not ticked yet
    {
        //do nothing -- max delay here is ~1023 cycles
    }
    tmp = (  (TCNT1>tmp) ? (tmp) : (ICR1-TCNT1)+ICR1  );//if we are counting down add the top value
                                                        //to how far we have counted down
    return ((tmp*1000L)/(F_CPU /1000L))<<scale;
}

And actually, I’d go so far as to say Micros() would likely be the winner here. It looks simpler, with far fewer conditional jumps, and unlike the Timer1 function it does not have a busy loop in the middle that waits for the timer to tick.

Hi,

We may have a misunderstanding here, what I have been refering to is accessing the timer1 input capture register, ICR1 rather than the timer1 library.

As I have mentioned, I choose not to user timer1 in any form because I prefer having the option of using the servo library, in which case I use micros for my timing.

To see how fast or slow micros is, you should look at the generated assembly for example

A++;

generates a single instruction where A is a local uint8_t variable optimised into a register, it could generate 12 instructions if A was a global long, the difference in C doesnt look like much, but the generated code could be 12 times slower.

If you don't get there first I will have a look at what gets generated for micros and a call to micros which assigns a value to a usable variable later this evening.

Duane B.