Programmably readable signature bytes in ATmegaXX8 ?

Go To Last Post
3 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So on which ATmegaxx8 variants (A, P, PA, null) is it possible for a program (on the chip itself) to read the chip's signature bytes? This is in the xxxP datasheet, where it says to use SIGRD, but never defines SIGRD and says that the appropriate bit in the usual register "must be zero." The capability is not mentioned at all in the non-P datasheet.

Of particular interest is the ATmega168x, since I don't think there ever were very many non-P ATmega328s shipped?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would suggest you try it. The SPMEN sounds like Self PrograM ENable, which is called "SELFPRGEN" in the register description. There is just one bit undocumented for SPMCSR, so the SIGRD bit must be bit 5.....

z = 0;
SPMCSR = 0x21;
retval = lpm (); 

and see if you get a result. Let us know! If you fill in the blanks in my pseudocode, I will test it for you on my atmega168. (no P).

Atmel is pretty good at acknowledging documentation errors, so if you point it out to them, they will revise the datasheet, and the next revision will include the corrections.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's the arduino sketch :-) (someone in transition; on it's way to being a complete dump of the poweron state info.) You'll need their "Flash" library for the easy printing from progmem. And cpuname.h is something I cooked up that can be omitted for now (unless there is a standard version that does the same thing?)

#include 
#include 
#include 
#include 

/*
 * SIGRD is a "must be zero" bit on most Arduino CPUs; can we read the sig or not?
 */
#define SIGRD 5

void setup()
{
  Serial.begin(9600);
}

byte fuse_bits_low;
byte fuse_bits_extended;
byte fuse_bits_high;
byte lock_bits;
byte sig1, sig2, sig3;
byte oscal;

void space() {
  Serial.print(' ');
}

void print_serno(void)
{
  int i;
  int unoSerial[6];
  int startAddr=1018;
  unsigned long serno = 0;

  for (i = 0; i < 6; i++) {
      unoSerial[i] = EEPROM.read(startAddr + i);
  }
  if (unoSerial[0] == 'U' && unoSerial[1] == 'N' && unoSerial[2] == 'O') {

      Serial << F("Your Serial Number is: UNO");

      for (i = 3; i < 6; i = i + 1) {
      serno = serno*256 + unoSerial[i];
      Serial.print(" ");
      Serial.print(unoSerial[i], HEX);
      }
      Serial << F(" (") << serno << F(")");
  } else {
      Serial << F("No Serial Number");
  }
  Serial.println();
}

void print_binary(byte b)
{
    for (byte i=0x80; i>0; i>>=1) {
    if (b&i) {
        Serial.print('1');
    } else {
        Serial.print('0');
    }
    }
}

void print_fuse_low(void)
{
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || \
    defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) ||  \
    defined(__AVR_ATmega168A__) || defined(__AVR_ATmega168PA__)
/*
 * Fuse Low is same on 48/88/168/328
 */
    Serial << F("Fuse Low = ");
    print_binary(fuse_bits_low);
    Serial << F(" (");
    Serial.print(fuse_bits_low, HEX);
    Serial << F(")\n");
    Serial << F("           ||||++++______"); 
    switch (fuse_bits_low & 0xF) {
    case 0: Serial << F("Reserved");
    break;
    case 1: Serial << F("External Clock");
    break;
    case 2: Serial << F("Calibrated 8MHz Internal Clock");
    break;
    case 3: Serial << F("Internal 128kHz Clock");
    break;
    case 4: Serial << F("LF Crystal, 1K CK Startup");
    break;
    case 5: Serial << F("LF Crystal 32K CK Startup");
    break;
    case 6: Serial << F("Full Swing Crystal ");
    break;
    case 7: Serial << F("Full Swing Crystal");
    break;
    case 8:
    case 9:
    Serial << F("Low Power Crystal 0.4 - 0.8MHz");
    break;
    case 10:
    case 11:
    Serial << F("Low Power Crystal 0.9 - 3.0MHz");
    break;
    case 12:
    case 13:
    Serial << F("Low Power Crystal 3 - 8MHz");
    break;
    case 14:
    case 15:
    Serial << F("Low Power Crystal 8 - 16MHz");    
    break;
    }

    Serial.println();
    Serial << F("           ||++__________"); 
    Serial << F("Start Up Time=");
    Serial.print((fuse_bits_low >> 4) & 3, BIN);

    Serial.println();
    Serial << F("           |+____________"); 
    Serial << F("Clock Output ");
    if (fuse_bits_low & (1<<FUSE_CKOUT))
    Serial << F("Disabled");
    else
    Serial << F("Enabled");

    Serial.println();
    Serial << F("           +_____________");
    if (fuse_bits_low & (1<<FUSE_CKDIV8)) {
    Serial << F("(no divide)");
    } else {
    Serial << F("Divide Clock by 8");
    }


#elif defined(__AVR_ATmega8__)
#endif
    Serial.println();
}

void loop()
{
  delay(2000);
  Serial << F("\nCompiled for " __CPUNAME "\n");
  print_serno();  
  cli();
  fuse_bits_low = boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS);
  fuse_bits_extended = boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS);
  fuse_bits_high = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
  lock_bits = boot_lock_fuse_bits_get(GET_LOCK_BITS);
  sig1 = boot_signature_byte_get(0);
  sig2 = boot_signature_byte_get(2);
  sig3 = boot_signature_byte_get(4);
  oscal = boot_signature_byte_get(1);
  sei();

  Serial << F("\nFuse bits (L/E/H): ");
  Serial.print(fuse_bits_low, HEX);
  space();
  Serial.print(fuse_bits_extended, HEX);
  space();
  Serial.print(fuse_bits_high, HEX);
  Serial << F("\nLock bits:         ");
  Serial.print(lock_bits, HEX);
  Serial << F("\nSignature:         ");
  Serial.print(sig1, HEX);
  space();
  Serial.print(sig2, HEX);
  space();
  Serial.print(sig3, HEX);
  Serial << F("\nOscal:             ");
  Serial.println(oscal, HEX);
  Serial.println();

  print_fuse_low();

#if defined(__AVR_ATMmega328P__) || defined(__AVR_ATmega328__)
#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || \
    defined(__AVR_ATmega168A__) || defined(__AVR_ATmega168PA__)
#elif defined(__AVR_ATmega8__)
#endif
  while (1) {
    if (Serial.read() > 0)
      break;
  }
}