Tasmota/lib/Mutichannel_Gas_Sensor/examples/UpdateFrimware/UpdateFrimware.ino

513 lines
16 KiB
Arduino
Raw Normal View History

// Atmega chip programmer
// Author: Nick Gammon
// Date: 22nd May 2012
// Version: 1.17
// Version 1.1: Reset foundSig to -1 each time around the loop.
// Version 1.2: Put hex bootloader data into separate files
// Version 1.3: Added verify, and MD5 sums
// Version 1.4: Added signatures for ATmeag8U2/16U2/32U2 (7 May 2012)
// Version 1.5: Added signature for ATmega1284P (8 May 2012)
// Version 1.6: Allow sketches to read bootloader area (lockbyte: 0x2F)
// Version 1.7: Added choice of bootloaders for the Atmega328P (8 MHz or 16 MHz)
// Version 1.8: Output an 8 MHz clock on pin 9
// Version 1.9: Added support for Atmega1284P, and fixed some bugs
// Version 1.10: Corrected flash size for Atmega1284P.
// Version 1.11: Added support for Atmega1280. Removed MD5SUM stuff to make room.
// Version 1.12: Added signatures for ATtiny2313A, ATtiny4313, ATtiny13
// Version 1.13: Added signature for Atmega8A
// Version 1.14: Added bootloader for Atmega8
// Version 1.15: Removed extraneous 0xFF from some files
// Version 1.16: Added signature for Atmega328
// Version 1.17: 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>
#include <avr/pgmspace.h>
const unsigned long BAUD_RATE = 115200;
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,
// writes are preceded by progamEnable
chipErase = 0x80,
writeLockByte = 0xE0,
writeLowFuseByte = 0xA0,
writeHighFuseByte = 0xA8,
writeExtendedFuseByte = 0xA4,
pollReady = 0xF0,
programAcknowledge = 0x53,
readSignatureByte = 0x30,
readCalibrationByte = 0x38,
readLowFuseByte = 0x50, readLowFuseByteArg2 = 0x00,
readExtendedFuseByte = 0x50, readExtendedFuseByteArg2 = 0x08,
readHighFuseByte = 0x58, readHighFuseByteArg2 = 0x08,
readLockByte = 0x58, readLockByteArg2 = 0x00,
readProgramMemory = 0x20,
writeProgramMemory = 0x4C,
loadExtendedAddressByte = 0x4D,
loadProgramMemory = 0x40,
}; // end of enum
// structure to hold signature and other relevant data about each chip
typedef struct {
byte sig [3];
char * desc;
unsigned long flashSize;
unsigned int baseBootSize;
const byte * bootloader;
unsigned long loaderStart; // bytes
unsigned int loaderLength; // bytes
unsigned long pageSize; // bytes
byte lowFuse, highFuse, extFuse, lockByte;
} signatureType;
const unsigned long kb = 1024;
// hex bootloader data
#include "bootloader_atmega168.h"
// see Atmega328 datasheet page 298
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,
atmega168_optiboot, // loader image
//0x3E00, // start address
0x0,
sizeof atmega168_optiboot,
128, // page size (for committing)
0xC6, // fuse low byte: external full-swing crystal
0xde, // fuse high byte: SPI enable, brown-out detection at 2.7V
0xf8, // fuse extended byte: boot into bootloader, 512 byte bootloader
0xcf }, // lock bits: SPM is not allowed to write to the Boot Loader section.
}; // 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
// read a byte from flash memory
byte readFlash (unsigned long addr)
{
byte high = (addr & 1) ? 0x08 : 0; // set if high byte wanted
addr >>= 1; // turn into word address
// 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
return program (readProgramMemory | high, highByte (addr), lowByte (addr));
} // end of readFlash
// write a byte to the flash memory buffer (ready for committing)
byte writeFlash (unsigned long addr, const byte data)
{
byte high = (addr & 1) ? 0x08 : 0; // set if high byte wanted
addr >>= 1; // turn into word address
program (loadProgramMemory | high, 0, lowByte (addr), data);
} // end of writeFlash
// show a byte in hex with leading zero and optional newline
void showHex (const byte b, const boolean newline = false, const boolean show0x = true)
{
if (show0x)
Serial.print (F("0x"));
// 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
// convert a boolean to Yes/No
void showYesNo (const boolean b, const boolean newline = false)
{
if (b)
Serial.print (F("Yes"));
else
Serial.print (F("No"));
if (newline)
Serial.println ();
} // end of showYesNo
// poll the target device until it is ready to be programmed
void pollUntilReady ()
{
while ((program (pollReady) & 1) == 1)
{} // wait till ready
} // end of pollUntilReady
// commit page
void commitPage (unsigned long addr)
{
//Serial.print (F("Committing page starting at 0x"));
//Serial.println (addr, HEX);
addr >>= 1; // turn into word address
// 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
program (writeProgramMemory, highByte (addr), lowByte (addr));
pollUntilReady ();
} // end of commitPage
// write specified value to specified fuse/lock byte
void writeFuse (const byte newValue, const byte instruction)
{
if (newValue == 0)
return; // ignore
program (progamEnable, instruction, 0, newValue);
pollUntilReady ();
} // end of writeFuse
void getFuseBytes ()
{
Serial.print (F("LFuse = "));
showHex (program (readLowFuseByte, readLowFuseByteArg2), true);
Serial.print (F("HFuse = "));
showHex (program (readHighFuseByte, readHighFuseByteArg2), true);
Serial.print (F("EFuse = "));
showHex (program (readExtendedFuseByte, readExtendedFuseByteArg2), true);
Serial.print (F("Lock byte = "));
showHex (program (readLockByte, readLockByteArg2), true);
Serial.print ("Clock calibration = ");
showHex (program (readCalibrationByte), true);
} // end of getFuseBytes
// burn the bootloader to the target device
void writeBootloader ()
{
if (signatures [foundSig].bootloader == 0)
{
Serial.println (F("No bootloader support for this device."));
return;
} // end if
int i;
byte lFuse = program (readLowFuseByte, readLowFuseByteArg2);
byte newlFuse = signatures [foundSig].lowFuse;
byte newhFuse = signatures [foundSig].highFuse;
byte newextFuse = signatures [foundSig].extFuse;
byte newlockByte = signatures [foundSig].lockByte;
unsigned long addr = signatures [foundSig].loaderStart;
unsigned int len = signatures [foundSig].loaderLength;
unsigned long pagesize = signatures [foundSig].pageSize;
unsigned long pagemask = ~(pagesize - 1);
const byte * bootloader = signatures [foundSig].bootloader;
Serial.print (F("Bootloader address = 0x"));
Serial.println (addr, HEX);
Serial.print (F("Bootloader length = "));
Serial.print (len);
Serial.println (F(" bytes."));
byte subcommand = 'U';
// Atmega328P or Atmega328
if (signatures [foundSig].sig [0] == 0x1E &&
signatures [foundSig].sig [1] == 0x95 &&
(signatures [foundSig].sig [2] == 0x0F || signatures [foundSig].sig [2] == 0x14)
)
{
Serial.println (F("Type 'L' to use Lilypad (8 MHz) loader, or 'U' for Uno (16 MHz) loader ..."));
do
{
subcommand = toupper (Serial.read ());
} while (subcommand != 'L' && subcommand != 'U');
if (subcommand == 'L') // use internal 8 MHz clock
{
Serial.println (F("Using Lilypad 8 MHz loader."));
bootloader = atmega168_optiboot;
newlFuse = 0xE2; // internal 8 MHz oscillator
newhFuse = 0xDA; // 2048 byte bootloader, SPI enabled
addr = 0x7800;
len = sizeof atmega168_optiboot;
} // end of using the 8 MHz clock
else
Serial.println (F("Using Uno Optiboot 16 MHz loader."));
} // end of being Atmega328P
unsigned long oldPage = addr & pagemask;
Serial.println (F("Type 'V' to verify, or 'G' to program the chip with the bootloader ..."));
char command;
do
{
command = toupper (Serial.read ());
} while (command != 'G' && command != 'V');
if (command == 'G')
{
Serial.println (F("Erasing chip ..."));
program (progamEnable, chipErase); // erase it
pollUntilReady ();
Serial.println (F("Writing bootloader ..."));
for (i = 0; i < len; i += 2)
{
unsigned long thisPage = (addr + i) & pagemask;
// page changed? commit old one
if (thisPage != oldPage)
{
commitPage (oldPage);
oldPage = thisPage;
}
unsigned char c1 = pgm_read_byte(bootloader + i);
unsigned char c2 = pgm_read_byte(bootloader + i+1);
writeFlash (addr + i, c1);
writeFlash (addr + i + 1, c2);
} // end while doing each word
Serial.println();
// commit final page
commitPage (oldPage);
Serial.println ("Written.");
} // end if programming
Serial.println (F("Verifying ..."));
// count errors
unsigned int errors = 0;
// check each byte
for (i = 0; i < signatures [foundSig].loaderLength; i++)
{
//if(i==0)Serial.print(" ");
byte found = readFlash (addr + i);
byte expected = pgm_read_byte(bootloader + i);
if (found != expected)
{
if (errors <= 100)
{
Serial.print (F("Verification error at address "));
Serial.print (addr + i, HEX);
Serial.print (F(". Got: "));
showHex (found);
Serial.print (F(" Expected: "));
showHex (expected, true);
} // end of haven't shown 100 errors yet
errors++;
} // end if error
} // end of for
Serial.println("\r\n");
if (errors == 0)
Serial.println (F("No errors found."));
else
{
Serial.print (errors, DEC);
Serial.println (F(" verification error(s)."));
if (errors > 100)
Serial.println (F("First 100 shown."));
return; // don't change fuses if errors
} // end if
if (command == 'G')
{
Serial.println (F("Writing fuses ..."));
writeFuse (newlFuse, writeLowFuseByte);
writeFuse (newhFuse, writeHighFuseByte);
writeFuse (newextFuse, writeExtendedFuseByte);
writeFuse (newlockByte, writeLockByte);
// confirm them
getFuseBytes ();
} // end if programming
Serial.println (F("Done."));
} // end of writeBootloader
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 (F("Entered programming mode OK."));
} // end of startProgramming
void getSignature ()
{
foundSig = -1;
lastAddressMSB = 0;
byte sig [3];
Serial.print (F("Signature = "));
for (byte i = 0; i < 3; i++)
{
sig [i] = program (readSignatureByte, 0, i);
showHex (sig [i]);
} // end for each signature byte
Serial.println ();
for (int j = 0; j < NUMITEMS (signatures); j++)
{
if (memcmp (sig, signatures [j].sig, sizeof sig) == 0)
{
foundSig = j;
Serial.print (F("Processor = "));
Serial.println (signatures [j].desc);
Serial.print (F("Flash memory size = "));
Serial.print (signatures [j].flashSize, DEC);
Serial.println (F(" bytes."));
return;
} // end of signature found
} // end of for each signature
Serial.println (F("Unrecogized signature."));
} // end of getSignature
void setup ()
{
Serial.begin (BAUD_RATE);
while (!Serial) ; // for Leonardo, Micro etc.
Serial.println ();
Serial.println (F("Atmega chip programmer."));
Serial.println (F("Written by Nick Gammon."));
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
} // end of setup
void loop ()
{
startProgramming ();
getSignature ();
getFuseBytes ();
// if we found a signature try to write a bootloader
if (foundSig != -1)
writeBootloader ();
// release reset
digitalWrite (RESET, HIGH);
Serial.println (F("Type 'C' when ready to continue with another chip ..."));
while (toupper (Serial.read ()) != 'C')
{}
} // end of loop