Analog Comparator falling edge setup

Hi,

this is the first time I'm using the analog comparator. I'm trying to compare AIN0 and AIN1. A pulse is applied to AIN0 and a rising ramp to AIN1. Once the value of AIN1 is higher than the maximum of the pulses on AIN0, no more falling edges should occur, right? So my goal is to trigger an interrupt on every falling edge of the comparator and detect a missing edge to calculate my results.

Setup:

  // Analog Comparator Multiplexed Input:
  // If ACME is cleared or ADEN is set, AIN1 is applied to the negative input to the Analog Comparator.
  ADCSRB &= ~(1<<ACME);
  
  // Setup of ACSR (Comparator Register)
  // Bit 7(MSB)  : ACD:          Analog Comparator Disable;               When this bit is written logic one, the power to the Analog Comparator is switched off. This bit can be set at any time to turn off the Analog Comparator. This will reduce power consumption in Active and Idle mode. When chang- ing the ACD bit, the Analog Comparator Interrupt must be disabled by clearing the ACIE bit in ACSR. Otherwise an interrupt can occur when the bit is changed.
  // Bit 6       : ACBG:         Analog Comparator Bandgap Select;        When this bit is set, a fixed bandgap reference voltage replaces the positive input to the Analog Comparator. When this bit is cleared, AIN0 is applied to the positive input of the Analog Comparator. When the bandgap reference is used as input to the Analog Comparator, it will take a certain time for the voltage to stabilize. If not stabilized, the first conversion may give a wrong value.
  // Bit 5       : ACO:          Analog Comparator Output;                The output of the Analog Comparator is synchronized and then directly connected to ACO. The synchronization introduces a delay of 1 - 2 clock cycles.
  // Bit 4       : ACI:          Analog Comparator Interrupt Flag;        This bit is set by hardware when a comparator output event triggers the interrupt mode defined by ACIS1 and ACIS0. The Analog Comparator interrupt routine is executed if the ACIE bit is set and the I-bit in SREG is set. ACI is cleared by hardware when executing the corresponding interrupt handling vector. Alternatively, ACI is cleared by writing a logic one to the flag.
  // Bit 3       : ACIE:         Analog Comparator Interrupt Enable;      When the ACIE bit is written logic one and the I-bit in the Status Register is set, the Analog Comparator interrupt is activated. When written logic zero, the interrupt is disabled.
  // Bit 2       : ACIC:         Analog Comparator Input Capture Enable;  When written logic one, this bit enables the input capture function in Timer/Counter1 to be triggered by the Analog Comparator. The comparator output is in this case directly connected to the input capture front-end logic, making the comparator utilize the noise canceler and edge select features of the Timer/Counter1 Input Capture interrupt. When written logic zero, no connection between the Analog Comparator and the input capture function exists. To make the comparator trigger the Timer/Counter1 Input Capture interrupt, the ICIE1 bit in the Timer Interrupt Mask Register (TIMSK1) must be set.
  // Bit 1,0(LSB): ACIS1, ACIS0: Analog Comparator Interrupt Mode Select; These bits determine which comparator events that trigger the Analog Comparator interrupt.
  /* 
     ACIS1 | ACIS0 | Interrupt Mode
     0       0       Comparator Interrupt on Output Toggle.
     0       1       Reserved.
     1       0       Comparator Interrupt on Falling Output Edge.
     1       1       Comparator Interrupt on Rising Output Edge.
  */
  ACSR = B00011010;
  
  // Disable digital input buffer
  // AIN1, AIN0 Digital Input Disable: When this bit is written logic one, the digital input buffer on the AIN1/0 pin is disabled. The corresponding PIN Reg- ister bit will always read as zero when this bit is set. When an analog signal is applied to the AIN1/0 pin and the digital input from this pin is not needed, this bit should be written logic one to reduce power consumption in the dig- ital input buffer.
  DIDR1  |=  (1<< AIN0D);
  DIDR1  |=  (1<< AIN1D);

Will this work or am I doing something wrong?
Thanks for your help!

I don't see anything wrong with that code. Is it working?

I couldn't test it yet, will report back. Thanks!

I cannot tell if yours work but this is what I use:

//reset the comparator
void comp_init(void) {
	//ain0/ain1 assumed to be input

	//configure output pin

	//configure the analog comparator
#if defined(DIDR)
	DIDR = 	(1<<AIN1D) |			//disable ain1's digital input
			(1<<AIN0D)				//disable ain0's digital input
			;
#endif

#if defined(DIDR1)
	DIDR1 = (1<<AIN1D) |			//disable ain1's digital input
			(1<<AIN0D)				//disable ain0's digital input
			;
#endif

	//configure ACSRB if available
#if defined(ACSRB)
	ACSRB &=~(0<<ACME);				//disable analog comparator multiplexer
#endif

	//configure ACSR
	ACSR &=~(1<<ACIE);				//disable interrupt
	ACSR = 	(0<<ACD) |				//analog comparator power on
#if defined(ACBG)
			(0<<ACBG) |				//bandgap reference not selected. Use AIN1 for non-inverting input
#endif
			(1<<ACI) |				//clear acif by writing '1' to it
			(0<<ACIE) |				//interrupt not yet enabled
			(0<<ACIC) |				//analog comparator input capture disabled
			(0<<ACIS1) | (0<<ACIS0)	//interrupt on output toggle
			//(0<<ACIS1) | (1<<ACIS0)	//reserved
			//(1<<ACIS1) | (0<<ACIS0)	//interrupt on falling output edge
			//(1<<ACIS1) | (1<<ACIS0)	//interrupt on rising output edge
			;

	ACSR |= (1<<ACIE);				//enable the interrupt

	//rest comp_ptr
	_comp_isr_ptr = _comp_empty_handler;
}

It has other stuff (user-installed interrupt handler) but the basic structure is the same. I was targetted for a bigger chip but should be able to compile for yours as well.

Will this work

I can confirm for you that it will work.

By the way, I have another question on that topic.
I'm using Timer1 to generate an interrupt every Xµs that triggers the pulse for the comparator. That means that the comparator interrupt will be triggered just before the other interrupt finishes. Somewhere I heard, that interrupts aren't allowed within interrupts. Is that true? If the Arduino "remembers" that there was another(different) interrupt during the timer interrupt and executes it afterwards, that would be fine because the comparator interrupt only increments a value and timing isn't important, as long as it doesn't forget the comparator interrupt.

Somewhere I heard, that interrupts aren't allowed within interrupts.

You can nest interrupts. But that's not for the faint of heart.

In your case, the ACI flag will be set while the timer isr is being serviced. The execution will return to where it was before the timer isr was executed, and then jump right back to the analog comparator isr (assuming no other interrupt flags are set).

I'm using Timer1 to generate an interrupt every Xµs that triggers the pulse for the comparator.

That's a weird set up.

dhenry:

I'm using Timer1 to generate an interrupt every Xµs that triggers the pulse for the comparator.

That's a weird set up.

The pulse controls external circuitry and the result of that circuit(very fast) triggers the comparator interrupt. Still weird? How could I improve it?

So the timer will trigger an external device to create a voltage which the analog comparator is going to compare against?

What does the whole thing do?

dhenry:
So the timer will trigger an external device to create a voltage which the analog comparator is going to compare against?

What does the whole thing do?

Measure the ESR of a cap

I assume that you aren't using a bridge here.

The typical approach then is to charge up a fully depleted cap and measure the current during the initial charge up. The comparator is probably not a very good tool for this.

Unless you have a different approach?

dhenry:
I assume that you aren't using a bridge here.

The typical approach then is to charge up a fully depleted cap and measure the current during the initial charge up. The comparator is probably not a very good tool for this.

Unless you have a different approach?

I feed the DUT with a constant current(chopped by the generated pulse), amplify the generated voltage over the DUT and compare it to the voltage of a reference cap that is being charged by a constant current(==> linear rising voltage). If you want to read more, here's a link: http://members.ozemail.com.au/~bobpar/k7214.pdf I basically just added some stuff I wanted and converted it to be Arduino based.

But my question was if you know a better way with only one interrupt :wink:

But my question was if you know a better way with only one interrupt

I think his approach is quite convoluted - C10 for example only serves to tell time, which the mcu can do with ease.

The basic theory here is that when you charge up a capacitor with a constant current, the voltage rise

deltaV = I * Tc / C, assuming deltaV is sufficiently small and ESR is sufficiently small.

When you discharge, the residual voltage at Td is

Vd = deltaV * exp(-Td / (ESR * C)).

So after each charge/discharge cycle, the voltage across the capacitor has gone up by Vd.

If, after n such cycles, the voltage across the capacitor is V, you have

V/n = (I * Tc / C) * exp (-Td / (ESR * C)), assuming of course V is sufficiently small.

If you know V, n, I, Tc, Td, C, you can solve for ESR from the above.

You can take his hardware, and you can rewrite the software:

  1. charge the capacitor at I for Tc;
  2. discharge the capacitor for Td;
  3. go back to 1 for n times;
  4. measure the voltage across the capacitor, V;
  5. calculate ESR.

No need to have a comparator.

The comparator(and C10) is essentially there for an extremely fast analogRead(), which otherwise wouldn't be possible fast enough.
The goal of an ESR meter is to make measurements in circuit possible and if you charge the capacitor you'd activate other stuff in there.

dhenry:
If you know V, n, I, Tc, Td, C, you can solve for ESR from the above.

Where should the µC know C from? I don't want to enter it every time.

Anyhow, can I improve my software solution or do I require two interrupts, without changes to the way the measurement is done?

The comparator(and C10) is essentially there

No.

Where should the µC know C from?

The meter you are trying to build does not measure ESR: it provides an indication of ESR, in conjunction with a known C.

Anyhow, can I improve my software solution or do I require two interrupts, without changes to the way the measurement is done?

Yes. This would be what I would do:

  1. measure the voltage on the capacitor, V0;
  2. charge up the capacitor for a known period of time, Tc;
  3. measure the voltage on the capacitor, V1; C = (V1-V0) / (I * Tc);
  4. discharge the capacitor for a known period of time, Td;
  5. measure the voltage, V2: V2 = V1 * exp (-Td / (ESR * C)), solve for ESR.

Done.

It uses the same hardware.

Somehow I knew that I shouldn't have said what I'm building because there is always someone who just wants to argue instead of helping someone.
Btw, the Meter has been sold tens on thousands of times (as a kit and prebuild) and is still being sold, so the method of "indication" can't be too wrong.

daywalkerdha:
Somewhere I heard, that interrupts aren't allowed within interrupts. Is that true? If the Arduino "remembers" that there was another(different) interrupt during the timer interrupt and executes it afterwards, that would be fine ...

It wouldn't work very well if it could only handle one interrupt source at a time. Yes it will remember interrupt events. The ISR will not be entered until the current one completes, and one more instruction is executed. Then the highest priority outstanding interrupt (if any) will be serviced.

Thanks!

I have another question on timing inside an interrupt. Currently a

bitWrite(PORTD, measurementRange, 0);

is just called multiple times inside a timer interrupt to make a pulse with a width of somewhere between 4µs and 15µs(adjustable) because i read here » Pin I/O performance » JeeLabs that it takes about 1µs to execute a bitWrite(). Is there a better way for such short timings? It doesn't have to be that precise, just the same amount of delay every time once it's set.