Quadrature Output from Single TC0/1 Timer on XMEGA 128A1U

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

Hello All,

 

Many posts here deal with the quadrature encoder hardware on the XMEGA, but I have a different problem: I don't need to DECODE quadrature signals, I need to CREATE them.  I need two squarewaves 90 degrees out of phase, with variable frequency up to 100kHz.  Ideally, this would be a single-timer solution, with no CPU intervention other than setting up the operating conditions.  I would also like to easily be able to change the direction of the signals, i.e., change which signals leads and which lags.

 

Have any of you solved such an problem?  If so, I would love to hear how you did it.  Thanks in advance!

 

Altazi

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

Is that 100kHz the edge rate, or the signal freq (means 400kHz edge rate)

You could toggle the pins in a compact interrupt, with a few lines of assembler.

If you do not have the CPU resource spare, add a logic chip, or a small MCU, and run that from a timer overflow.

 

Smallest MCU with Config Logic looks to be ATtiny212, that might have enough to code, otherwise SiLego have compact logic parts that could encode.

Last Edited: Mon. Apr 30, 2018 - 10:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

That would be a 400kHz edge rate.  The timing must be hardware precise.  The CPU is going to be busy doing a few other things, so it would be best if I could avoid an interrupt.  Adding external hardware would be a real complication - I am trying to accomplish my goal using an Xplained Pro 128A1U.

 

Thanks!

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

Configure a timer for Frequency Waveform Generation mode; writing CCA register sets the frequency. Use a second compare channel that's set to half the CCA value. Feed that second compare to the event system to toggle a second pin. Reversing direction is simple toggling one of those pins manually.

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

Scratch what I said above about toggling a pin with the event system. The SAM devices support toggling, but the XMEGAs don't.

 

An ugly option to avoid CPU/ISR would be to route the event pulse externally back to the analog comparator and have it toggle it's output pin.

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

Maybe the AWEX unit in pattern generation mode. Don't know, never used it before.

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

Thanks, Balisong42.  I was with you all the way until you said 'toggle a pin', and I had been failing to figure out a way to do that in an XMEGA.  Thanks for confirming it isn't possible.  Too bad, that would have been a good solution.  Back to the drawing board . . .

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

Hi All,

 

This is what I have come up with so far.  I don't like it, and I am not even certain that it will work.  I would configure a timer for single-slope PWM output, and generate waveforms that look like this:

 

;	         ________________                  ________________
;	PWM A	|                |________________|                |________________|
;                         ________________________          ________________________
;	PWM B#	|________|                        |________|                        |
;	         ________________________          _________________________
;	PWM C	|                        |________|                         |_______|
;                         _______________                   ________________
;	B# & C	_________|               |_________________|                |________

You can see that ANDing the B-invert and C produce a possibly reasonable 90-degree shifted wavefrom relative to PWM A.  If I configure the port outputs to be WIREAND, I should be able to tie output compare outputs B and C together, to produce the desired waveform.

 

Why I don't like it: This means that I can use only timers with four CC outputs, and the resulting 90-degree output is 'soft', not being push-pull.  I might need to buffer it, which is highly undesirable.  Plus, I am not certain that the ANDed waveform would be free from glitches when the PWM timer resets.

 

If anyone has any better solutions, I'd love to hear about them!

 

Altazi

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

* UPDATE: This approach will not work.  The XMEGA128A1U does not support firmware control of the polarity of the CC outputs.  I had been working with the XMEGA32E5 on another project, and it has those polarity controls.  Damn . . . back to the drawing board, AGAIN.

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

How about using DMA. Trigger from a timer. Source is an array {b00000001,b00000010}. Destination is port OUTTGL.

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

I managed to get square-waves with 90 degree phase on an A4U with this code:
 

int main(void)
{
	
	// 32MHz internal clock
	OSC.CTRL |= 0x02; // enable 32MHz osc
	while((OSC.STATUS&0x02)!=0x02) {} // wait for 32MHz osc to stabilize
	CCP = 0xD8; // unlock protect register
	CLK.CTRL = 0x01; // select 32MHz clock
	
		
	PORTC.DIRSET = 0x03;  // enable CCA and CCB outputs
	
	TCC0.CTRLA = TC_CLKSEL_DIV1_gc;
	TCC0.CTRLB = TC_WGMODE_FRQ_gc | ((1<<4) | (1<<5)); // enable CCA and CCB

	TCC0.CCA = 160; // 5.0us half-period
	TCC0.CCB = 80;  // 90 degree phase shift
	
		
//	PORTC.PIN2CTRL = (1<<1); // invert output for phase reversal
	
    while (1) {}
}

CCA determines the period.  CCB determines the phase shift. Inverting an output pin works for reversing.

And I still have no idea how AWEX works in PG mode. frown

 

Last Edited: Tue. May 1, 2018 - 11:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The XMEGA128A1U does not support firmware control of the polarity of the CC outputs.

?

 

I believe that it does support pin output polarity control, if you and I are both thinking the same thing.

 

The current Xmega128A1U data sheet, Section 13.13.15 PINnCTRL - Pin n Configuration register should reverse the output polarity of whatever else is driving the pin.

 

You reverse the polarity on any Xmega I/O pin at the level of the Port I/O setup, not at the level of the Timer/Counter or configuration.

 

JC

 

Last Edited: Tue. May 1, 2018 - 02:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I never used Xmega'x, but could it be possible to:

- Program the counter to count up and down.

- Program one compare channel to set at 0 and clear at TOP. ( "normal" PWM ? )

- Program another compare channel to SET if cnt >n and CLEAR if cnt < n. ( "Phase correct" PWM ? ).

 

Atmel (unfortunately) tends to write extive descriptions in their datasheets on how you can configure registers for some pre defined functionality, but if you want some other functionailty you need to interpret the register bits a bit creatively and try things out.

 

To get your ouput rate you can only use a small count number, but that can be remedied by cascading 2 counters.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

Hello All,

 

Thank you for the great suggestions.  I am presently working on another section of the project, and hope to get back to the quadrature generation shortly.  I will report my results.

 

Altazi

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

Oh man, Ballisong already posted a clean solution.

 

I also managed to generate two, 100 kHz square waves, with either lead or lag of Signal A with respect to Signal B.

Ballisong set it up for the HW to handle the I/O pin toggle.

 

I used ISRs, so it cuts into the foreground processing.

 

I used an Xmega32E5 as that was the Xmega I had handy.

 

I used TCC4 in Normal Mode to automatically roll over at a 100 kHz rate.

I used the four Compare/Capture Channels available to trigger an interrupt at each of the output signal transition points.

Each ISR is one (HLL) command, they either set the pin, or reset the pin.

No Push/Pop are required, as the ISR's simply manipulate the output pin hardware, and do not change an variables or perform any calculations.

So the ISR's are pretty skinny.

 

I noticed that TCC5 only has TWO CC Channels, while TCC4 has 4 channels, hence TCC4 was used for this implementation.

 

I noticed that I could NOT get TCC5 CC Channel A and B to work correctly, using just those two channels.

The output pin kept changing state even with the ISR's disabled...

Could be a bug in my program, the compiler, or the (early) E5 chip.

 

With TCC4 the output works great.

 

As already mentioned, the easiest way to flip the Lead / Lag of Signal A with respect to Signal B is to simply set the output polarity bit for Signal A, which will invert Signal A, and make it lag instead of lead.

 

Good luck with your project!

 

JC

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

Hello All,

 

The approach shown by Balisong42 worked as indicated.  I always wondered what would happen if one used the other compares (CCB-CCD) in the frequency generation mode.  Paulvdh was dead-on when he described the documentation issues.  DocJC was also correct regarding the output inversion via the PINnCTRL register.  The 32E5 had separate CCx inversion controls in the timer hardware, which the 128A1U does not.  By using the port output inversion control on the CCB output, I can set the lead-lag relationship of the two phases.  I believe this problem is solved.  Thank you so much!

 

Now I will connect the quadrature generator timer to the event system, and create a means of counting the cycles, so I can create a specified number of quadrature cycles - all without CPU intervention.

 

Altazi

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

Altazi wrote:
Now I will connect the quadrature generator timer to the event system, and create a means of counting the cycles, so I can create a specified number of quadrature cycles - all without CPU intervention.  

 

Can you post code when done, of the core portions, as a simple quad generator is a useful testing tool ?

Suggest you also include notes on Upper and Lower edge rates supported.

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

Alas that the event action for a TC0/1 timer does not include STOPPING the timer.  I have TCC0 as the quadrature generator, with TCC0 CCA interrupt connected to event CH0.  TCC1 uses event CH0 as a clock source, and so counts the quadrature cycles - well, half of them, since each TCC0 timeout is a half-cycle of a waveform.  Since I cannot get TCC1 to stop TCC0 by hardware, I can sample the TCC1 OVF interrupt.  So close . . .

 

Altazi

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

Hello All,

 

Here is the resulting code.  It is tested and working.  Some notes:

 

1.  No attempt was made to control the ending signal levels.  If a phase output ends up high, it stays high until changed by the next quadrature output operation.

 

2.  In the waveform generation mode, TCC0 output toggles on every overflow.  Thus, the TCC0 period specified is for one-half of a square wave.

 

3.  As shown, TCC0 is clocked by the 32MHz CPU clock.  This gives a TCC0 maximum period of 2.048ms.  Thus, the maximum quadrature output period is 4.096ms.  If you need a slower quadrature output, change the TCC0 clock selection in the QUAD_START routine.

 

4.  TCC1 is used as the quadrature cycle counter.  It is clocked by event channel 0, which is triggered by TCC0 OVF.  Since TCC0 creates two OVF events per quadrature waveform, the count loaded into TCC1 is the HALF-cycle count.  For my application, being able to generate 32k+ quad cycles was fine.  If you don't want to use the cycle count feature, omit the TCC1 code altogether.  You will need to manually stop the TCC0 timer.

 

5.  Experiment with even and odd cycle counts to see the effect on the ending levels of the phase outputs.

 

6.  Yes, it is in assembler.  I like to have total control of what is happening, where it happens, and how it happens.

 

Thanks to all of those who provided helpful suggestions!

 

Altazi

 

;-------------------------------;
;	subroutine QUAD_CONFIG	;
;-------------------------------;
;
; Configure TCC0 for quadrature output, but do not start timer
;
; Upon entry, the following RAM variables must be set
;
; QUAD_PER		16-bit	Quadrature waveform half-period time in 31.25ns increments
;				Valid range is 4 - 65535
; QUAD_CTRL		8-bit	Quadrature control register
;				b0 = phase control (0 = B leads A, 1 = A leads B)
;
; QUAD_CNT		16-bit	Quadrature half-cycle count
;				Valid range is 2 - 65535

QUAD_CONFIG:
	PUSH	R16			; save regs
	PUSH	R17			;

	LDI	R16,$00			; begin with timer off
	STS	TCC0_CTRLA,R16		;

	LDS	R16,PORTC_DIR		; get port data direction register
	SBR	R16,$0F			; enable OC0D-OC0A as outputs
	STS	PORTC_DIR,R16		; write new value for port data direction register

	LDI	R16,$31			; OC0B-OC0A enabled, frequency gen mode
	STS	TCC0_CTRLB,R16		;

	LDI	R16,$00			; no event system action
	STS	TCC0_CTRLD,R16		;

	LDI	R16,$00			; normal timer/counter mode (16-bits)
	STS	TCC0_CTRLE,R16		;

	LDS	R16,$00			; no interrupts
	STS	TCC0_INTCTRLA,R16	;
	STS	TCC0_INTCTRLB,R16	;

	LDS	R16,QUAD_PER		; get quadrature timer period from variable
	LDS	R17,QUAD_PER+1		;
	STS	TCC0_CCA,R16		; phase A freq low
	STS	TCC0_CCA+1,R17		; phase A freq high

	LSR	R16			; shift right for divide-by-2
	ROR	R17			; to create 90 degree offset

	STS	TCC0_CCB,R16		; phase B freq low
	STS	TCC0_CCB+1,R17		; phae B freq high

	LDS	R17,QUAD_CTRL		; get quadrature control variable
	LDI	R16,$00			; default is noninverted output for CCB
	CPI	R17,0			; inversion selected?
	BREQ	QUAD_CONFIG_10		; skip if no inversion

	SBR	R16,$40			; set for inversion of CCB output

QUAD_CONFIG_10:
	STS	PORTC_PIN1CTRL,R16	;
	
; Connect TCC0 to event system

	LDI	R16,$C4			; select TCC0 CCA as event source
	STS	EVSYS_CH0MUX,R16	;

; Configure TCC1 as quadrature cycle counter

	LDI	R16,$00			; begin with timer off
	STS	TCC1_CTRLA,R16		;

	LDI	R16,$00			; normal mode - no waveform generation,
	STS	TCC1_CTRLB,R16		; count based on PER

	LDI	R16,$00			; normal timer/counter mode (16-bits)
	STS	TCC1_CTRLE,R16		;

	LDI	R16,$01			; interrupt on overflow
	STS	TCC1_INTFLAGS,R16	; clear overflow interrupt flags
	STS	TCC1_INTCTRLA,R16	; set interrupt level
	LDI	R16,$00			; no CCx interrupts
	STS	TCC1_INTCTRLB,R16	;

; Get user-set count limit
; actual waveform cycles will be half of this count, as one TCC0 cycle
; is half of a squarewave.

	LDS	R16,QUAD_CNT		; quadrature cycle count variable
	LDS	R17,QUAD_CNT+1		;
	STS	TCC1_PER,R16		; period low
	STS	TCC1_PER+1,R17		; preiod high

	LDI	R16,$08			; select event CH0 clock source
	STS	TCC1_CTRLA,R16		;

	POP	R17			; restore regs
	POP	R16			;
	RET				; done


;-------------------------------;
;	subroutine QUAD_START	;
;-------------------------------;
;
; Start TCC0 quadrature waveform output

QUAD_START:
	PUSH	R16			; save reg

	LDI	R16,$01			; select CLK/1 to start timer
	STS	TCC0_CTRLA,R16		;

	POP	R16			; restore reg
	RET				; done
;==============================================================================
; Timer TCC1 Overflow interrupt - Quadrature Cycle Count Termination
;==============================================================================
;

TCC1_OV_INT:

; Always save R16 and CPU status register

	PUSH	R16			; save registers onto stack
	IN	R16,SREG		; get status register (in I/O space)
	PUSH	R16			; and save it on stack

; Stop TCC0 quadrature timer

	CLR	R16			; zero
	STS	TCC0_CTRLA,R16		; turn off TCC0 clock to stop timer

; Exit interrupt

TCC1_OV_INT_99:

; Always restore CPU status register and R16

	POP	R16			; restore status register from stack
	OUT	SREG,R16		; restore status register (in I/O space)
	POP	R16			;
	RETI				; done!