timing capture glitch with TC0 and TC2 forming 32bit timer

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

Hi,

I'm trying to use TC0 and TC2 and PWM2 to form a perfect 32bit hardware timer for my digital input signal's timing measurement.

My UC3B is running at FCPU and FPBA equal to 48MHz, I use TC0 as low 16bits running at PBA/2 and TC2 as high 16bits running by TCLK2 which is driven by PWM2 outputing clock with PBA/(2*65536) frq. The initial sync between PWM2 output and TC0 clock is done by:

static const tc_interrupt_t TC0_INTERRUPT =
{
  .etrgs = 0,
  .ldrbs = 1,
  .ldras = 1,
  .cpcs  = 0,
  .cpbs  = 0,
  .cpas  = 0,
  .lovrs = 0,
  .covfs = 0
};


// Options for TC0 capture mode.
tc_capture_opt_t tc0_capture_opt_sync =
{
  .channel  = TC_CHANNEL0,                  // Channel selection.

  .ldrb     = TC_SEL_RISING_EDGE,         	// RB loading selection.
  .ldra     = TC_SEL_RISING_EDGE,          	// RA loading selection.

  .cpctrg   = TC_NO_TRIGGER_COMPARE_RC,     // RC compare trigger enable.
  .abetrg   = TC_EXT_TRIG_SEL_TIOB,         // TIOA or TIOB external trigger selection.
  .etrgedg  = TC_SEL_NO_EDGE,				// External trigger edge selection.

  .ldbdis   = FALSE,                        // Counter clock disable with RB loading.
  .ldbstop  = FALSE,                        // Counter clock stopped with RB loading.

  .burst    = TC_BURST_CLK_AND_XC2,           // Burst signal selection.
  .clki     = TC_CLOCK_RISING_EDGE,         // Clock inversion.
  .tcclks   = TC_CLOCK_SOURCE_TC3           // Internal source clock 2, connected to fPBA / 8.
};


// Options for TC0 capture mode.
tc_capture_opt_t tc0_capture_opt =
{
  .channel  = TC_CHANNEL0,                  // Channel selection.

  .ldrb     = TC_SEL_RISING_EDGE,         	// RB loading selection.
  .ldra     = TC_SEL_RISING_EDGE,          	// RA loading selection.

  .cpctrg   = TC_NO_TRIGGER_COMPARE_RC,     // RC compare trigger enable.
  .abetrg   = TC_EXT_TRIG_SEL_TIOB,         // TIOA or TIOB external trigger selection.
  .etrgedg  = TC_SEL_NO_EDGE,				// External trigger edge selection.

  .ldbdis   = FALSE,                        // Counter clock disable with RB loading.
  .ldbstop  = FALSE,                        // Counter clock stopped with RB loading.

  .burst    = TC_BURST_NOT_GATED,           // Burst signal selection.
  .clki     = TC_CLOCK_RISING_EDGE,         // Clock inversion.
  .tcclks   = TC_CLOCK_SOURCE_TC3           // Internal source clock 2, connected to fPBA / 8.
};


// Options for TC2 capture mode.
tc_capture_opt_t tc2_capture_opt =
{
  .channel  = TC_CHANNEL2,                  // Channel selection.

  .ldrb     = TC_SEL_RISING_EDGE,         	// RB loading selection.
  .ldra     = TC_SEL_RISING_EDGE,          	// RA loading selection.

  .cpctrg   = TC_NO_TRIGGER_COMPARE_RC,     // RC compare trigger enable.
  .abetrg   = TC_EXT_TRIG_SEL_TIOB,         // TIOA or TIOB external trigger selection.
  .etrgedg  = TC_SEL_NO_EDGE,				// External trigger edge selection.

  .ldbdis   = FALSE,                        // Counter clock disable with RB loading.
  .ldbstop  = FALSE,                        // Counter clock stopped with RB loading.

  .burst    = TC_BURST_NOT_GATED,           // Burst signal selection.
  .clki     = TC_CLOCK_RISING_EDGE,         // Clock inversion.
  .tcclks   = TC_CLOCK_SOURCE_XC2           // External clock 2, connected to PWM2.
};


// Options for tc1 waveform generation.
tc_waveform_opt_t tc1_waveform_opt =
{
  .channel  = TC_CHANNEL1,                  // Channel selection.

  .bswtrg   = TC_EVT_EFFECT_NOOP,           // Software trigger effect on TIOB.
  .beevt    = TC_EVT_EFFECT_NOOP,           // External event effect on TIOB.
  .bcpc     = TC_EVT_EFFECT_NOOP,           // RC compare effect on TIOB.
  .bcpb     = TC_EVT_EFFECT_NOOP,           // RB compare effect on TIOB.

  .aswtrg   = TC_EVT_EFFECT_NOOP,           // Software trigger effect on TIOA.
  .aeevt    = TC_EVT_EFFECT_NOOP,           // External event effect on TIOA.
  .acpc     = TC_EVT_EFFECT_NOOP,            // RC compare effect on TIOA.
  .acpa     = TC_EVT_EFFECT_NOOP,          // RA compare effect on TIOA.

  .wavsel   = TC_WAVEFORM_SEL_UP_MODE_RC_TRIGGER,      // Waveform selection: Up mode with automatic trigger on RC compare.
  .enetrg   = FALSE,                        // External event trigger enable.
  .eevt     = TC_EXT_EVENT_SEL_TIOB_INPUT,  // External event selection.
  .eevtedg  = TC_SEL_NO_EDGE,               // External event edge selection.
  .cpcdis   = TRUE,                        // Counter disable when RC compare.
  .cpcstop  = FALSE,                        // Counter clock stopped with RC compare.

  .burst    = TC_BURST_NOT_GATED,           // Burst signal selection.
  .clki     = TC_CLOCK_RISING_EDGE,         // Clock inversion.
  .tcclks   = TC_CLOCK_SOURCE_TC5           //Internal source clock 2, connected to fPBA / 128.
};



static const tc_interrupt_t TC1_INTERRUPT =
{
  .etrgs = 0,
  .ldrbs = 0,
  .ldras = 0,
  .cpcs  = 1,
  .cpbs  = 0,
  .cpas  = 0,
  .lovrs = 0,
  .covfs = 0
};

	// PWM controller configuration.
	pwm_opt.diva = AVR32_PWM_DIVA_CLK_OFF;
	pwm_opt.divb = AVR32_PWM_DIVB_CLK_OFF;
	pwm_opt.prea = AVR32_PWM_PREA_MCK;
	pwm_opt.preb = AVR32_PWM_PREB_MCK;

pwm_channel2.CMR.calg = PWM_MODE_LEFT_ALIGNED;       // Channel mode.
	pwm_channel2.CMR.cpol = PWM_POLARITY_LOW;            // Channel polarity.
	pwm_channel2.CMR.cpd = PWM_UPDATE_DUTY;              // Not used the first time.
	pwm_channel2.CMR.cpre = AVR32_PWM_CPRE_MCK_DIV_2;  // Channel prescaler.
	pwm_channel2.cdty = 32768;   // Channel duty cycle, should be < CPRD.
	pwm_channel2.cprd = 65536;  // Channel period.
	pwm_channel2.cupd = 0;   // Channel update is not used here.
	
	pwm_channel_init(PWM_CHANNEL2, &pwm_channel2); // Set channel configuration to channel 2.

// Initialize the timer/counter capture.
	tc_init_capture(tc, &tc0_capture_opt_sync);
	tc_init_capture(tc, &tc2_capture_opt);

	tc_init_waveform(tc, &tc1_waveform_opt);

	// Set the compare trigger for TC1 as 2ms.
	tc_write_rc(tc, TC_CHANNEL1, TC1_DELAY_COUNT);

	// sync TC0 and TC2
	
	pwm_start_channels(1 << PWM_CHANNEL2);  // Start channel 2.
	
	// Start the timer/counter.
	tc_start(tc, TC_CHANNEL0);                    // And start the timer/counter.
	tc_start(tc, TC_CHANNEL2);                    // And start the timer/counter.
	
	gpio_enable_pin_interrupt(AVR32_TC_CLK2_0_1_PIN, GPIO_RISING_EDGE);
	
	// Look if the AVR32_TC_CLK2_0_1_PIN is still low??
	while (!gpio_get_pin_value(AVR32_TC_CLK2_0_1_PIN))
	{	
		;
	}
	
	gpio_disable_pin_interrupt(AVR32_TC_CLK2_0_1_PIN);
	
	// reset TC2
	tc_software_trigger(tc, TC_CHANNEL2);
	
	tc_init_capture(tc, &tc0_capture_opt);

// now TC0 and TC2 are in sync	

the ISR of TC0 RA and RB are:

/*! \brief TC0 ra interrupt.
 */
#if __GNUC__
__attribute__((__interrupt__))
#endif
static void tc0_ra_irq(void)
{
	// reset and start TC1
	tc_start(tc, TC_CHANNEL1);
	
	if (tc_read_sr(tc, TC_CHANNEL0) & AVR32_TC_SR0_LDRAS_MASK)
	{
		T1 = (U32)tc_read_ra(tc, TC_CHANNEL0);// Read value from RA register only when the pulse has become stable.
		T1 |= ((U32)tc_read_ra(tc, TC_CHANNEL2)) << 16;	
	}
	else
	{
		T1 = (U32)tc_read_rb(tc, TC_CHANNEL0);// Read value from RA register only when the pulse has become stable.
		T1 |= ((U32)tc_read_rb(tc, TC_CHANNEL2)) << 16;			
	}
	
	INTC_register_interrupt(&tc0_rb_irq, AVR32_TC_IRQ0, TC_INT_LEVEL);
	
	// Clear the TC0 interrupt flag. This is a side effect of reading the TC SR.
	tc_read_sr(tc, TC_CHANNEL0);
}

/*! \brief TC0 rb interrupt.
 */
#if __GNUC__
__attribute__((__interrupt__))
#endif
static void tc0_rb_irq(void)
{
	// reset and start TC1
	tc_start(tc, TC_CHANNEL1);
	
	if (tc_read_sr(tc, TC_CHANNEL0) & AVR32_TC_SR0_LDRBS_MASK)
	{
		T1 = (U32)tc_read_rb(tc, TC_CHANNEL0);// Read value from RA register only when the pulse has become stable.
		T1 |= ((U32)tc_read_rb(tc, TC_CHANNEL2)) << 16;	
	}
	else
	{
		T1 = (U32)tc_read_ra(tc, TC_CHANNEL0);// Read value from RA register only when the pulse has become stable.
		T1 |= ((U32)tc_read_ra(tc, TC_CHANNEL2)) << 16;			
	}
	
	INTC_register_interrupt(&tc0_ra_irq, AVR32_TC_IRQ0, TC_INT_LEVEL);
	
	// Clear the TC0 interrupt flag. This is a side effect of reading the TC SR.
	tc_read_sr(tc, TC_CHANNEL0);
}



/*! \brief TC1 interrupt.
 */
#if __GNUC__
__attribute__((__interrupt__))
#endif
static void tc1_irq(void)
{
	U32 n;

	static U32 T2 = 0;
	
	gpio_enable_pin_interrupt(AVR32_TC_A2_0_1_PIN, GPIO_RISING_EDGE);
	
	if (gpio_get_pin_value(AVR32_TC_A2_0_1_PIN))// Look if the pulse is still high??
	{	
		
		n = T1 - T2;// Length of time from previous edge the timer triggered to the new edge detected.
		
		T2 = T1; //Replace the old value of time stamp with the new one
	}
	
	gpio_disable_pin_interrupt(AVR32_TC_A2_0_1_PIN);
	
	// Clear the TC1 interrupt flag. This is a side effect of reading the TC SR.
	tc_read_sr(tc, TC_CHANNEL1);
}

I tested my program by inputing a reference signal waveform with fixed period of 8Hz and then I constantly read the value n at 4KHz which is the calculated period of the incoming signal and it always have the glitch values 2934464 and 3065536 which is 65536 counts more or less than the expected glitch-free number 3000000. Now I'm wondering if these glitches are caused by the small TC2 to TC0 sync error of about 20ns (which is half cycles of PBA/2 = 24MHz) that I measured and found.

Anyone have an idea about this strange thing and could this still be my software bug?

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

I just found out the way to fix the sync error.
I need to change the PWM2 init code to

	pwm_channel2.CMR.calg = PWM_MODE_LEFT_ALIGNED;       // Channel mode.
	pwm_channel2.CMR.cpol = PWM_POLARITY_LOW;            // Channel polarity.
	pwm_channel2.CMR.cpd = PWM_UPDATE_DUTY;              // Not used the first time.
	pwm_channel2.CMR.cpre = AVR32_PWM_CPRE_MCK;  // Channel prescaler.
	pwm_channel2.cdty = 32768 * 2;   // Channel duty cycle, should be < CPRD.
	pwm_channel2.cprd = 65536 * 2;  // Channel period.
	pwm_channel2.cupd = 32768 * 2 + 1;   // Channel update is not used here.
	
	pwm_channel_init(PWM_CHANNEL2, &pwm_channel2); // Set channel configuration to channel 2.

and I need to add code:

pwm_async_update_channel(PWM_CHANNEL2, &pwm_channel2);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

now i think I found the root cause of the small single PBA cycle sync error. It's because PWM is always driven by PBA by default, but TC is always driven by 32KHz OSC, then when you set TC input clock to PBA/2 and start PWM, then the sync error could be either 0 or 1 PBA cycle, depend on the exact time (number of PBA clock cycles) between TC input clock setting and PWM enabling.

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

but even after the small sync error is gone, I still get the 65536 count error.

I think this must be a software bug, but where???

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

Try clocking/incrementing the "high" counter by overflow/carry from the lower counter instead of using two different clocks.

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

Don't know if this has anything to do with your problem but did you take into account the PWM errata? From the errata section of the datasheet:

2. PWN counter restarts at 0x0001
The PWM counter restarts at 0x0001 and not 0x0000 as specified. Because of this the first
PWM period has one more clock cycle.
Fix/Workaround
- The first period is 0x0000, 0x0001, ..., period
- Consecutive periods are 0x0001, 0x0002, ..., period

Letting the smoke out since 1978

 

 

 

 

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

yes, the errata is noticed. My PWM period setting is according to this errata.

pwm_channel2.cprd = 65536 * 2;

this code should set PWM2 to output correct clock frequency to drive TC2 at PBA/(2*65536).