Taillieu.Info

More Than a Hobby..

Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /customers/3/2/5/taillieu.info/httpd.www/templates/taillieuinfo_joomla3_rev1/functions.php on line 194

Atmega chip fuse detector / program memory dump

// Atmega chip fuse detector

// Author: Nick Gammon

// Date: 22nd May 2012

// Version: 1.8

 

// Version 1.1 added signatures for Attiny24/44/84 (5 May 2012)

// Version 1.2 added signatures for ATmeag8U2/16U2/32U2 (7 May 2012)

// Version 1.3: Added signature for ATmega1284P (8 May 2012)

// Version 1.4: Output an 8 MHz clock on pin 9

// Version 1.5: Corrected flash size for Atmega1284P.

// Version 1.6: Added signatures for ATtiny2313A, ATtiny4313, ATtiny13

// Version 1.7: Added signature for Atmega8A

// Version 1.8: Allowed for running on the Leonardo, Micro, etc.

 

/*

 

 Copyright 2012 Nick Gammon.

 

 

 PERMISSION TO DISTRIBUTE

 

 Permission is hereby granted, free of charge, to any person obtaining a copy of this software 

 and associated documentation files (the "Software"), to deal in the Software without restriction, 

 including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 

 and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 

 subject to the following conditions:

 

 The above copyright notice and this permission notice shall be included in 

 all copies or substantial portions of the Software.

 

 

 LIMITATION OF LIABILITY

 

 The software is provided "as is", without warranty of any kind, express or implied, 

 including but not limited to the warranties of merchantability, fitness for a particular 

 purpose and noninfringement. In no event shall the authors or copyright holders be liable 

 for any claim, damages or other liability, whether in an action of contract, 

 tort or otherwise, arising from, out of or in connection with the software 

 or the use or other dealings in the software. 

 

*/

 

#include <SPI.h>

extern "C" 

  {

  #include "md5.h"

  }

 

const byte CLOCKOUT = 9;

const byte RESET = 10;  // --> goes to reset on the target board

 

#if ARDUINO < 100

  const byte SCK = 13;    // SPI clock

#endif

 

// number of items in an array

#define NUMITEMS(arg) ((unsigned int) (sizeof (arg) / sizeof (arg [0])))

 

// programming commands to send via SPI to the chip

enum {

    progamEnable = 0xAC,

    programAcknowledge = 0x53,

    

    readSignatureByte = 0x30,

    readCalibrationByte = 0x38,

    

    readLowFuseByte = 0x50,       readLowFuseByteArg2 = 0x00,

    readExtendedFuseByte = 0x50,  readExtendedFuseByteArg2 = 0x08,

    readHighFuseByte = 0x58,      readHighFuseByteArg2 = 0x08,  

    readLockByte = 0x58,          readLockByteArg2 = 0x00,  

    

    readProgramMemory = 0x20,  

    loadExtendedAddressByte = 0x4D,

    

};  // end of enum

 

typedef struct {

   byte sig [3];

   char * desc;

   unsigned long flashSize;

   unsigned int baseBootSize;

} signatureType;

 

const unsigned long kb = 1024;

 

// see Atmega328 datasheet page 298

const signatureType signatures [] = 

  {

//     signature          description   flash size  bootloader size

 

  // Attiny84 family

  { { 0x1E, 0x91, 0x0B }, "ATtiny24",   2 * kb,         0 },

  { { 0x1E, 0x92, 0x07 }, "ATtiny44",   4 * kb,         0 },

  { { 0x1E, 0x93, 0x0C }, "ATtiny84",   8 * kb,         0 },

 

  // Attiny85 family

  { { 0x1E, 0x91, 0x08 }, "ATtiny25",   2 * kb,         0 },

  { { 0x1E, 0x92, 0x06 }, "ATtiny45",   4 * kb,         0 },

  { { 0x1E, 0x93, 0x0B }, "ATtiny85",   8 * kb,         0 },

 

  // Atmega328 family

  { { 0x1E, 0x92, 0x0A }, "ATmega48PA",   4 * kb,         0 },

  { { 0x1E, 0x93, 0x0F }, "ATmega88PA",   8 * kb,       256 },

  { { 0x1E, 0x94, 0x0B }, "ATmega168PA", 16 * kb,       256 },

  { { 0x1E, 0x95, 0x0F }, "ATmega328P",  32 * kb,       512 },

 

  // Atmega644 family

  { { 0x1E, 0x94, 0x0A }, "ATmega164P",   16 * kb,      256 },

  { { 0x1E, 0x95, 0x08 }, "ATmega324P",   32 * kb,      512 },

  { { 0x1E, 0x96, 0x0A }, "ATmega644P",   64 * kb,   1 * kb },

 

  // Atmega2560 family

  { { 0x1E, 0x96, 0x08 }, "ATmega640",    64 * kb,   1 * kb },

  { { 0x1E, 0x97, 0x03 }, "ATmega1280",  128 * kb,   1 * kb },

  { { 0x1E, 0x97, 0x04 }, "ATmega1281",  128 * kb,   1 * kb },

  { { 0x1E, 0x98, 0x01 }, "ATmega2560",  256 * kb,   1 * kb },

  { { 0x1E, 0x98, 0x02 }, "ATmega2561",  256 * kb,   1 * kb },

  

  // Atmega32U2 family

  { { 0x1E, 0x93, 0x89 }, "ATmega8U2",    8 * kb,   512 },

  { { 0x1E, 0x94, 0x89 }, "ATmega16U2",  16 * kb,   512 },

  { { 0x1E, 0x95, 0x8A }, "ATmega32U2",  32 * kb,   512 },

 

  // Atmega32U4 family

  { { 0x1E, 0x94, 0x88 }, "ATmega16U4",  16 * kb,   512 },

  { { 0x1E, 0x95, 0x87 }, "ATmega32U4",  32 * kb,   512 },

  

  // ATmega1284P family

  { { 0x1E, 0x97, 0x05 }, "ATmega1284P", 128 * kb,   1 * kb },

  

  // ATtiny4313 family

  { { 0x1E, 0x91, 0x0A }, "ATtiny2313A", 2 * kb,   0 },

  { { 0x1E, 0x92, 0x0D }, "ATtiny4313",  4 * kb,   0 },

  

  // ATtiny13 family

  { { 0x1E, 0x90, 0x07 }, "ATtiny13A",   1 * kb,   0 },

  

  // Atmega8A family

  { { 0x1E, 0x93, 0x07 }, "ATmega8A",    8 * kb, 256 },

  

  };  // end of signatures

 

// if signature found in above table, this is its index

int foundSig = -1;

byte lastAddressMSB = 0;

 

// execute one programming instruction ... b1 is command, b2, b3, b4 are arguments

//  processor may return a result on the 4th transfer, this is returned.

byte program (const byte b1, const byte b2 = 0, const byte b3 = 0, const byte b4 = 0)

  {

  SPI.transfer (b1);  

  SPI.transfer (b2);  

  SPI.transfer (b3);  

  return SPI.transfer (b4);  

  } // end of program

  

byte readFlash (unsigned long addr)

  {

  

  // set the extended (most significant) address byte if necessary

  byte MSB = (addr >> 16) & 0xFF;

  if (MSB != lastAddressMSB)

    {

    program (loadExtendedAddressByte, 0, MSB); 

    lastAddressMSB = MSB;

    }  // end if different MSB

     

  byte high = (addr & 1) ? 0x08 : 0;  // set if high byte wanted

  addr >>= 1;  // turn into word address

  return program (readProgramMemory | high, highByte (addr), lowByte (addr));

  } // end of readFlash

  

void showHex (const byte b, const boolean newline = false)

  {

  // try to avoid using sprintf

  char buf [4] = { ((b >> 4) & 0x0F) | '0', (b & 0x0F) | '0', ' ' , 0 };

  if (buf [0] > '9')

    buf [0] += 7;

  if (buf [1] > '9')

    buf [1] += 7;

  Serial.print (buf);

  if (newline)

    Serial.println ();

  }  // end of showHex 

  

void showYesNo (const boolean b, const boolean newline = false)

  {

  if (b)

    Serial.print ("Yes");

  else

    Serial.print ("No");

  if (newline)

    Serial.println ();

  }  // end of showYesNo 

  

void readBootloader ()

  {

  unsigned long addr;

  unsigned int  len;

  byte hFuse = program (readHighFuseByte, readHighFuseByteArg2);

  

  if (signatures [foundSig].baseBootSize == 0)

    {

    Serial.println ("No bootloader support.");

    return;  

    }

    

  addr = signatures [foundSig].flashSize;

  len = signatures [foundSig].baseBootSize;

  

  Serial.print ("Bootloader in use: ");

  showYesNo ((hFuse & _BV (0)) == 0, true);

  Serial.print ("EEPROM preserved through erase: ");

  showYesNo ((hFuse & _BV (3)) == 0, true);

  Serial.print ("Watchdog timer always on: ");

  showYesNo ((hFuse & _BV (4)) == 0, true);

  

  // work out bootloader size

  // these 2 bits basically give a base bootloader size multiplier

  switch ((hFuse >> 1) & 3)

    {

    case 0: len *= 8; break;  

    case 1: len *= 4; break;  

    case 2: len *= 2; break;  

    case 3: len *= 1; break;  

    }  // end of switch

 

  // where bootloader starts

  addr -= len;

  

  Serial.print ("Bootloader is ");

  Serial.print (len);

  Serial.print (" bytes starting at ");

  Serial.println (addr, HEX);

  Serial.println ();

  Serial.println ("Bootloader:");

  Serial.println ();

 

  for (int i = 0; i < len; i++)

    {

    // show address

    if (i % 16 == 0)

      {

      Serial.print (addr + i, HEX);

      Serial.print (": ");

      }

    showHex (readFlash (addr + i));

    // new line every 16 bytes

    if (i % 16 == 15)

      Serial.println ();

    }  // end of for

    

  Serial.println ();

  Serial.print ("MD5 sum of bootloader = ");

  

  md5_context ctx;

  byte md5sum [16];

  byte mem;

 

  md5_starts( &ctx );

  

  while (len--)

    {

    mem = readFlash (addr++);

    md5_update( &ctx, &mem, 1);

    }  // end of doing MD5 sum on each byte

    

  md5_finish( &ctx, md5sum );

 

  for (int i = 0; i < sizeof md5sum; i++)

    showHex (md5sum [i]);

  Serial.println ();  

 

  } // end of readBootloader

  

void readProgram ()

  {

  unsigned long addr = 0;

  unsigned int  len = 256;

  Serial.println ();

  Serial.println ("First 256 bytes of program memory:");

  Serial.println ();

 

  for (int i = 0; i < len; i++)

    {

    // show address

    if (i % 16 == 0)

      {

      Serial.print (addr + i, HEX);

      Serial.print (": ");

      }

    showHex (readFlash (addr + i));

    // new line every 16 bytes

    if (i % 16 == 15)

      Serial.println ();

    }  // end of for

    

  Serial.println ();

 

  } // end of readProgram

  

void startProgramming ()

  {

  byte confirm;

  pinMode (RESET, OUTPUT);

  pinMode (SCK, OUTPUT);

  

  // we are in sync if we get back programAcknowledge on the third byte

  do 

    {

    delay (100);

    // ensure SCK low

    digitalWrite (SCK, LOW);

    // then pulse reset, see page 309 of datasheet

    digitalWrite (RESET, HIGH);

    delay (1);  // pulse for at least 2 clock cycles

    digitalWrite (RESET, LOW);

    delay (25);  // wait at least 20 mS

    SPI.transfer (progamEnable);  

    SPI.transfer (programAcknowledge);  

    confirm = SPI.transfer (0);  

    SPI.transfer (0);  

    } while (confirm != programAcknowledge);

    

  Serial.println ("Entered programming mode OK.");

  }  // end of startProgramming

 

void getSignature ()

  {

  byte sig [3];

  Serial.print ("Signature = ");

  for (byte i = 0; i < 3; i++)

    {

    sig [i] = program (readSignatureByte, 0, i); 

    showHex (sig [i]);

    }

  Serial.println ();

  

  for (int j = 0; j < NUMITEMS (signatures); j++)

    {

    if (memcmp (sig, signatures [j].sig, sizeof sig) == 0)

      {

      foundSig = j;

      Serial.print ("Processor = ");

      Serial.println (signatures [j].desc);

      Serial.print ("Flash memory size = ");

      Serial.println (signatures [j].flashSize, DEC);

      return;

      }

    }

 

  Serial.println ("Unrecogized signature.");  

  }  // end of getSignature

 

void getFuseBytes ()

  {

  Serial.print ("LFuse = ");

  showHex (program (readLowFuseByte, readLowFuseByteArg2), true);

  Serial.print ("HFuse = ");

  showHex (program (readHighFuseByte, readHighFuseByteArg2), true);

  Serial.print ("EFuse = ");

  showHex (program (readExtendedFuseByte, readExtendedFuseByteArg2), true);

  Serial.print ("Lock byte = ");

  showHex (program (readLockByte, readLockByteArg2), true);

  Serial.print ("Clock calibration = ");

  showHex (program (readCalibrationByte), true);

  }  // end of getFuseBytes

 

void setup ()

  {

  Serial.begin (115200);

  while (!Serial) ;  // for Leonardo, Micro etc.

  Serial.println ();

  Serial.println ("Atmega chip detector.");

 

  digitalWrite(RESET, HIGH);  // ensure SS stays high for now

  SPI.begin ();

  

  // slow down SPI for benefit of slower processors like the Attiny

  SPI.setClockDivider(SPI_CLOCK_DIV64);

 

  pinMode (CLOCKOUT, OUTPUT);

  

  // set up Timer 1

  TCCR1A = _BV (COM1A0);  // toggle OC1A on Compare Match

  TCCR1B = _BV(WGM12) | _BV(CS10);   // CTC, no prescaling

  OCR1A =  0;       // output every cycle

  

  startProgramming ();

  getSignature ();

  getFuseBytes ();

  

  if (foundSig != -1)

    {

    readBootloader ();

    }

    

  readProgram ();

 }  // end of setup

 

void loop () {}