XMega Frequency & pulse width capture (twice) on same port

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

The project I am building requires me to measure frequency (period) and pulse width. I need to do this for 2 different sources (2 FAN PWM signals to be precise).

In the past I have used underneath code to readout these values for 1 fan.

 

Since I am planning on using a 32D4 (or alike -> same D version), there is a limited amount of timers. I am wondering if underneath could not be optimized to use 1 timer for 4 signals (2 frequencies and 2 pulse width -> CCA to CCD) on 1 timer lets say TCC0)

First I started with the underneath code, which is using TCC0 and TCC1 (while it is the same FAN), my htought is that this can be combined on 1 Timer TCC0)

 

Start (after reading this, I think it will not work because TCC0 is working on PIN0:3 while here it is defined on port 4 -> corrected this is mijn thoughts in underneath code snippets):

void init_inputFREQcapture(void)
{
	PORTC.DIRCLR   = PIN4_bm;
	PORTC.PIN4CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_RISING_gc;
	EVSYS.CH0MUX   = EVSYS_CHMUX_PORTC_PIN4_gc;
	EVSYS.CH0CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
	TCC0.CTRLB     = TC0_CCAEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC0.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC0.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC0.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC0.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC0.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC0.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC0.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
}

void init_inputPWcapture(void)
{
	PORTC.DIRCLR   = PIN5_bm;
	PORTC.PIN4CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_BOTHEDGES_gc;
	EVSYS.CH1MUX   = EVSYS_CHMUX_PORTC_PIN5_gc;
	EVSYS.CH1CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	TCC1.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH1_gc;
	TCC1.CTRLB     = TC1_CCAEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC1.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC1.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC1.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC1.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC1.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC1.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC1.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
}

First Change (Change TCC0 and TCC1 to TCC0 only):

void init_inputFREQcapture(void)
{
	PORTC.DIRCLR   = PIN4_bm;
	PORTC.PIN4CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_RISING_gc;
	EVSYS.CH0MUX   = EVSYS_CHMUX_PORTC_PIN4_gc;
	EVSYS.CH0CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
	TCC0.CTRLB     = TC0_CCAEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC0.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC0.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC0.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC0.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC0.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC0.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC0.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
}

void init_inputPWcapture(void)
{
	PORTC.DIRCLR   = PIN5_bm;
	PORTC.PIN4CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_BOTHEDGES_gc;
	EVSYS.CH1MUX   = EVSYS_CHMUX_PORTC_PIN5_gc;
	EVSYS.CH1CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH1_gc;
	TCC0.CTRLB     = TC0_CCBEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC0.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC0.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC0.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC0.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC0.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC0.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC0.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
}

Second Change (now TCC1 would be available to do signal 2 -> Fan 2):

void init_inputFREQcapture(void)
{
	PORTC.DIRCLR   = PIN0_bm;
	PORTC.PIN0CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_RISING_gc;
	EVSYS.CH0MUX   = EVSYS_CHMUX_PORTC_PIN0_gc;
	EVSYS.CH0CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
	TCC0.CTRLB     = TC0_CCAEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC0.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC0.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC0.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC0.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC0.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC0.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC0.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
}

void init_inputPWcapture(void)
{
	PORTC.DIRCLR   = PIN1_bm;
	PORTC.PIN1CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_BOTHEDGES_gc;
	EVSYS.CH1MUX   = EVSYS_CHMUX_PORTC_PIN1_gc;
	EVSYS.CH1CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH1_gc;
	TCC0.CTRLB     = TC0_CCBEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC0.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC0.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC0.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC0.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC0.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC0.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC0.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
}
void init_inputFREQcapture(void)
{
	PORTC.DIRCLR   = PIN4_bm;
	PORTC.PIN4CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_RISING_gc;
	EVSYS.CH0MUX   = EVSYS_CHMUX_PORTC_PIN4_gc;
	EVSYS.CH0CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	TCC1.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
	TCC1.CTRLB     = TC1_CCAEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC1.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC1.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC1.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC1.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC1.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC1.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC1.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
}

void init_inputPWcapture(void)
{
	PORTC.DIRCLR   = PIN5_bm;
	PORTC.PIN4CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_BOTHEDGES_gc;
	EVSYS.CH1MUX   = EVSYS_CHMUX_PORTC_PIN5_gc;
	EVSYS.CH1CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	TCC1.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH1_gc;
	TCC1.CTRLB     = TC1_CCBEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC1.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC1.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC1.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC1.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC1.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC1.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC1.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
}

Is this last example/assumption correct, and do you experts have advices?

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

A single TC can measure the frequency OR the pulse width of ONE signal at a time.  Depending on your sample rate, you could use a single timer to measure both for two signals, as long as you reconfigured the TC (FRQ or PW), PINSENSE (rising edge for FRQ or both edges for PW), and EVENT channel (fan A or fan B) after each capture.

Greg Muth

Portland, OR, US

Xplained/Pro/Mini Boards mostly

 

Make Xmega Great Again!

 

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

Hi Greg, thanks for your reply.

First I have to say, that I like your idea to use 1 timer for both fans. Because of the polling it does in my current set-up it will not do anything till I actually "poll" the signal. It would be different if I configured it as a interrupt, but that is not interesting for my purpose.

 

Secondly, what I read from the atmega AU (and D) manual is that the TC will be restarted on a "new"/positive edge event. This means I can read with the same TC (only a different event CH0 or CH1) FRQ and PW. If I combine with your idea to use only 1 TC, I could after polling that data change the ports to read it for FAN 2.

 

From Manual (AU):

  • FRQ: Since all capture channels use the same counter (CNT), only one capture channel must be enabled at a time. If two capture channels are used with different sources, the counter will be restarted on positive edge events from both input sources, and the result will have no meaning.
  • PW:  The counter will then restart on positive edge events, and the input capture will be performed on the negative edge event. 

 

What I do not understand is how to combine (probably not possible and that is why you said you need to reconfigure your port etc):

	TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
	TCC0.CTRLB     = TC0_CCAEN_bm | TC_WGMODE_NORMAL_gc;

	TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH1_gc;
	TCC0.CTRLB     = TC0_CCBEN_bm | TC_WGMODE_NORMAL_gc;

 

So based on your info and further analysis of my own code I will just try it the way you propose.

 

Unfortunately I cannot use 1 timer, since FAN 1 is connected to PIN0 &1 of PORTC (TC0) and PIN4&5 are also on PORTC (TC1), so I will always use 2 timers.

What I did is adjusted my code to get the underneath:

Switching CTRLD with respective CH and FRQ OR PW should give me the ability to get PW or FRQ.

 

void init_captureFAN1(void)
{
	PORTC.DIRCLR   = PIN0_bm | PIN1_bm;
	PORTC.PIN0CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_RISING_gc;
	PORTC.PIN1CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_BOTHEDGES_gc;
	EVSYS.CH0MUX   = EVSYS_CHMUX_PORTC_PIN0_gc;
	EVSYS.CH1MUX   = EVSYS_CHMUX_PORTC_PIN1_gc;
	EVSYS.CH0CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	EVSYS.CH1CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	
	// Switch to be able to capture PWM OR FRQ
	switch ( CaptureSelection ) {
		case  1:
		TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
		break;

		case  2:
		TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH1_gc;
		break;
	}
	
	TCC0.CTRLB     = TC0_CCAEN_bm | TC0_CCBEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC0.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC0.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC0.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC0.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC0.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC0.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC0.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
}

void init_captureFAN2(void)
{
	PORTC.DIRCLR   = PIN4_bm | PIN5_bm;
	PORTC.PIN4CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_RISING_gc;
	PORTC.PIN5CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_BOTHEDGES_gc;
	EVSYS.CH2MUX   = EVSYS_CHMUX_PORTC_PIN4_gc;
	EVSYS.CH3MUX   = EVSYS_CHMUX_PORTC_PIN5_gc;
	EVSYS.CH2CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	EVSYS.CH3CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	
	// Switch to be able to capture PWM OR FRQ
	switch ( CaptureSelection ) {
		case  1:
		TCC1.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH2_gc;
		break;

		case  2:
		TCC1.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH3_gc;
		break;
	}
	
	TCC1.CTRLB     = TC1_CCAEN_bm | TC1_CCBEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC1.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC1.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC1.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC1.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC1.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC1.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC1.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
}

If this works I can try to move the following block into my code to make switching easy:

	switch ( CaptureSelection ) {
		case  1:
		TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
		break;

		case  2:
		TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH1_gc;
		break;
	}

I will test it later today with a known source (artificial PWM signal where PW and FRQ are known).

Last Edited: Sat. Jan 9, 2016 - 08:06 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Been working on this problem the whole afternoon, however I am not able to solve how to reset/adjust a working input capture to switch from PW to FRQ.

 

void init_captureFAN1(uint8_t CaptureSelection)
{
	PORTC.DIRCLR   = PIN0_bm;
	PORTC.PIN0CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_BOTHEDGES_gc;
	EVSYS.CH0MUX   = EVSYS_CHMUX_PORTC_PIN0_gc;
	EVSYS.CH0CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	// Switch to be able to capture PWM OR FRQ
	switch ( CaptureSelection ) {
		case  1:
		TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
		break;

		case  2:
		TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH0_gc;
		break;
	}
	
	TCC0.CTRLB     = TC0_CCAEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC0.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC0.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC0.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC0.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC0.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC0.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC0.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
	TCC0.CTRLFSET = TC_CMD_RESTART_gc;
}

Which is polled/used by this code:

void CheckPW(){
	char str[15];
	float FreqPWM;
	period1=0;
	pulseWidth1=0;
	period2=0;
	pulseWidth2=0;
	
	init_captureFAN1(1);
	period1 = TCC0.CCA;
	
	//init_captureFAN2();
	//period2 = TCC1.CCA;
	
	init_captureFAN1(2);
	pulseWidth1 = TCC0.CCA;

	//init_captureFAN2();
	//pulseWidth2 = TCC1.CCB;
		
	sprintf(str,"%lu", period1);
	dbgPutStr("Current Period 1: ");
	dbgPutStr(str);
	dbgPutStr("\r\n");
		
	sprintf(str,"%lu", pulseWidth1);
	dbgPutStr("Current PW 1: ");
	dbgPutStr(str);
	dbgPutStr("\r\n");
}

The output is consistently the same:

Frequency value: 159                                                            
Pulse Width value: 127                                                          
Current Period 1: 0                                                             
Current PW 1: 0                                                                 
Current Frequency: 0.0000 Hz                                                    
Current PulseWidth: 4294967295 %                                                
Current Period 1: 127                                                           
Current PW 1: 127                                                               
Current Frequency: 31496.0625 Hz                                                
Current PulseWidth: 100 %                                                       
Current Period 1: 127                                                           
Current PW 1: 127                                                               
Current Frequency: 31496.0625 Hz                                                
Current PulseWidth: 100 % 

When I disable this line in tool:

init_captureFAN1(2);

Then the output is like:

Frequency value: 159                                                            
Pulse Width value: 127                                                          
Current Period 1: 0                                                             
Current PW 1: 0                                                                 
Current Frequency: 0.0000 Hz                                                    
Current PulseWidth: 4294967295 %                                                
Current Period 1: 160                                                           
Current PW 1: 160                                                               
Current Frequency: 25000.0000 Hz                                                
Current PulseWidth: 100 %                                                       
Current Period 1: 160                                                           
Current PW 1: 160                                                               
Current Frequency: 25000.0000 Hz                                                
Current PulseWidth: 100 %       

Both outputs are correct, it is a PW of 127 and a period of 159 (which makes also the Frequency of 25000Hz correct). However I have tried to reset/OFF CTRLD.

Currently I have no idea to make the switch back and forth between FRQ and PW.

An advice what I am missing would be very welcome. 

(Thoughts, do I need to reset the EVSYS?)

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

Found the problem, but I am still unable to solve it the correct way.

 

I call my function with this code:

 

	pulseWidth1 = init_captureFAN1(1);
	period1 = init_captureFAN1(2);

It is calling this function:

uint32_t init_captureFAN1(uint8_t CaptureSelection)
{
	//char str[15];
	//sprintf(str,"%u", CaptureSelection);
	//dbgPutStr("Capture Selection Value: ");
	//dbgPutStr(str);
	//dbgPutStr("\r\n");

	PORTC.DIRCLR   = PIN0_bm;
	PORTC.PIN0CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_BOTHEDGES_gc;
	EVSYS.CH0MUX   = EVSYS_CHMUX_PORTC_PIN0_gc;
	EVSYS.CH0CTRL  = EVSYS_DIGFILT_1SAMPLE_gc;
	TCC0.CTRLD     =TC_EVSEL_CH0_gc;
	// Switch to be able to capture PWM OR FRQ
	switch ( CaptureSelection ) {
		case  1:
		//TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
		TCC0.CTRLD     |= TC_EVACT_FRQ_gc;
		break;

		case  2:
		//TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH0_gc;
		TCC0.CTRLD     |= TC_EVACT_PW_gc;
		break;
	}
	
	TCC0.CTRLB     = TC0_CCAEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC0.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC0.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC0.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC0.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC0.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC0.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC0.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
	TCC0.CTRLFSET = TC_CMD_RESTART_gc;
	//dbgPutStr("\r\n");
	return (TCC0.CCA);
}

When I enable the red colored line I get the FRQ and the PW. When I disable it, I will only get the PW.

I have the idea I need to reset the EVSYS:

EVSYS_DIGFILT_1SAMPLE_gc;

However not yet found a way to do this...

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

You sure Heisenberg's Uncertainty Principal won't prevent you from knowing both the pulse width and frequency? 

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

Unfortunately I cannot use 1 timer, since FAN 1 is connected to PIN0 &1 of PORTC (TC0) and PIN4&5 are also on PORTC (TC1), so I will always use 2 timers.

No you won't.  The FRQ and PW modes on the timer use event channels rather than a specific capture input, so you can have TCC0 measure the FRQ and/or PW of, say, PORTF.3 if you like.

 

By changing either the event channel mux (from Fan A to Fan B) or the event channel the TC is watching (CH0 for Fan A, CH1 for Fan B), you can select which signal a TC is watching.

 

For the sake of argument, let's say that Fan A on on pin D3 and Fan B is on pin E7, and we will use a single event channel.

 

To measure the FRQ of Fan A:

    EVSYS.CH0MUX   = EVSYS_CHMUX_PORTD_PIN3_gc;
    PORTD.PIN3CTRL = PORT_ISC_RISING_gc;
    TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;

To measure the PW of Fan A:

    PORTD.PIN3CTRL = PORT_ISC_BOTHEDGES_gc;
    TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH0_gc;

To measure the FRQ of Fan B:

    EVSYS.CH0MUX   = EVSYS_CHMUX_PORTE_PIN7_gc;
    PORTE.PIN7CTRL = PORT_ISC_RISING_gc;
    TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;

To measure the PW of Fan B:

    PORTE.PIN7CTRL = PORT_ISC_BOTHEDGES_gc;
    TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH0_gc;

This is not complete code, it only shows how you would configure the event channel, pin, and timer mode.  You would want to stop the TC before reconfiguring, then start it again after reconfiguring.  The safest way to do this would be to issue a RESET command to the TC before reconfiguring.  The reset essentially puts the TC in its power-up reset state, that way you know where you are starting from before reconfiguring.

 

 

edit: fixed multiple typos

Greg Muth

Portland, OR, US

Xplained/Pro/Mini Boards mostly

 

Make Xmega Great Again!

 

Last Edited: Sun. Jan 10, 2016 - 09:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

I'm stuck indoors with the flu so I thought I'd give this a go to get my mind off of how crappy I feel...  It compiles, but as for actually working, well, that's another matter entirely.  I find I like writing code I don't have to make sure it does what it's supposed to do!wink

 

/*
 * XMEGA FRQ and PW of Two Signals Using One TC.c
 * 
 * Use a single TC and event channel to measure the frequency and pulse-width
 * of two signals.
 *
 * Fan A is on PORTD.0 and Fan B is on PORTD.1
 * 
 */ 


#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>


// configuration macros
#define TC_CLOCK_SELECT		TC_CLKSEL_DIV64_gc
#define TC_INTERUPT_LEVEL	TC_CCAINTLVL_LO_gc


// these make the code easier to read
#define STATE_FREQ_A		0
#define STATE_FREQ_B		1
#define STATE_PW_A			2
#define STATE_PW_B			3


// these are global so the ISR can see them
volatile uint16_t frq_a;
volatile uint16_t frq_b;
volatile uint16_t pw_a;
volatile uint16_t pw_b;


// a strcture to keep track of status
volatile struct {
	uint8_t state:2;	// current state
	uint8_t fa_rdy:1;	// freq A is ready
	uint8_t fb_rdy:1;	// freq B is ready
	uint8_t pa_rdy:1;	// pw A is ready
	uint8_t pb_rdy:1;	// pw B is ready
	uint8_t rc_req:1;	// reconfigure required
	uint8_t unused:1;	// an unused bit
} status;


// function prototypes
void reconfigure_tc(void);


// inline functions

static inline void init_pmic(void)
{

	PMIC.CTRL = PMIC_HILVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_LOLVLEN_bm;
	sei();

}

static inline void start_tc(void)
{

	// initialize state
	status.state = 0;
	status.fa_rdy = status.fb_rdy = status.pa_rdy = status.pb_rdy = status.rc_req = false;

	// reconfigure TC for current state
	reconfigure_tc();
	
	// set interrupt level, enabling the interrupt
	TCC0.INTCTRLB = TC_INTERUPT_LEVEL;
	
}


static inline bool freq_a_ready(void)
{
	
	bool rdy = status.fa_rdy;
	status.fa_rdy = false;

	return rdy;

}


static inline bool freq_b_ready(void)
{
	
	bool rdy = status.fb_rdy;
	status.fb_rdy = false;

	return rdy;

}


static inline bool pw_a_ready(void)
{
	
	bool rdy = status.pa_rdy;
	status.pa_rdy = false;

	return rdy;

}


static inline bool pw_b_ready(void)
{
	
	bool rdy = status.pb_rdy;
	status.pb_rdy = false;

	return rdy;

}


static inline bool rec_req(void)
{
	
	bool req = status.rc_req;
	status.rc_req = false;

	return req;

}


// reconfigures event channle, pin, and TC based on current state
void reconfigure_tc(void)
{

	// clear the reconfigure flag
	status.rc_req = false;

	// reset the TC
	TCC0.CTRLFSET = TC_CMD_RESET_gc;

	// reconfigure based on current state
	switch(status.state) {
		
		case STATE_FREQ_A:
			EVSYS_CH0MUX = EVSYS_CHMUX_PORTD_PIN0_gc;
			PORTD.PIN0CTRL = PORT_ISC_RISING_gc | PORT_OPC_PULLDOWN_gc;
			TCC0.CTRLD = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
			break;

		case STATE_FREQ_B:
			EVSYS_CH0MUX = EVSYS_CHMUX_PORTD_PIN1_gc;
			PORTD.PIN1CTRL = PORT_ISC_RISING_gc | PORT_OPC_PULLDOWN_gc;
			TCC0.CTRLD = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
			break;

		case STATE_PW_A:
			EVSYS_CH0MUX = EVSYS_CHMUX_PORTD_PIN0_gc;
			PORTD.PIN0CTRL = PORT_ISC_BOTHEDGES_gc | PORT_OPC_PULLDOWN_gc;
			TCC0.CTRLD = TC_EVACT_PW_gc | TC_EVSEL_CH0_gc;
			break;

		case STATE_PW_B:
			EVSYS_CH0MUX = EVSYS_CHMUX_PORTD_PIN1_gc;
			PORTD.PIN1CTRL = PORT_ISC_BOTHEDGES_gc | PORT_OPC_PULLDOWN_gc;
			TCC0.CTRLD = TC_EVACT_PW_gc | TC_EVSEL_CH0_gc;
			break;

	}
	
	// start the TC clock running
	TCC0.CTRLA = TC_CLOCK_SELECT;

}


int main(void)
{

	// init system clock
	// init I/O pins	
	init_pmic();
	start_tc();
	
    while (1) 
    {

		if (rec_req()) reconfigure_tc();

		if (freq_a_ready()) {
			// do something with frq_a
		}
		else if (freq_b_ready()) {
			// do something with frq_b
		}
		else if (pw_a_ready()) {
			// do something with pw_a
		}
		else if (pw_b_ready()) {
			// do something with pw_b
		}
		
		// do other stuff here

    }

}


ISR(TCC0_CCA_vect)
{

	// stop the TC clock
	TCC0.CTRLA = TC_CLKSEL_OFF_gc;

	// set the reconfigure flag
	status.rc_req = true;
	
	// save value and set state
	switch(status.state++) {

		case STATE_FREQ_A:
			frq_a = TCC0.CCA;
			status.fa_rdy = true;
			break;

		case STATE_FREQ_B:
			frq_b = TCC0.CCA;
			status.fb_rdy = true;
			break;

		case STATE_PW_A:
			pw_a = TCC0.CCA;
			status.pa_rdy = true;
			break;

		case STATE_PW_B:
			pw_b = TCC0.CCA;
			status.pb_rdy = true;
			break;

	}

}

 

Greg Muth

Portland, OR, US

Xplained/Pro/Mini Boards mostly

 

Make Xmega Great Again!

 

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

Hi Greg, sorry for my late reply. Had some problems with my Xmega.

Blew up the first one and in between I changed the code and when I got my new experiment board rdy it seemed I made some mistakes resulting in non-working base code.

 

I fixed the code and all is working fine now, the problem I encountered in my code posted earlier is because the time the xmega has to calculate a period or PW is too short. So it will feedback the previous count.

I solved it by putting a usart output in between (which essentially slows the code down). Later I have replaced this with a:

_delay_ms( 1 );

To proceed with my project I have 2 options, dependent on the further possibilities:

  1. I can use my code and put PW and Period on separate IO with the same timer. I can poll the data whenever I feel the need. It will respond with the last available sample.
  2. interrupt driven as your example. This is a nice solution if I would like to capture multiple FANS. However I do not have enough timers, so was thinking if something with a multiplexer would be an option.

 

Still thinking what to do, because I have not yet found how to use a multiplexer in combination with the PWM signals (never used multiplexers).

 

Update: forgot that for same time measurement you need 2 timers (TCC0 and TCC1). However their ports are at the opposite end of the chip. Furthermore the time to capture the event is quite short, so easier would be to investigate the interrupt driven solution from Greg.

Going to do that, since I have a working basis, should not be to hard.

Last Edited: Wed. Jan 27, 2016 - 01:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oke, after some plying around and not getting it to work, I realized what the problem is.

Not yet having a solution however might be someone here that can point me in the right direction.

 

Currently if I have the interrupt running it will work as expected IF there is a PW smaller/lower than 100%. So if I have 150 counts for 100% and my PW is also 150, then there is no interrupt.

How can this be resolved, because now I am waiting and nothing happens, while when its 90% it is working perfectly.

Can I set a TOP value for PW reading, which fires when counting reaches this value?

 

Putting a TCC0.PER = 170; is not helping.

When using my old polling script it actually gives me the correct numbers...

 

Furthermore I have noticed interrupt is only accepting a input capture where the PW is smaller than freq. In my example I have set PER to 159. When using interrupt I can only have a PW of 154 while with polling I can go to 158 or 159 without problems.

I cannot figure out why...

 

So polling works better but has the disadvantage that it needs to be called from main, which I do not want. Any suggestions?

 

Polling Code:

uint32_t init_captureFAN1(uint8_t CaptureSelection)
{
	PORTC.DIRCLR   = PIN0_bm;
	PORTC.PIN0CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_BOTHEDGES_gc;
	EVSYS.CH0MUX   = EVSYS_CHMUX_PORTC_PIN0_gc;
	EVSYS.CH0CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	TCC0.CTRLD     = TC_EVSEL_CH0_gc;
	// Switch to be able to capture PWM OR FRQ
	switch ( CaptureSelection ) {
		case  1:
		//TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
		TCC0.CTRLD     |= TC_EVACT_FRQ_gc;
		break;

		case  2:
		//TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH0_gc;
		TCC0.CTRLD     |= TC_EVACT_PW_gc;
		break;
	}
	
	TCC0.CTRLB     = TC0_CCAEN_bm | TC_WGMODE_NORMAL_gc;
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC0.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC0.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC0.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC0.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC0.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC0.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC0.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
	//dbgPutStr("\r\n");
	_delay_ms( 1 );
	return (TCC0.CCA);
}

 

 

Interrupt code (not returning the PW when PW = 100%):

ISR(TCC0_CCA_vect)
{
	// set the reconfigure flag
	status.rc_req = true;

	// save value and set state
	switch(status.state) {
		case STATE_FREQ_A:
			// configure for next type of interrupt
			frq_a = TCC0.CCA;
			status.fa_rdy = true;
			break;
		case STATE_PW_A:
			// configure for next type of interrupt
			pw_a = TCC0.CCA;
			status.pa_rdy = true;
			break;
	}
}

void reconfigure_tc(void)
{

	// clear the reconfigure flag
	status.rc_req = false;
		
	// reset the TC
	//TCC0.CTRLFSET = TC_CMD_RESET_gc;
	//TCC0.CTRLA = TC_CLKSEL_OFF_gc;
	
	// reconfigure based on current state

	PORTC.DIRCLR   = PIN0_bm;
	PORTC.PIN0CTRL = PORT_OPC_PULLDOWN_gc | PORT_ISC_BOTHEDGES_gc;
	EVSYS.CH0MUX   = EVSYS_CHMUX_PORTC_PIN0_gc;
	EVSYS.CH0CTRL  = EVSYS_DIGFILT_8SAMPLES_gc;
	TCC0.CTRLD     = TC_EVSEL_CH0_gc;
	// Switch to be able to capture PWM OR FRQ
	switch ( status.state ) {
		case  STATE_FREQ_A:
		//TCC0.CTRLD     = TC_EVACT_FRQ_gc | TC_EVSEL_CH0_gc;
		TCC0.CTRLD     |= TC_EVACT_FRQ_gc;
		//TCC0.CTRLD     |= TC_EVACT_PW_gc;
		//TCC0.PER= 0xFFFF;
		break;

		case  STATE_PW_A:
		//TCC0.CTRLD     = TC_EVACT_PW_gc | TC_EVSEL_CH0_gc;
		TCC0.CTRLD     |= TC_EVACT_PW_gc;
		//TCC0.CNT= 0;
		//TCC0.TOP = 150;
		break;
	}
	
	TCC0.CTRLB     = TC0_CCAEN_bm | TC_WGMODE_NORMAL_gc;

	// start the TC clock running 
	
	switch ( ICPreScale ) {
		
		case  1:
		TCC0.CTRLA     = TC_CLKSEL_DIV1_gc;
		break;

		case  2:
		TCC0.CTRLA     = TC_CLKSEL_DIV2_gc;
		break;

		case 4:
		TCC0.CTRLA     = TC_CLKSEL_DIV4_gc;
		break;
		
		case 8:
		TCC0.CTRLA     = TC_CLKSEL_DIV8_gc;
		break;
		
		case 64:
		TCC0.CTRLA     = TC_CLKSEL_DIV64_gc;
		break;
		
		case 256:
		TCC0.CTRLA     = TC_CLKSEL_DIV256_gc;
		break;
		
		case 1024:
		TCC0.CTRLA     = TC_CLKSEL_DIV1024_gc;
		break;
	}
	TCC0.CTRLFSET = TC_CMD_RESTART_gc;
}

 

 

 

 

Last Edited: Thu. Jan 28, 2016 - 09:14 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am bumping this topic, because I am out of ideas how to fix this.

Why is interrupt driver measurement not working, while polling is?

Really need some more expert eyes looking at it.

(If more code is required please let me know)