Electrical – Sensing Voltage with a Digital Pin

arduinofloating-pininterruptsvoltage divider

I am relatively new with Arduino and did a little bit of research prior to this post. But I am trying to sense when I have voltage coming from an AC/DC converter (12Vdc) so I can switch from my battery to my AC/DC converter. I am using an interrupt on my Arduino Mega to efficiently switch between power sources. I tried just using a voltage divider and reading 5 volts into my digital pin, but it seems that the pin is "floating" regardless of power being supplied or not, and not correctly sensing the voltage coming from my AC/DC converter. Is there a way for me to incorporate capacitors or some other circuit to correctly sense voltage with a voltage divider? Any help is appreciated.

schematic

simulate this circuit – Schematic created using CircuitLab

Best Answer

You say you are using an Arduino Mega board which implies ATMega1280 or 2560. Both of these have an Analogue Comparator module which is built in. The Arduino boards route out the pin with the alternate function of "AIN1" to what Arduino call "digital pin 5".

If you enable the internal bandgap as the reference as the source for "AIN0" (something which is configured internally), then you can basically use a potential divider on D5 which is calculated so that when the battery voltage drops below a certain threshold, the centre of the divider (connected to the pin) drops below 1.1V. When this happens, the comparator inside the ATMega will change state and this event can be configured to generate an interrupt (ISR(ANALOG_COMP_vect)).

The comparator can be configured using the following C code (compilable with avr-gcc):

//Code to enable comparator
ADCSRB &= ~(1 << ACME); //Connect AIN1/D5 to the comparator
DIDR1 |= (1 << AIN1D); //Disable digital functionality of AIN1 pin so we can use comparator
ACSR = (0 << ACD) | (1 << ACBG) | (1 << ACIS1) | (1 << ACIS0); //Enable comparator, connect bandgap, and set to rising edge sensitivity (AIN1 falls below AIN0)
//-------

//If you want to generate an interrupt, do this to enable it (make sure you have an ISR for ANALOG_COMP_vect):
ACSR |= (1 << ACIE); //Enable interrupt.
...
ISR(ANALOG_COMP_vect) {
    //Do something when voltage drops too low
}
//-------

//If you want to do it via polling mode, do this to check the value
if (ACSR & (1 << ACO)) {
    //Do something if the voltage drops too low
}
//-------