ATMEGA328P analog input comparator - using

(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?

Apologies if this sounds confused.

Yes. If you're using an internal signal, the external pin is free for other use.

Apologies if I make it even more confusing :grimacing:

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:

:point_right: 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.

Pah!

So everybody's on the same page. :drooling_face:

Analog comparator ch. 27.1, p.284

27.3.1 Analog Comparator Control and Status Register, p. 285

Table 17-9. Port D Pins Alternate Functions, p. 98

Not so much measure as trigger an interrupt to save values to EEPROM on power down.

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.

:question: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.

I'm sorry that I forgot how to use the comparator :frowning_face:

Finally, the correct datasheet (Ihope).

https://www.mouser.com/datasheet/2/268/ATmega48A_PA_88A_PA_168A_PA_328_P_DS_DS40002061B-1900559.pdf

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.

Thanks!  I suspected this was the case but I didn't want to chance letting even a small bit of smoke out.

Where do you see that? I only see the BOD triggering a reset, not being able to trigger an interrupt.

Google secret voltmeter , is that what you want ?

You're right, after looking at it more intently I don't see any interrupt mentioned for the BOD. I saw 'fuse bits' and ran away!

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);
  }
}
2 Likes

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.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.