(table 13.11, I/O ports) and Ch 22. Analog comparator
I'm trying to use the analog comparator to monitor Vcc. I'm not understanding the datasheet in regards to alternate functions. My question: is it possible to use the internal bandgap reference for AIN0 and simultaneously use PD6 for digital I/O? Figure 22-1. Analog Comparator Block Diagram implies that it is possible but I can't find anything definitive in the text.
If not, I have another question. The datasheet recommends using the internal pullups for any unused I/O pins (13.2.6). If the bandgap reference is in use - meaning the external pin is unused - how is the pullup set?
You selected the datasheet of the "automotive" version. Comparing that datasheet with the normal ATmega328P, there seems to be some differences. I don't understand how that is possible, so I assume that I'm wrong ?
Can you use the normal datasheet, with a link and with page numbers and screendumps ?
The "automotive" version is from 2015. The normal datasheet is from 2020 and there is a errata document of 2021.
Some chip versions of the ATmega328P have MUX problem according to the errata:
If you want to measure the VCC using the internal voltage reference, then there is a known solution for that : https://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/
I have different code, because the next analogRead() was inaccurate, therefor I do a dummy read. This is a serious problem with the Arduino Mega 2560.
The " 40001906A.pdf" document that you linked to is for the ATmega328PB, and you say that you have the ATmega328P. Those are not the same.
Do you want to measure the VCC to check if it is going down ? But instead of polling (with the known and often used solution) you want a trigger to a interrupt ? And you also want to keep using PD6 as a digital pin.
Writing to EEPROM from an interrupt routine is possible. If you can put that trigger in code, then it is a good solution.
Analog comparator moved to ch. 23
23.1 Analog Comparator Control and Status Register
Table 14-9. Port D Pins Alternate Functions
It appears to be mostly chapter rearrangement - the figures and tables retain their ch.n identifiers.
Yes, check if Vcc is dropping but not measure, in the usual sense like with an ADC. The idea is to offload this monitoring to hardware and just sense yes/no with the comparator.
Yes, the ISR (ANALOG_COMP_vect).
Yes. I have verified @Jiggy-Ninja's solution works. Bandgap selected, potentiometer on pin 7 (AIN1), and pin 6 driving an LED to signal the analog comparator output (ACO).
The datasheet(s) indicate that the brown-out detector could be used but that gets into arcana like fuse setting and ICSP programming. I don't want to go there right now.
No. I want to offload the monitoring to hardware and just have it give me a go/no go signal (interrupt). The idea is to NOT have the code constantly checking Vcc. My thought is that the hardware providing an interrupt would allow more time for saving whatever variables have been deemed necessary to restore on startup. An additional benefit might be to save the user from saving variables to EEPROM every time a setting changes. And, of course, just to see if it can be done!
This does work. I have come up with an example which saves and restores one variable.
/*
Analog comparator demo. Bandgap reference and AIN1 on D7
Board = UNO
Comparator settings based on Nick Gammon's page:
http://www.gammon.com.au/forum/?id=11916
The first time the program is run stateCount will be 255 (out of range)
(assuming no prior use of the EEPROM). When the button is pressed
stateCount will cycle through three values, 0-2. Zero = OFF, one = slow
flash, two = fast flash. When power is lost stateCount is saved to
EEPROM by the ANALOG_COMP_vect interrupt service routine. When
power returns stateCount will be restored from EEPROM and the code
will resume the previously set flash mode.
Code in setup() initializes the analog comparator to generate an
interrupt when the voltage on AIN1 - connected to a voltage divider -
drops below the internal 1.1v bandgap reference (connected to the
analog comparator positive input). The voltage divider is connected
between Vcc and GND and the wiper to AIN1 (pin 7).
This version saves only one 8-bit variable.
*/
/*
Current voltage divider values on pot - ~6800 and ~3300 ohms. With
Vcc at ~5V this provides 1.634v at divider tap.
*/
#include <EEPROM.h>
const byte switchPin = 11;
const byte LEDpin = 6; // This pin is used intentionally to demonstrate that
// pin 6 can be used for normal digital I/O when the
// analog comparator input AIN0 is connected to the
// bandgpap reference.
const uint16_t EEPROM_BASE = 10; // non-volatile address to store/retrieve data
uint8_t stateCount = 0; // flash or don't flash LED
ISR (ANALOG_COMP_vect)
{
/*
When board power is lost the analog comparator triggers
an interrupt which saves the current stateCount.
This state will be restored when power is reapplied.
*/
EEPROM.put(EEPROM_BASE, stateCount);
while (1); // stay here until power down
}
void setup ()
{
DIDR1 = AIN1D | AIN0D; // datasheet 27.3.2 Digital Input Disable Register 1
ACSR = bit (ACI) ; // (Clear) Analog Comparator Interrupt Flag
ACSR |= bit (ACIE); // Analog Comparator Interrupt Enable ON
ACSR |= bit (ACBG); // Internal bandgap reference ON
ADCSRB = 0; // (Disable) ACME: Analog Comparator Multiplexer Enable
// Prepare the analog comparator for the current setup:
// Voltage divider on pin 7, LED flasher on pin 6.
// ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger on rising edge)
ACSR |= bit (ACIS0);
ACSR |= bit (ACIS1);
pinMode(LEDpin, OUTPUT); // Use the now disconnected PD6 as an output
pinMode(switchPin, INPUT_PULLUP); // parallel .1uf capacitor debouncing
EEPROM.get(EEPROM_BASE, stateCount); // Restore stateCount from EEPROM
// Serial.println("end of setup");
} // end of setup
void loop ()
{
static byte lastSW;
byte switchState = digitalRead(switchPin);
if (switchState == LOW && lastSW == HIGH) {
stateCount++;
if (stateCount > 2) stateCount = 0;
// Serial.println("switch pressed");
}
lastSW = switchState;
if (stateCount == 1) { // LED flashes slow
delay(1000);
digitalWrite(LEDpin, HIGH);
delay(1000);
digitalWrite(LEDpin, LOW);
}
else if (stateCount == 2) { // LED flashes fast
delay(500);
digitalWrite(LEDpin, HIGH);
delay(500);
digitalWrite(LEDpin, LOW);
}
else { // LED is OFF
digitalWrite(LEDpin, LOW);
}
}
I added a 1000uF capacitor across the Arduino 5V output line and can save at least seven values now. I also have an I2C 16x2 display attached. Not pursued further.