Help with frequency capture in Xmega

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

Hello!

I am trying to use an xmega 16a4 xmega processor for frequency capture. Essentially there is a generator connected to PD4 that should produce a signal between 100 and 200 kHz.

Basically what I would like to do is to set up a timer TCC0 in freq capture mode and then read the result during interrupts.

I have also tried clocking the timer using an event channel, but in that case the timer simply will not start.

I have measured the corresponding pin with an oscillograph and it is possible to see that a correct signal is being generated. When I check the CCA register however I always get a 0 as a result.

As far as I know my approach may be completely faulty. In that case maybe somebody can help and point me in the right direction

Here's the code :


unsigned long cnt2=0;

PORTD_PIN4CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_RISING_gc;

void timer2_init (void){

	
	EVSYS.CH0MUX = EVSYS_CHMUX_PORTD_PIN4_gc;
	EVSYS.CH0CTRL = 0x07;
	
	TCC1.CTRLA = TC_CLKSEL_DIV1_gc;

	TCC1.CTRLB = 0x10; //Enable capture on channel A
	
	TCC1.CTRLD = 0xA8; // 1010 1000 //Set register to frequency capture, event channel 0 (1000)
	

	TCC1.PER=0x7fff;

	TCC1.INTCTRLB = 0x01;
		
}


ISR (TCC1_CCA_vect){
	cnt2 = TCC1_CCA;
}

I use another timer TCC0 in order to create simple delays and timing events and it works fine. If I could somehow clock the timer from an event channel then I could simply count the ticks each millisecond, but thus far this has not worked either.

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

Well, there are 2 ways you might do this. Ok, 3.

1. Count rising or falling edges for a specific time and multiply.

2. Time between a specific number of rising or falling edges and divide.

3. Send your signal to a PLL and measure the input of the VCO with the A/D. (Use 1 or 2)

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

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

Ok I would prefer the 1st method. In this case the exact frequency is not so important. Rather it is necessary to get the relationship between the current frequency and 200 kHz. Meaning that I actually just need a percentage.

Anyway when I tried counting the events, the timer simply did not start.

Does anybody have a sample code that has been proven to work for clocking a timer from an event channel?

If I could get this to work I could just read the TCC1.CNT value every 10ms and reset the counter.

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

If you use the XMegas frequency capture you use method 2, you get the time between two rising edges.

I can't see anything wrong except a couple of magic numbers.

And cnt2 should be volatile if you use it in main, otherwise the compiler may throw it out. But I guess you could read TCC1.CCA in main to get the last measured period instead of using an ISR. Did you enable interrupts?

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

Some more comments.

TCC1.PER=0x7fff; 

This isn't needed in frequency capture mode, it's only useful if you need to sense both rising and falling edges. In that case MSB is one for rising edges, so it will always be set with your configuration.

Here's how I would do it (sketchy), I think it's about the same as your's (but I don't use the interrupt).

    // PD4 input with pulldown and event 0 on rising edges
    PORTD.PIN4CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_RISING_gc; 
    EVSYS.CH0MUX = EVSYS_CHMUX_PORTD_PIN4_gc;
    EVSYS.CH0CTRL = EVSYS_DIGFILT_8SAMPLES_gc;

    // TCC1 for input frequency capture on CCA from event channel 0
    TCC1.CTRLB = TC1_CCAEN_bm;
    TCC1.CTRLD = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
    // We only sence rising edges, set period to max (default)
    TCC1.PER = 0xFFFF;
    // Start timer with presc 1:1
    TCC1.CTRLA = TC_CLKSEL_DIV1_gc;

    while(1)
    {
        // Frequency from last capture.
        uint32_t frequency = F_CPU / TCC1.CCA;
        // ...
    }
===================================================================
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you test it? As I recall both frequency and pulse width capture need PORT_ISC_BOTHEDGES_gc.
/Lars

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

I tested it (on a XMega32A4U) and it works both with RISING and BOTHEDGES, but not with FALLING. I guess it just ignores the falling edge event in the BOTHEDGES case (and it isn't needed for anything as far as I can see).

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

I recall reading something about needing to invert the pin to get the other edge in the datasheet.

Jeff Nichols

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

Hi all, i am trying out snigelen's code with the frequency capture on xmega-128A1.
I am trying it out with a simple led trigger based on the frequency that is captured. Example that the reference frequency is 20kHz, the leds did not light up. Can anybody help with the code? Is there a mistake somewhere?

Thanks

static uint32_t frequency;


int main( void )
{
	
	
   	PORTE.DIR = 0xFF; 
   	PORTE.OUT = 0xFF; 
	while(1)
	{
		Example3();
	

			if(frequency > AE20) //this indicates 20kHz is this correct?//
	  		{
	  		 
			PORTE.OUT = 0x00;
			
			}
	
	  		else
	  		{
	  		PORTE.OUT = 0xFF;
	   		}
}
}


void Example3( void )
{
	// PD4 input with pulldown and event 0 on rising edges 
    PORTD.PIN4CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_RISING_gc; 
    EVSYS.CH0MUX = EVSYS_CHMUX_PORTD_PIN4_gc; 
    EVSYS.CH0CTRL = EVSYS_DIGFILT_8SAMPLES_gc; 

    // TCC1 for input frequency capture on CCA from event channel 0
    TCC1.CTRLB = TC1_CCAEN_bm; 
    TCC1.CTRLD = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc; 
    // We only sence rising edges, set period to max (default) 
    TCC1.PER = 0xFFFF; 
    // Start timer with presc 1:1 
    TCC1.CTRLA = TC_CLKSEL_DIV1_gc; 

     
     
        // Frequency from last capture. 
        frequency = F_CPU / TCC1.CCA; 
        // ... 
    
}