Interrupt on falling voltage on analog pin doesn't work

I have stucked and need help.

I've tried to make a simple project with analog input but I can't even catch an interrupt from analog pin A2.

I need to trigger some procedure then A2 voltage goes below 1.1 volt. So I've wrote this code in setup procedure:

ACSR = 0b01001110; // ACD=0, ACBG=1, ACO=0, ACI=0, ACIE=1, ACIC=1 and select falling edge

DIDR1 |= (1<< AIN0D);
DIDR1 |= (1<< AIN1D);

ADCSRB |= (1<<ACME); //ACME (connect to analog input)
ADCSRA |= (0<<ADEN); //ADEN (disable converter)

//select A2 as input pin
ADMUX |= (0<<MUX2);
ADMUX |= (1<<MUX1);
ADMUX |= (0<<MUX0);

and then I added interrupt procedure:

ISR(ANALOG_COMP_vect){
Serial.println("This is an analog interrupt!");

}

but ISR procedure not run at all.

What is wrong with this code?

NEVER print from within an interrupt. Printing depends on interrupts, and they are off while an ISR is executing.

The usual approach is to have the ISR set a global flag (declared volatile) that the interrupt has taken place, and check for that in the main program.

volatile int flag=0;  //declared outside of setup() and loop()
ISR(ANALOG_COMP_vect){
  flag=1;
}

Have a look here on how to use the voltage comparator.

Riva:
Have a look here on how to use the voltage comparator.

Thank you very much, but this article describes comparison with digital (pwm) pin, but I need to make a comparison with analog pin.... So, this question is still open...

GregoryRusakov:
Thank you very much, but this article describes comparison with digital (pwm) pin, but I need to make a comparison with analog pin.... So, this question is still open...

I think you have misunderstood as these are pins for doing analogue comparator readings unless you turn off the ADC and use an analogue pin as the negative input to the comparator and the bandgap reference for the positive side (can you use 1.1v for this?).

The link provided by Riva to Nick Gammon's article on how to use the analog comparator does exactly what you want, except using one other pin. Get his example working first, then modify it to use another pin.

Try this snippet and see if it works

// defines for setting and clearing register bits
#ifndef cbi
  #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
  #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


cbi(ADCSRA, ADEN);        // Disable ADC (Must be done before ACME is set)
sbi(ADCSRB, ACME);        // Enable AC multiplexer

cbi(ACSR, ACIE);          // Disable comparator interrupt (to set ACD)

cbi(ACSR, ACD);           // Enable comparator
sbi(ACSR, ACBG);          // Select bandgap as pos comparator input (preset to 1.1V first)
cbi(ACSR, ACO);
cbi(ACSR, ACI);           // Clear pending interrupt if there is one
cbi(ACSR, ACIC);          // Clear input capture enable
sbi(ACSR, ACIS1);
cbi(ACSR, ACIS0);         // Comparator interrupt on falling edge
sbi(ACSR, ACIE);          // Enable comparator interrupt again

sbi(ADMUX, REFS1);
sbi(ADMUX, REFS0);        // Select 1.1V reference
cbi(ADMUX, MUX3);
cbi(ADMUX, MUX2);
sbi(ADMUX, MUX1);
cbi(ADMUX, MUX0);         // Select A2 as comparator negative input


ISR(ANALOG_COMP_vect){
  // code here
}

jremington:
The link provided by Riva to Nick Gammon's article on how to use the analog comparator does exactly what you want, except using one other pin. Get his example working first, then modify it to use another pin.

I don't think so, becouse that diagram uses digital pins D6 and D7 (this works ok) but I need to use analog pin A2 (instead digital D7) and Bandgap reference (ACBG) with 1.1 volt instead of PWM digital D6.

Please take a look at the diagram (its from Atmega datasheet).

https://drive.google.com/file/d/0B5SzbOYxLdVFZXc0Mi1zLUhGamM/view?usp=sharing

image attached here:

analog interrupts.png

Are we helping you to pass an course/exam?

You are confused by the D6 and D7 designations on the connection (PORTD) -- those refer to AIN0 and AIN1, which connect to the analog comparator. You MUST use AIN0 as the reference, but for AIN1 you can use either PORTD pin 7 or one of the analog pins via the multiplexer.

Here is what the ATmega data sheet says about alternative functions of D6 and D7:

• AIN1/OC2B/PCINT23 – Port D, Bit 7
AIN1, Analog Comparator Negative Input. Configure the port pin as input with the internal pull-up
switched off to avoid the digital port function from interfering with the function of the Analog
Comparator.

• AIN0/OC0A/PCINT22 – Port D, Bit 6
AIN0, Analog Comparator Positive Input. Configure the port pin as input with the internal pull-up
switched off to avoid the digital port function from interfering with the function of the Analog
Comparator.

Upon power up, D6 and D7 are automatically configured appropriately. As I said, get Nick Gammon's code working before you modify it to use an analog pin for the sensed input.

Riva:
Try this snippet and see if it works

// defines for setting and clearing register bits

#ifndef cbi
  #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
  #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

cbi(ADCSRA, ADEN);        // Disable ADC (Must be done before ACME is set)
sbi(ADCSRB, ACME);        // Enable AC multiplexer

cbi(ACSR, ACIE);          // Disable comparator interrupt (to set ACD)

cbi(ACSR, ACD);          // Enable comparator
sbi(ACSR, ACBG);          // Select bandgap as pos comparator input (preset to 1.1V first)
cbi(ACSR, ACO);
cbi(ACSR, ACI);          // Clear pending interrupt if there is one
cbi(ACSR, ACIC);          // Clear input capture enable
sbi(ACSR, ACIS1);
cbi(ACSR, ACIS0);        // Comparator interrupt on falling edge
sbi(ACSR, ACIE);          // Enable comparator interrupt again

sbi(ADMUX, REFS1);
sbi(ADMUX, REFS0);        // Select 1.1V reference
cbi(ADMUX, MUX3);
cbi(ADMUX, MUX2);
sbi(ADMUX, MUX1);
cbi(ADMUX, MUX0);        // Select A2 as comparator negative input

ISR(ANALOG_COMP_vect){
  // code here
}

Thanks, this code works, but a bit strange. It works only if negative input connected to A0 not to A2. So this lines:

cbi(ADMUX, MUX3);
cbi(ADMUX, MUX2);
sbi(ADMUX, MUX1);
cbi(ADMUX, MUX0); // Select A2 as comparator negative input

are not working... and I can't select A2, it always monitoring voltage from A0 only.

Riva:
Are we helping you to pass an course/exam?

No, the image above i found in the Internet.

GregoryRusakov:
Thanks, this code works, but a bit strange. It works only if negative input connected to A0 not to A2. So this lines:

cbi(ADMUX, MUX3);
cbi(ADMUX, MUX2);
sbi(ADMUX, MUX1);
cbi(ADMUX, MUX0); // Select A2 as comparator negative input

are not working... and I can't select A2, it always monitoring voltage from A0 only.

I have no idea why that does not work. Only thing I can suggest is move the mux select code above the comparator enable section. Also just noticed the datasheet shows only mux2:0 for comparator but there are mux3:0 so does this mean you cannot use all analogue pins? Have commented out mux3 just in case.

// defines for setting and clearing register bits
#ifndef cbi
  #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
  #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


cbi(ADCSRA, ADEN);        // Disable ADC (Must be done before ACME is set)
sbi(ADCSRB, ACME);        // Enable AC multiplexer

cbi(ACSR, ACIE);          // Disable comparator interrupt (to set ACD)

sbi(ADMUX, REFS1);
sbi(ADMUX, REFS0);        // Select 1.1V reference
//cbi(ADMUX, MUX3);
cbi(ADMUX, MUX2);
sbi(ADMUX, MUX1);
cbi(ADMUX, MUX0);         // Select A2 as comparator negative input

cbi(ACSR, ACD);           // Enable comparator
sbi(ACSR, ACBG);          // Select bandgap as pos comparator input (preset to 1.1V first)
cbi(ACSR, ACO);
cbi(ACSR, ACI);           // Clear pending interrupt if there is one
cbi(ACSR, ACIC);          // Clear input capture enable
sbi(ACSR, ACIS1);
cbi(ACSR, ACIS0);         // Comparator interrupt on falling edge
sbi(ACSR, ACIE);          // Enable comparator interrupt again



ISR(ANALOG_COMP_vect){
  // code here
}

Riva, thank you very much for the support.
I've found my own error. I've added line
"analogRead(A0);" in the main loop. And with this line in the main loop I could receive voltage only from A0 despite selected in MUX bites.

After removing analogRead(A0) from the main loop MUX selection works fine. But I've got some strange behavior - if I touch other analog pins with ground or even by hands Arduino fully stops until reset.

P.S. Sorry for my English, English is not my first language.

GregoryRusakov:
Riva, thank you very much for the support.
I've found my own error. I've added line
"analogRead(A0);" in the main loop. And with this line in the main loop I could receive voltage only from A0 despite selected in MUX bites.
Ah, that makes sense as it will have re-programmed the mux.

After removing analogRead(A0) from the main loop MUX selection works fine. But I've got some strange behavior - if I touch other analog pins with ground or even by hands Arduino fully stops until reset.
All I can suggest is debugging with serial or LED to see if code stalls at a fixed point or just random. If it's random then I don't know what to suggest apart from ensuring signals are within spec for the arduino pins and maybe decouple a bit more. If it's a fixed point then maybe something in missing in code.
You could always use AIN0/AIN1 if they are spare.
Maybe you have other errors in the main code.

P.S. Sorry for my English, English is not my first language.
I'm understanding you fine.