Timer1 input compare triggered by Analog Compare

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

Has someone used timer1 input compare triggered by analog compare?
I did it, but it work only if I set (TIMSK1=0x20), the drawback is when ICP1 change ( drive a 7 segment display) the interrupt fire again.
The question: There is a possibility to fire icp1 interrupt only by analog comparator while ICP1 pin changes make no effects? (pin14 in my atmega88).
If yes, someone may show me how?

Now the setup working is:
TCCR1B=0x04
TIMSK1=0x20
ACSR=0x0E

All other registers for TIMER1 and ANALOG COMPARE are 0x00.

Many thanks Fabio

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

A deeper reading of data sheet (page 243 and 115) seems timer1 handles as not mutual exclusion the two source.
ICP1 pin is always connected to Timer1 internal icp circuit and analog compare int source may be connected, like or logic gate...
I'm wrong?
Then I have to disable ICP1 int when I have to change PORTB.0 clear Int flag and re-arm it...
Good job ATMEL! :evil:

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

Quote:

Has someone used timer1 input compare triggered by analog compare?

Yes, in a production application.
Quote:

Now the setup working is:
TCCR1B=0x04
TIMSK1=0x20
ACSR=0x0E

So, that is ACSR = (1<<ACIE)|(1<<ACIC)|(1<<ACIS1);

You have set up to trigger the analog comparator interrupt on the falling edge, and to trigger the ICP interrupt on the falling edge of the analog comparator ACO.

Why do you want both interrupts for the same event?

For rising edge, I simply have

// Analog Comparator initialization
// Analog Comparator: On
// Digital input buffers on AIN0: Off, AIN1: Off
DIDR1=0x03;
// Analog Comparator Input Capture by Timer/Counter 1: On
ACSR=0x04;
ADCSRB=0x00;
...
			TCCR1A = 0;						// Normal mode, no connected pins

			TCCR1B =	(1 << ICES1) |		// Input capture edge select--rising
						(1 << CS10);	 	// Normal mode; prescaler of 1
...
					// Set up the ICP on timer1, routed from analog comparator
					TIFR1 = (1 << ICF1);	// clear any previous trigger ...
					TIMSK1 |= (1 << ICIE1);	// ... and go!

I cannot remember getting "false hits" from ICP1 pin. I would have noticed it in this app as ICP1/PB0 is an output that changes often during the operation of the device.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Thank theusch for your reply.
On port B pin 0 (ICP1) there is one segment of 7 segment display so every time I change this segment I have a fake ICP trigger...

Quote:
So, that is ACSR = (1<<ACIE)|(1<<ACIC)|(1<<ACIS1);

No I don't use analog input compare int. Correct.

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

Quote:

No I don't use analog input compare int. Correct.

Then why have you configured for it?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Ask to compiler automatic peripheral configurator code generator...

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

runblade wrote:
A deeper reading of data sheet (page 243 and 115) seems timer1 handles as not mutual exclusion the two source.
ICP1 pin is always connected to Timer1 internal icp circuit and analog compare int source may be connected, like or logic gate...
How do you come to that conclusion?

The datasheet is very clear on the matter. The second page you quote, page 115, contains figure 16-3. In it is clearly shown a multiplexer that selects only one of two inputs, ICPn and ACO, selected by ACIC. How is this an 'OR'?

Quote:
I'm wrong?
Yes.

I've written a test program based on your posted code snippet:

#include 
#include 
#include 

volatile uint8_t compf = 0;
volatile uint8_t captf = 0;

ISR(ANALOG_COMP_vect) {
  compf = 1;
}

ISR(TIMER1_CAPT_vect) {
  captf = 1;
}

int __attribute__ ((__OS_main__)) main(void) {

  // Debug serial (use your own routines)
  serial_configure();

  // ICP1 and AIN1 outputs
  DDRD |= (1<<PD7);
  DDRB |= (1<<PB0);
  
  // Mode 0, prescaler = 256, capture on falling edge
  TCCR1B = (1<<CS12);
  TIMSK1 = (1<<ICIE1);
  
  // AIN0 = Vbg, ACO to input capture
  ACSR = (1<<ACBG) | (1<<ACIE) | (1<<ACIC) | (1<<ACIS1);
  
  // Clear all flags before beginning
  TIFR1 = (1<<ICF1);
  ACSR |= (1<<ACI);
  sei();
  
  // Loop
  while(1) {
    // Report
    printf("AIN1=%d ICP1=%d ACSR=0x%02X ICR1=0x%04X compf=%d captf=%d\r\n",
           !!(PIND&(1<<PD7)), !!(PINB&(1<<PB0)), ACSR, ICR1, compf, captf);
    // Clear flags
    compf = 0;
    captf = 0;
    PIND = (1<<PD7);  // Toggle AIN1
    //PINB = (1<<PB0);  // Toggle ICP1
  }

}

When toggling AIN1, I get the expected results:

AIN1=0 ICP1=0 ACSR=0x6E ICR1=0x0000 compf=0 captf=0
AIN1=1 ICP1=0 ACSR=0x4E ICR1=0x0F00 compf=1 captf=1
AIN1=0 ICP1=0 ACSR=0x6E ICR1=0x0F00 compf=0 captf=0
AIN1=1 ICP1=0 ACSR=0x4E ICR1=0x2D03 compf=1 captf=1

Both ISRs run, ACO reflects the state of AIN1, and ICR1 shows captures on falling edges of ACO.

When toggling ICP1 with:

    //PIND = (1<<PD7);  // Toggle AIN1
    PINB = (1<<PB0);  // Toggle ICP1

... I also get the expected results:

AIN1=0 ICP1=0 ACSR=0x6E ICR1=0x0000 compf=0 captf=0
AIN1=0 ICP1=1 ACSR=0x6E ICR1=0x0000 compf=0 captf=0
AIN1=0 ICP1=0 ACSR=0x6E ICR1=0x0000 compf=0 captf=0
AIN1=0 ICP1=1 ACSR=0x6E ICR1=0x0000 compf=0 captf=0

Again, ACO reflects the state of AIN1 (which is unchanging), but no interrupts fire, and no capture occurs.

Quote:
Then I have to disable ICP1 int when I have to change PORTB.0 clear Int flag and re-arm it...
Post a succinct, complete program that elicits the observed behaviour. I'd bet that by doing so you'll see the problem.
Quote:
Good job ATMEL! :evil:
I would have a look at your circuit and your code a bit more carefully before accusing Atmel ;)

JJ

///////////////////////////////////////////////////////////////////////////////

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Good job ATMEL reference to indicate the clear explanation how works analog comparator ICP1 on data sheet.

For to be sure you need to to try...I need to be sure how function reading data sheet, not by experiment.

Cleared this, I discover

 ACSR&=~(1<<ACIC)

generate ICP1 interrupt...

Thanks to everyone

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

Quote:

Good job ATMEL reference to indicate the clear explanation how works analog comparator ICP1 on data sheet.

Quote:
16.2.1 Registers
The Timer/Counter (TCNT1), Output Compare Registers (OCR1A/B), and Input Capture Register (ICR1) are all 16-bit registers. ...

The Input Capture Register can capture the Timer/Counter value at a given external (edge triggered) event on either the Input Capture pin (ICP1) or on the Analog Comparator pins (See ”Analog Comparator” on page 239) The Input Capture unit includes a digital filtering unit (Noise Canceler) for reducing the chance of capturing noise spikes.

There is a note about why you got the ICP hits when working with PB0:

Quote:
An Input Capture can be triggered by software by controlling the port of the ICP1 pin.

Quote:

Cleared this, I discover

Code:
ACSR&=~(1<<ACIC)


??? That sounds backwards. To trigger ICP from the analog comparator, you would set ACIC not clear it.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

runblade wrote:
Good job ATMEL reference to indicate the clear explanation how works analog comparator ICP1 on data sheet.
But the datasheet is clear, as I showed you above.

[edit] I see @theusch beat me to it with yet another clear quote from the datsheet! [/edit]

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

In my code I need to switch off the acquisition of icp1 for change between two source, and this cause an ICP1 interrupt.

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

runblade wrote:
In my code I need to switch off the acquisition of icp1 for change between two source, and this cause an ICP1 interrupt.
Post your code.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
     
      ONCH1 
      RpmThreshold=0;      
      ACSR|=(1<<ACIC);
      CountNoIcp=0;
      while ((RpmThreshold < 2) && (CountNoIcpNOTICTHRED)    
      CH1_Value=0;               
      OFFCH
      
      ONCH2       
      RpmThreshold=0;
      ACSR|=(1<<ACIC);
      CountNoIcp=0;
      while ((RpmThreshold < 2) && (CountNoIcpNOTICTHRED) CH2_Value=0;              
      OFFCH 

In this case after ACSR&=~(1<<ACIC); start ICP1 Interrupt. (two time).

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

runblade wrote:

     
      ONCH1 
...etc.

In this case after ACSR&=~(1<<ACIC); start ICP1 Interrupt. (two time).

I'm afraid that's not enough to make sense of.

Your stated trouble was:

Quote:
...the drawback is when ICP1 change ( drive a 7 segment display) the interrupt fire again.
This is simply not the case, as clearly stated in the datasheet, and demonstrated by the test program above.

You need to restate your problem.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Avr studio in debugging mode (debug wire mode) set icp1 interrupt flag immediately after ACSR&=~(1<<ACIC).

#define ONCH1 PORTC.4=1; PORTC.5=0; // activate buffer channel 1
#define ONCH2 PORTC.5=1; PORTC.4=0; // activate buffer channel 2
#define OFFCH PORTC.5=1; PORTC.4=1;

for select/dis-active input channel..

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

I dare say when you change things it causes a capture event as the level changed.

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

So you're twiddling bits on PORTC. How is this relevant to the comparator? Have you set ACME in ADCSRB? Or do you have some external connection between PC4/PC5 and AIN0/AIN1?

As for ICF1 getting set when changing ACIC from 1 to 0, that will happen iff (ACO == 1) && (ICP1 == ICES1 == 0).

A change in ACIC (clear or set) changes the selection of input to the capture unit. The two inputs are ACO and ICP1. If the state of those two inputs is different at the moment you switch inputs, the capture unit sees that as an edge like any other.

If ACIC = 1, the capture unit is seeing ACO as an input. If ICES1 = 0 (as it seems to in your case), the capture unit will trigger on a transtion from 1 to 0. So if ACO = 1 and ICP1 is low at the moment you clear ACIC (selecting ICP1 as an input), then the capture unit will see a transition from 1 to 0 which will trigger a capture and set ICF1.

This is expected, and the datasheet clearly warns you:

In the datasheet, Atmel wrote:
16.6.1 Input Capture Trigger Source
    The main trigger source for the Input Capture unit is the Input Capture pin (ICP1). Timer/Counter1 can alternatively use the Analog Comparator output as trigger source for the Input Capture unit. The Analog Comparator is selected as trigger source by setting the Analog Comparator Input Capture (ACIC) bit in the Analog Comparator Control and Status Register (ACSR). Be aware that changing trigger source can trigger a capture. The Input Capture Flag must therefore be cleared after the change.

    Both the Input Capture pin (ICP1) and the Analog Comparator output (ACO) inputs are sampled using the same technique as for the T1 pin (Figure 17-1 on page 139). The edge detector is also identical. However, when the noise canceler is enabled, additional logic is inserted before the edge detector, which increases the delay by four system clock cycles. Note that the input of the noise canceler and edge detector is always enabled unless the Timer/Counter is set in a Waveform Generation mode that uses ICR1 to define TOP.

    An Input Capture can be triggered by software by controlling the port of the ICP1 pin.

You haven't posted enough code for me to understand why you think you need to set ACIC and then clear it again.

If you really do, you can handle the false trigger (as you have discovered) by temporarily disabling the capture interrupt:

TIMSK1 &= ~(1<<ICIE1);
ACSR &= ~(1<<ACIC);
TIFR = (1<<ICF1);
TIMSK1 |= (1<<ICIE1);

Since you may see a similar problem when setting ACIC, you can do the same there:

TIMSK1 &= ~(1<<ICIE1);
ACSR |= (1<<ACIC);
TIFR = (1<<ICF1);
TIMSK1 |= (1<<ICIE1);

This prevents the capture ISR from running, but doesn't prevent the capture itself: ICR1 will be updated at the moment ACIC is cleared.

One way to avoid the capture is to change the capture edge before clearing ACIC:

TCCR1B |= (1<<ICES1);
ACSR &= ~(1<<ACIC);
TCCR1B &= ~(1<<ICES1);

This works because the capture unit doesn't experience a false trigger when changing ICES1.

You could instead change the state of ICP1 before the change:

PORTD.0=1;
ACSR &= ~(1<<ACIC);
PORTD.0=0;

... but of course that will affect anything connected to PD0.

All of these 'countermeasures' have their drawbacks. If a legitimate capture event comes along during the coutermeasure, it may be lost. In the case of the last two, a false trigger might still occur if an opposite edge occurs during the countermeasure. You need to select the appropriate technique to your applications.

You may also need to ensure atomicity, depending on your use of other interrupts.

By the way, the input capture unit doesn't care about ACIS[1:0]. Those are for the comparator interrupt only.

If you stated your goal instead of posting indecipherable snippets of code, someone might be able to suggest a better overall solution. What is connected to the comparator? What are you actually trying to capture? Are you trying to capture more than one kind of event?

At no point have you posted code that shows for what purpose you're using the captured values in ICR1. I'd guess it has something to do with measuring RPM, but I'm tired of guessing.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]