xmega timer/counter - how can I produce accurate PPM ?

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm looking at how to produce accurate PPM (as in radio-control). Goal is to control 24 RC servos using 4 output pins and 3 x 8-bit shift registers.

In the mega (not xmega) the counter/compare unit has the COMx bits in TCCRxA. I could run the timer in normal mode, and in the interrupt handler for a compare match set the next value for OCRx and also what to do in the next match. That could make very accurate PPM. If the timer ran at 1 MHz I got 1 uSec resolution and accuracy, independent of interrupt latency.

Working a little harder I could produce accurate PPM or PWM even when using the 8 bit timers (also in the ATTiny mcus).

Well... I do not see such option in the xmega. It is still possible to use the interrupt handler to change the port output (but accuracy will depend on interrupt latency which I cannot guarantee).

The documentation hints that I could use the event system, but I still need to understand if it's at all possible and what are the limitations.

Attaching ATMega328p code example.

#include 
#include 
// ATmega328p PPM code.
// The 16 bit timer1 is initialized for normal mode. DIV8 prescaler. COM1A1 is set.
// F_CPU is 8000000L.

// Convert microseconds to timer ticks
#define TICKS(n) (n)	// for 8 MHz clock and 8x prescaler

#define FRAME_LEN	TICKS(22000)
#define PULSE_LEN	TICKS(500)

#define N_CHANNELS	7

// Values are timer ticks equivalence of servo pulse width
// valid values are between TICKS(1000) and TICKS(2000).
volatile uint16_t channel_values[N_CHANNELS];

// Generate PPM on OCR1A
// This function sets the next value for the compare register, and the action to do
// on the next compare match. It gives very accurate PPM even when interrupt
// response latency is high.
ISR(TIMER1_COMPA_vect) {
	static uint8_t ch_no;
	static uint16_t frameleft;
	if(TCCR1A & _BV(COM1A0)) {
		TCCR1A &= ~_BV(COM1A0);
		OCR1A += PULSE_LEN;
	}
	else {
		TCCR1A |= _BV(COM1A0);
		if(ch_no < N_CHANNELS) {
			OCR1A += channel_values[ch_no] - PULSE_LEN;
			frameleft -= channel_values[ch_no];
			ch_no++;
		}
		else {
			OCR1A += frameleft - PULSE_LEN;
			frameleft = FRAME_LEN;
			ch_no = 0;
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I could run the timer in normal mode, and in the interrupt handler for a compare match set the next value for OCRx and also what to do in the next match.

you can use the compare interrupt on the xmega to update your compare registers. you can use up to 6 compare matches per timer (4+2). the compare interrupts are enabled by INTCTRLB registers

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you're using a 64 pinner, it'll have 22 PWM channels that you can use to reduce hardware complexity. Then you can do the other 2 using s.ware PWM, or you can do s.ware PWM of all 24 using a timer ISR and s.ware logic to keep track of duty cycle of every channel. I'd bet it's alot easier to get it working my way than messing with the external hardware. It's just more to go wrong and have to debug down the road of time if/when those chips start acting up .

You can set, clr or toggle I/O pins using the correct setting of an I/O control reg. For the time scale of RC servos, you'll have plenty of time to do that for even 24 pins !

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hzrnbgy wrote:
you can use the compare interrupt on the xmega to update your compare registers.

That's probably what I will try. But as I plan to use more IO's that will also use interrupts it would be difficult to avoid glitches that result from late handling of interrupts.

Author wrote:
If you're using a 64 pinner, it'll have 22 PWM channels

That's not an option.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you perhaps worrying too much about interrupt latency? This might be a very minor issue when compared to the actual performance of a hobby servo.

Tom Pappano
Tulsa, Oklahoma

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

remember that the xmegas come with a programmable multilevel interrupt controller. you can assign priority levels on your interrupts.

the highest level can preempt med and low level interrupt. this is where you put your interrupts with the most critical timing requirements (those that need to happen immediately). unfortunately, you "cannot run" two or more high priority interrupts at the same time. it is inherent to single processing core devices.

then there are the medium and low priorities.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The multilevel interrupt controller may help but not too much. I expect to have 3 sources that are the same priority, and possibly other interrupt sources that are higher priority.

I will check if it's possible to let the the pin work in PWM mode and then reset it and increment the compare register by the interrupt handler. If it's possible it will resolve the problem (the pulse width is not critical).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Remember, the Xmegas can run at 32 MHz!
Even if the entire project used software for the timing one is looking a microseconds variability controlling a low precision output device that runs on fractional mSec timing commands.

JC