ATxMega128A1 frequency measurement with input capture and events

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

Hello, guys.
Im using ATxMega128A1 for freq measure. Is my first XMega experience.

Atmel Studio 7.0

 

Clock freq = 32 MHz

input square signal => PORTD.0

This variant is worked (all external functions from examples for XMega's):

 

void freq_init() {
    // setup PD0 on output, by rising edge
    PORTD.PIN0CTRL = 0x01;
    PORTD.DIRCLR = 0x01;
    // assign PORTD.0 to event 0 source
    EVSYS_SetEventSource(0, EVSYS_CHMUX_PORTD_PIN0_gc);
    // by event 0 need capture CNT in CCA and reset counter C1
    TCC1.CTRLD = (uint8_t) TC_EVSEL_CH0_gc | TC_EVACT_FRQ_gc;
    // on "Compare or Capture" mode for channel A
    TCC1.CTRLB |= TC1_CCAEN_bm;
    // setup period and freq of timer C1
    TCC1.PER = 0xFFFF;
    TCC1.CTRLA = TC_CLKSEL_DIV1_gc;
}

void func()
{
    //...
    freq = TCC1.CCA;
    //...
}

 

but is good for low-freq signal (about 5-10 kHz).

I need 60-110 kHz - that variant will be inaccurate, because that freq is close to clock freq.

 

I reach did that:

 

1) timer 0 works from system clock freq.

2) timer 1 count input freq-signal

3) when timer 1 overflow after 256 clocks, then timer 0 need capture current CNT and restart

 

But i can't get worked code variant.

 

That's not worked properly:

 

void freq_init() {
// timer 1
	// setup PD0 on output, by rising edge
	PORTD.PIN0CTRL = 0x01;
	PORTD.DIRCLR = 0x01;
	// assign him on event 2 source
	EVSYS_SetEventSource(2, EVSYS_CHMUX_PORTD_PIN0_gc);
	// setup timer 1 period
	TCD1.PER = 0x00FF;
	// by event 2 timer 1 need increment (count)
	TCD1.CTRLA = 0b00001010; // i don't found right constants, see that in ATxMega128A1 manual
// timer 2
	// by overflow timer 1 generate event 3
	EVSYS_SetEventSource(3, EVSYS_CHMUX_TCD1_OVF_gc);
	// by event 3 timer 2 need capture CNT in CCA and restart
	TCC1.CTRLD = (uint8_t) TC_EVSEL_CH3_gc | TC_EVACT_FRQ_gc; // (1*)
//TCC1.CTRLD = (uint8_t) TC_EVSEL_CH0_gc | TC_EVACT_CAPT_gc; // (2*) see comment after code
	// enable mode "Compare or Capture" for channel A
	TCC1.CTRLB |= TC1_CCAEN_bm;
	// setup period and freq for timer 2
	TCC1.PER = 0xFFFF;
	TCC1.CTRLA = TC_CLKSEL_DIV1_gc;
}

If use 2* instead 1*, then capture works, but timer not restart.

Maybe you notice a my mistake.

 

I cannot used interrupts, because program all time busy manual data transfers by own serial protocol. Any interrupt crash current transfer data routine.

Also i using mixing assembler and C code (because data transfers by freq <= 400 kHz, freq and transfer delay may changing runtime).
In asm-functions using this registers (by way of "Mixing code for XMEGA's" manual):

 

    r8, r9, r10, r11, r13, r14, r16

 

r16 needs me for 'ldi' command, but r16 is using in-time integer divide operation in C-code too (i add push/pop).

Maybe problem in that code mixing? Or i using wrong registers?

 

Also i tried divide input freq on 256 and output signal on PORTC.7 by way event 0, connect it with another port and generate another event for timer 1, but code not worked.

 

From other resources - using third timer (for detection pause by clock transfer line), with interrupt routine.
Isn't depends with frequency measurement:

 

void timer2_init(void) {
    // PORTD3 by any edge
    PORTD.PIN3CTRL = 0x00;
    // assing to event 1
    EVSYS_SetEventSource(1, EVSYS_CHMUX_PORTD_PIN3_gc);
    // from event 1 need restart timer
    TCE0.CTRLD = (uint8_t) TC_EVSEL_CH1_gc | TC_EVACT_RESTART_gc;
    // enable overflow and set medium priority
    TCE0.INTCTRLA = 0x02;
    // set period
    TCE0.PER = 0x00B0; // 11 mks
    TCE0.CNT = 0x0000;
    // start timer (div = 2)
    TCE0.CTRLA = TC_CLKSEL_DIV2_gc;
}

 

Sorry for my bad english knowledge and typos.

Delelop. env: Atmel Studio 7.0
Programmer: AVRisp mkII

Last Edited: Thu. Aug 24, 2017 - 05:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

but is good for low-freq signal (about 5-10 kHz).
I need 60-110 kHz - that variant will be inaccurate, because that freq is close to clock freq.

 

The fastest the TC can operate is 32MHz.  If that does not provide enough resolution, you need a faster chip.  Getting more TCs involved is not going to help. 

Greg Muth

Portland, OR, US

Atmel Studio 7.0 on Windows 10

Xplained/Pro/Mini Boards mostly

 

 

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

Greg_Muth

 

With 32MHz:

  290 in CCA is 110,344 kHz
  291 in CCA is 109,965 kHz

  dF = 379 Hz

Isn't pretty accurate, need detect freq between 290 and 291.

 

With div 128 for input clock:

37120 = 110,344 kHz
37121 = 110,341 kHz
dF = 3 Hz

Is more accurate, than just count input clocks.

For that not need more faster chip. One flaw in that method - is increase of measuring time in 128 times.

Delelop. env: Atmel Studio 7.0
Programmer: AVRisp mkII

Last Edited: Fri. Aug 25, 2017 - 04:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With this variant:

void freq_init() {
// timer 1
    // PD0 by rising front
    PORTD.PIN0CTRL = 0x01;
    PORTD.DIRCLR = 0x01;
    // assing on event 1
    EVSYS_SetEventSource(1, EVSYS_CHMUX_PORTD_PIN0_gc);
    // setup period
    TCC0.PER = 0x00FF;
    // increment timer 1 by event 1
    TCC0.CTRLA = TC_CLKSEL_EVCH1_gc;

// timer 2
    // assing overflow timer 2 on event 0
    EVSYS_SetEventSource(0, EVSYS_CHMUX_TCC0_OVF_gc);
    // by event 0 timer 2 need save CNT in CCB and restart
    TCD0.CTRLD = (uint8_t) TC_EVSEL_CH0_gc | TC_EVACT_FRQ_gc;
    // enable mode "Compare or Capture" for channel B
    TCD0.CTRLB |= TC1_CCBEN_bm;
    // setup period and freq from sysclk / 1
    TCD0.PER = 0xFFFF;
    TCD0.CTRLA = TC_CLKSEL_DIV1_gc;
}

void func() {
    // ...
    freq = TCD0.CCB;
    // ...
}

Input Capture worked, but... overflow TCC0 appears every tick on PORTD.0. By my idea it's sould be every 256 ticks.

UPD: No, is not worked. Still trying.

 

I have found post with same problems.

I temporarily mounted external timer 74HC393 for divide input freq by 128. But still need did this by microcontroller.

Delelop. env: Atmel Studio 7.0
Programmer: AVRisp mkII

Last Edited: Fri. Aug 25, 2017 - 01:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello, again.
Now i make like that:

input freq => PORTD.0 => increment timer C0 => overflow C0 => output OC0A to PORTC.0 => connection => PORTD.1 => event 2 by rising edge => capture for timer D0

But i still want too know how can did same thing with 2 events.

Delelop. env: Atmel Studio 7.0
Programmer: AVRisp mkII

Last Edited: Mon. Aug 28, 2017 - 05:27 PM