You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

897 lines
24 KiB

/**************************************************
* TPI programmer for ATtiny4/5/9/10/20/40
*
* Make the connections as shown below.
*
* To use:
***** Buad rate must be set to 9600 ****
*
* - Upload to arduino and power off
* - Connect ATtiny10 as shown
* - Power on and open the serial monitor
* - If things are working so far you should
* see "NVM enabled" and "ATtiny10/20/40 connected".
* - Input one-letter commands via serial monitor:
*
* D = dump memory. Displays all current memory
* on the chip
*
* E = erase chip. Erases all program memory
* automatically done at time of programming
*
* P = write program. After sending this, paste
* the program from the hex file into the
* serial monitor.
*
* S = set fuse. follow the instructions to set
* one of the three fuses.
*
* C = clear fuse. follow the instructions to clear
* one of the three fuses.
*
* L = Set Lock Bits No further programming & verification
* possible
*
* H = Toggle High Voltage Programming
*
* T = Toggle +12v enabled by High, or Low
*
* R/r = Quick reset
*
* - Finally, power off the arduino and remove the
* Attiny10/20/40
*
*
* Arduino ATtiny10 *
* ----------+ +---------------- *
* (SS#) 10 |--[R]-----| 6 (RESET#/PB3) *
* | | *
* (MOSI) 11 |--[R]--+--| 1 (TPIDATA/PB0) *
* | | | *
* (MISO) 12 |--[R]--+ | *
* | | *
* (SCK) 13 |--[R]-----| 3 (TPICLK/PB1) *
* ----------+ +---------------- *
* *
* ----------+ +---------------- *
* (HVP) 9 |--- | 6 (RESET#/PB3) *
* | | *
* *
* -[R]- = a 220 - 1K Ohm resistor *
* *
* this picture : 2011/12/08 by pcm1723 *
* modified :2015/02/27 by KD *
* *
* thanks to pcm1723 for tpitest.pde upon which *
* this is based *
**************************************************
Updates:
Apr 02, 2018: Ksdsksd@gmail.com
* Added Lock bit setting to main menu
Jan 23, 2017: Ksdsksd@gmail.com
* Thanks to InoueTaichi Fixed incorrect #define Tiny40
Mar 05, 2015: Ksdsksd@gamil.com
* Added notifications to setting and clearing the system flags.
Feb 23, 2015: Ksdsksd@gamil.com
* Changed the programmer Diagram, This is the config I use, and get a sucessful programming of a tiny10 at 9600 baud.
Mar 22, 2014: Ksdsksd@gmail.com
* Added the quick reset to high before resetting the device.
* Added code to stop the SPI and float the pins for testing the device while connected.
Mar 20, 2014: Ksdsksd@gmail.com
* Added a quick reset by sending 'r' or 'R' via the serial monitor.
* Added a High voltage programming option from pin 9, toggled by 'H'
* Added a High/low option for providing 12v to the reset pin, toggled by 'T'
Mar 17, 2014: Ksdsksd@gmail.com
* Had some trouble with the nibbles being swapped when programming on the 10 & 20,
added b1,b2 to hold the serial data before calling byteval()
* Added Nat Blundell's patch to the code
Apr 10, 2013: Ksdsksd@gmail.com
* Applied Fix for setting and clearing flags
Feb 7, 2013: Ksdsksd@gmail.com
* Fixed programming timer, had intitial start at zero instead of current time.
Dec 11, 2012: Ksdsksd@gmail.com
* Added detect and programming for 4/5/9
Dec 5-6, 2012: Ksdsksd@gmail.com
* Incorperated read, and verify into program. Now have no program size limitation by using 328p.
* Changed the outHex routines consolidated them into 1, number to be printed, and number of nibbles
* Added a type check to distinguish between Tiny10/20/40
* Added an auto word size check to ensure that there is the proper amount of words written for a 10/20/40
* Removed Read program, Verify, and Finish from options
* Changed baud rate to 19200 for delay from data written to the chip, to prevent serial buffer overrun.
Oct 5, 2012: Ksdsksd@gmail.com
*** Noticed that when programming, the verification fails
at times by last 1-2 bytes programmed, and the Tiny would act erratic.
Quick fix was adding 3 NOP's to the end the Tiny's code, and ignoring the errors, the Tiny then performed as expected.
Oct 4, 2012: Ksdsksd@gmail.com
* Moved all Serial printed strings to program space
* Added code to detect Tiny20
*/
#include <SPI.h>
#include "pins_arduino.h"
// define the instruction set bytes
#define SLD 0x20
#define SLDp 0x24
#define SST 0x60
#define SSTp 0x64
#define SSTPRH 0x69
#define SSTPRL 0x68
// see functions below ////////////////////////////////
// SIN 0b0aa1aaaa replace a with 6 address bits
// SOUT 0b1aa1aaaa replace a with 6 address bits
// SLDCS 0b1000aaaa replace a with address bits
// SSTCS 0b1100aaaa replace a with address bits
///////////////////////////////////////////////////////
#define SKEY 0xE0
#define NVM_PROGRAM_ENABLE 0x1289AB45CDD888FFULL // the ULL means unsigned long long
#define NVMCMD 0x33
#define NVMCSR 0x32
#define NVM_NOP 0x00
#define NVM_CHIP_ERASE 0x10
#define NVM_SECTION_ERASE 0x14
#define NVM_WORD_WRITE 0x1D
#define HVReset 9
#define Tiny4_5 10
#define Tiny9 1
#define Tiny10 1
#define Tiny20 2
#define Tiny40 4
#define TimeOut 1
#define HexError 2
#define TooLarge 3
// represents the current pointer register value
unsigned short adrs = 0x0000;
// used for storing a program file
uint8_t data[16]; //program data
unsigned int progSize = 0; //program size in bytes
// used for various purposes
long startTime;
int timeout;
uint8_t b, b1, b2, b3;
boolean idChecked;
boolean correct;
char type; // type of chip connected 1 = Tiny10, 2 = Tiny20
char HVP = 0;
char HVON = 0;
void setup(){
// set up serial
Serial.begin(9600); // you cant increase this, it'll overrun the buffer
Serial.println(F("setupend")); //anything?
// set up SPI
/* SPI.begin();
SPI.setBitOrder(LSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV32);
*/ start_tpi();
pinMode(HVReset, OUTPUT);
// initialize memory pointer register
setPointer(0x0000);
timeout = 20000;
idChecked = false;
} // end setup()
void hvserial()
{
if(HVP)
Serial.println(F("***High Voltage Programming Enabled***"));
else
Serial.println(F("High Voltage Programming Disabled"));
// M
Serial.print(HVON?F("HIGH"):F("LOW"));
Serial.print(F(" supplies 12v"));
}
void hvReset(char highLow)
{
if(HVP)
{
if(HVON) //if high enables 12v
highLow = !highLow; // invert the typical reset
digitalWrite(HVReset, highLow);
}
else
digitalWrite(SS, highLow);
}
void quickReset()
{
digitalWrite(SS,HIGH);
delay(1);
digitalWrite(SS,LOW);
delay(10);
digitalWrite(SS,HIGH);
}
void start_tpi() {
SPI.begin();
SPI.setBitOrder(LSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV32);
// enter TPI programming mode
hvReset(LOW);
// digitalWrite(SS, LOW); // assert RESET on tiny
delay(1); // t_RST min = 400 ns @ Vcc = 5 V
SPI.transfer(0xff); // activate TPI by emitting
SPI.transfer(0xff); // 16 or more pulses on TPICLK
SPI.transfer(0xff); // while holding TPIDATA to "1"
writeCSS(0x02, 0x04); // TPIPCR, guard time = 8bits (default=128)
send_skey(NVM_PROGRAM_ENABLE); // enable NVM interface
// wait for NVM to be enabled
while((readCSS(0x00) & 0x02) < 1){
// wait
}
Serial.println(F("NVM enabled"));
}
void setLockBits(){
Serial.print(F("Locking... Are you sure? Y/N"));
while(Serial.available() < 1);
char yn = Serial.read();
if(yn == 'n' || yn == 'N')
return;
setPointer(0x3F00);
writeIO(NVMCMD, NVM_WORD_WRITE);
tpi_send_byte(SSTp);
tpi_send_byte(0);
tpi_send_byte(SSTp);
tpi_send_byte(0xFF);
while((readIO(NVMCSR) & (1<<7)) != 0x00);
Serial.print(F("Locked..."));
}
void loop(){
Serial.print(F("Start"));
if(!idChecked){
// start_tpi();
checkID();
idChecked = true;
finish();
}
// when ready, send ready signal '.' and wait
Serial.print(F("\n>"));
while(Serial.available() < 1){
// wait
}
start_tpi();
// the first byte is a command
//** 'P' = program the ATtiny using the read program
//** 'D' = dump memory to serial monitor
//** 'E' = erase chip. erases current program memory.(done automatically by 'P')
//** 'S' = set fuse
//** 'C' = clear fuse
//** 'L' = Set Lock Bits
char comnd = Sread();
switch( comnd ){
case 'r':
case'R':
quickReset();
break;
case 'D':
dumpMemory();
break;
case 'H':
HVP = !HVP;
hvserial();
break;
case 'T':
HVON = !HVON;
hvserial();
break;
case 'P':
if(!writeProgram()){
startTime = millis();
while(millis()-startTime < 8000)
Serial.read();// if exited due to error, disregard all other serial data
}
break;
case 'E':
eraseChip();
break;
case 'S':
setConfig(true);
break;
case 'C':
setConfig(false);
break;
case 'L':
setLockBits();
break;
default:
Serial.println(F("Received unknown command"));
}
finish();
}
void ERROR_pgmSize(void)
{
Serial.println(F("program size is 0??"));
}
void ERROR_data(char i)
{
Serial.println(F("couldn't receive data:"));
switch(i){
case TimeOut:
Serial.println(F("timed out"));
break;
case HexError:
Serial.println(F("hex file format error"));
break;
case TooLarge:
Serial.println(F("program is too large"));
break;
default:
break;
}
}
// print the register, SRAM, config and signature memory
void dumpMemory(){
unsigned int len;
uint8_t i;
// initialize memory pointer register
setPointer(0x0000);
Serial.println(F("Current memory state:"));
if(type != Tiny4_5)
len = 0x400 * type; //the memory length for a 10/20/40 is 1024/2048/4096
else
len = 0x200; //tiny 4/5 has 512 bytes
len += 0x4000;
while(adrs < len){
// read the byte at the current pointer address
// and increment address
tpi_send_byte(SLDp);
b = tpi_receive_byte(); // get data byte
// read all the memory, but only print
// the register, SRAM, config and signature memory
if ((0x0000 <= adrs && adrs <= 0x005F) // register/SRAM
|(0x3F00 <= adrs && adrs <= 0x3F01) // NVM lock bits
|(0x3F40 <= adrs && adrs <= 0x3F41) // config
|(0x3F80 <= adrs && adrs <= 0x3F81) // calibration
|(0x3FC0 <= adrs && adrs <= 0x3FC3) // ID
|(0x4000 <= adrs && adrs <= len-1) ) { // program
// print +number along the top
if ((0x00 == adrs)
|(0x3f00 == adrs) // NVM lock bits
|(0x3F40 == adrs) // config
|(0x3F80 == adrs) // calibration
|(0x3FC0 == adrs) // ID
|(0x4000 == adrs) ) {
Serial.println();
if(adrs == 0x0000){ Serial.print(F("registers, SRAM")); }
if(adrs == 0x3F00){ Serial.print(F("NVM lock")); }
if(adrs == 0x3F40){ Serial.print(F("configuration")); }
if(adrs == 0x3F80){ Serial.print(F("calibration")); }
if(adrs == 0x3FC0){ Serial.print(F("device ID")); }
if(adrs == 0x4000){ Serial.print(F("program")); }
Serial.println();
for (i = 0; i < 5; i++)
Serial.print(F(" "));
for (i = 0; i < 16; i++) {
Serial.print(F(" +"));
Serial.print(i, HEX);
}
}
// print number on the left
if (0 == (0x000f & adrs)) {
Serial.println();
outHex(adrs, 4);
Serial.print(F(": ")); // delimiter
}
outHex(b, 2);
Serial.print(F(" "));
}
adrs++; // increment memory address
if(adrs == 0x0060){
// skip reserved memory
setPointer(0x3F00);
}
}
Serial.println(F(" "));
} // end dumpMemory()
// receive and translate the contents of a hex file, Program and verify on the fly
boolean writeProgram(){
char datlength[] = "00";
char addr[] = "0000";
char something[] = "00";
char chksm[] = "00";
unsigned int currentByte = 0;
progSize = 0;
uint8_t linelength = 0;
boolean fileEnd = false;
unsigned short tadrs;
tadrs = adrs = 0x4000;
correct = true;
unsigned long pgmStartTime = millis();
eraseChip(); // erase chip
char words = (type!=Tiny4_5?type:1);
char b1, b2;
// read in the data and
while(!fileEnd){
startTime = millis();
while(Serial.available() < 1){
if(millis()-startTime > timeout){
ERROR_data(TimeOut);
return false;
}
if(pgmStartTime == 0)
pgmStartTime = millis();
}
if(Sread() != ':'){ // maybe it was a newline??
if(Sread() != ':'){
ERROR_data(HexError);
return false;
}
}
// read data length
datlength[0] = Sread();
datlength[1] = Sread();
linelength = byteval(datlength[0], datlength[1]);
// read address. if "0000" currentByte = 0
addr[0] = Sread();
addr[1] = Sread();
addr[2] = Sread();
addr[3] = Sread();
if(linelength != 0x00 && addr[0]=='0' && addr[1]=='0' && addr[2]=='0' && addr[3]=='0')
currentByte = 0;
// read type thingy. "01" means end of file
something[0] = Sread();
something[1] = Sread();
if(something[1] == '1'){
fileEnd = true;
}
if(something[1] == '2'){
for (int i = 0; i<=linelength; i++){
Sread();
Sread();
}
}
else{
// read in the data
for(int k=0; k<linelength; k++){
while(Serial.available() < 1){
if(millis()-startTime > timeout){
ERROR_data(TimeOut);
return false;
}
}
b1=Sread();
b2=Sread();
data[currentByte] = byteval(b1, b2);
currentByte++;
progSize++;
if(progSize > (type!=Tiny4_5?type*1024:512)){
ERROR_data(TooLarge);
return 0;
}
if(fileEnd) //has the end of the file been reached?
while(currentByte < 2 * words){// append zeros to align the word count to program
data[currentByte] = 0;
currentByte++;
}
if( currentByte == 2 * words ){// is the word/Dword/Qword here?
currentByte = 0; // yes, reset counter
setPointer(tadrs); // point to the address to program
writeIO(NVMCMD, NVM_WORD_WRITE);
for(int i = 0; i<2 * words; i+=2){// loop for each word size depending on micro
// now write a word to program memory
tpi_send_byte(SSTp);
tpi_send_byte(data[i]); // LSB first
tpi_send_byte(SSTp);
tpi_send_byte(data[i+1]); // then MSB
SPI.transfer(0xff); //send idle between words
SPI.transfer(0xff); //send idle between words
}
while((readIO(NVMCSR) & (1<<7)) != 0x00){} // wait for write to finish
writeIO(NVMCMD, NVM_NOP);
SPI.transfer(0xff);
SPI.transfer(0xff);
//verify written words
setPointer(tadrs);
for (int c = 0; c < 2 * words; c++){
tpi_send_byte(SLDp);
b = tpi_receive_byte(); // get data byte
if(b != data[c]){
correct = false;
Serial.println(F("program error:"));
Serial.print(F("byte "));
outHex(adrs, 4);
Serial.print(F(" expected "));
outHex(data[c],2);
Serial.print(F(" read "));
outHex(b,2);
Serial.println();
if(!correct)
return false;
}
}
tadrs += 2 * words;
}
}
// read in the checksum.
startTime = millis();
while(Serial.available() == 0){
if(millis()-startTime > timeout){
ERROR_data(TimeOut);
return false;
}
}
chksm[0] = Sread();
chksm[1] = Sread();
}
}
// the program was successfully written
Serial.print(F("Successfully wrote program: "));
Serial.print(progSize, DEC);
Serial.print(F(" of "));
if(type != Tiny4_5)
Serial.print(1024 * type, DEC);
else
Serial.print(512, DEC);
Serial.print(F(" bytes\n in "));
Serial.print((millis()-pgmStartTime)/1000.0,DEC);
Serial.print(F(" Seconds"));
// digitalWrite(SS, HIGH); // release RESET
return true;
}
void eraseChip(){
// initialize memory pointer register
setPointer(0x4001); // need the +1 for chip erase
// erase the chip
writeIO(NVMCMD, NVM_CHIP_ERASE);
tpi_send_byte(SSTp);
tpi_send_byte(0xAA);
tpi_send_byte(SSTp);
tpi_send_byte(0xAA);
tpi_send_byte(SSTp);
tpi_send_byte(0xAA);
tpi_send_byte(SSTp);
tpi_send_byte(0xAA);
while((readIO(NVMCSR) & (1<<7)) != 0x00){
// wait for erasing to finish
}
Serial.println(F("chip erased"));
}
void setConfig(boolean val){
// get current config byte
setPointer(0x3F40);
tpi_send_byte(SLD);
b = tpi_receive_byte();
Serial.println(F("input one of these letters"));
Serial.println(F("c = system clock output"));
Serial.println(F("w = watchdog timer on"));
Serial.println(F("r = disable reset"));
Serial.println(F("x = cancel. don't change anything"));
while(Serial.available() < 1){
// wait
}
char comnd = Serial.read();
setPointer(0x3F40);
writeIO(NVMCMD, (val ? NVM_WORD_WRITE : NVM_SECTION_ERASE) );
if(comnd == 'c'){
tpi_send_byte(SSTp);
if(val){
tpi_send_byte(b & 0b11111011);
}else{
tpi_send_byte(b | 0x04);
}
tpi_send_byte(SSTp);
tpi_send_byte(0xFF);
}else if(comnd == 'w'){
tpi_send_byte(SSTp);
if(val){
tpi_send_byte(b & 0b11111101);
}else{
tpi_send_byte(b | 0x02);
}
tpi_send_byte(SSTp);
tpi_send_byte(0xFF);
}else if(comnd == 'r'){
tpi_send_byte(SSTp);
if(val){
tpi_send_byte(b & 0b11111110);
}else{
tpi_send_byte(b | 0x01);
}
tpi_send_byte(SSTp);
tpi_send_byte(0xFF);
}else if(comnd == 'x'){
// do nothing
}else{
Serial.println(F("received unknown command. Cancelling"));
}
while((readIO(NVMCSR) & (1<<7)) != 0x00){
// wait for write to finish
}
writeIO(NVMCMD, NVM_NOP);
SPI.transfer(0xff);
SPI.transfer(0xff);
if(comnd != 'x'){
Serial.print(F("\n\nSuccessfully "));
if(val)
Serial.print(F("Set "));
else
Serial.print(F("Cleared "));
Serial.print(F("\""));
if(comnd == 'w')
Serial.print(F("Watchdog"));
else if(comnd == 'c')
Serial.print(F("Clock Output"));
else if(comnd == 'r')
Serial.print(F("Reset"));
Serial.println(F("\" Flag\n"));
}
}
void finish(){
writeCSS(0x00, 0x00);
SPI.transfer(0xff);
SPI.transfer(0xff);
hvReset(HIGH);
// digitalWrite(SS, HIGH); // release RESET
delay(1); // t_RST min = 400 ns @ Vcc = 5 V
SPI.end();
DDRB &= 0b11000011; //tri-state spi so target can be tested
PORTB &= 0b11000011;
}
void checkID(){
// check the device ID
uint8_t id1, id2, id3;
setPointer(0x3FC0);
tpi_send_byte(SLDp);
id1 = tpi_receive_byte();
tpi_send_byte(SLDp);
id2 = tpi_receive_byte();
tpi_send_byte(SLDp);
id3 = tpi_receive_byte();
if(id1==0x1E && id2==0x8F && id3==0x0A){
Serial.print(F("ATtiny4"));
type = Tiny4_5;
}else if(id1==0x1E && id2==0x8F && id3==0x09){
Serial.print(F("ATtiny5"));
type = Tiny4_5;
}else if(id1==0x1E && id2==0x90 && id3==0x08){
Serial.print(F("ATtiny9"));
type = Tiny9;
}else if(id1==0x1E && id2==0x90 && id3==0x03){
Serial.print(F("ATtiny10"));
type = Tiny10;
}else if(id1==0x1E && id2==0x91 && id3==0x0f){
Serial.print(F("ATtiny20"));
type = Tiny20;
}else if(id1==0x1E && id2==0x92 && id3==0x0e){
Serial.print(F("ATtiny40"));
type = Tiny40;
}else{
Serial.print(F("Unknown chip"));
}
Serial.println(F(" connected"));
}
/*
* send a byte in one TPI frame (12 bits)
* (1 start + 8 data + 1 parity + 2 stop)
* using 2 SPI data bytes (2 x 8 = 16 clocks)
* (with 4 extra idle bits)
*/
void tpi_send_byte( uint8_t data ){
// compute partiy bit
uint8_t par = data;
par ^= (par >> 4); // b[7:4] (+) b[3:0]
par ^= (par >> 2); // b[3:2] (+) b[1:0]
par ^= (par >> 1); // b[1] (+) b[0]
// REMEMBER: this is in LSBfirst mode and idle is high
// (2 idle) + (1 start bit) + (data[4:0])
SPI.transfer(0x03 | (data << 3));
// (data[7:5]) + (1 parity) + (2 stop bits) + (2 idle)
SPI.transfer(0xf0 | (par << 3) | (data >> 5));
} // end tpi_send_byte()
/*
* receive TPI 12-bit format byte data
* via SPI 2 bytes (16 clocks) or 3 bytes (24 clocks)
*/
uint8_t tpi_receive_byte( void ){
//uint8_t b1, b2, b3;
// keep transmitting high(idle) while waiting for a start bit
do {
b1 = SPI.transfer(0xff);
} while (0xff == b1);
// get (partial) data bits
b2 = SPI.transfer(0xff);
// if the first byte(b1) contains less than 4 data bits
// we need to get a third byte to get the parity and stop bits
if (0x0f == (0x0f & b1)) {
b3 = SPI.transfer(0xff);
}
// now shift the bits into the right positions
// b1 should hold only idle and start bits = 0b01111111
while (0x7f != b1) { // data not aligned
b2 <<= 1; // shift left data bits
if (0x80 & b1) { // carry from 1st byte
b2 |= 1; // set bit
}
b1 <<= 1;
b1 |= 0x01; // fill with idle bit (1)
}
// now the data byte is stored in b2
return( b2 );
} // end tpi_receive_byte()
// send the 64 bit NVM key
void send_skey(uint64_t nvm_key){
tpi_send_byte(SKEY);
while(nvm_key){
tpi_send_byte(nvm_key & 0xFF);
nvm_key >>= 8;
}
} // end send_skey()
// sets the pointer address
void setPointer(unsigned short address){
adrs = address;
tpi_send_byte(SSTPRL);
tpi_send_byte(address & 0xff);
tpi_send_byte(SSTPRH);
tpi_send_byte((address>>8) & 0xff);
}
// writes using SOUT
void writeIO(uint8_t address, uint8_t value){
// SOUT 0b1aa1aaaa replace a with 6 address bits
tpi_send_byte(0x90 | (address & 0x0F) | ((address & 0x30) << 1));
tpi_send_byte(value);
}
// reads using SIN
uint8_t readIO(uint8_t address){
// SIN 0b0aa1aaaa replace a with 6 address bits
tpi_send_byte(0x10 | (address & 0x0F) | ((address & 0x30) << 1));
return tpi_receive_byte();
}
// writes to CSS
void writeCSS(uint8_t address, uint8_t value){
tpi_send_byte(0xC0 | address);
tpi_send_byte(value);
}
// reads from CSS
uint8_t readCSS(uint8_t address){
tpi_send_byte(0x80 | address);
return tpi_receive_byte();
}
// converts two chars to one byte
// c1 is MS, c2 is LS
uint8_t byteval(char c1, char c2){
uint8_t by;
if(c1 <= '9'){
by = c1-'0';
}else{
by = c1-'A'+10;
}
by = by << 4;
if(c2 <= '9'){
by += c2-'0';
}else{
by += c2-'A'+10;
}
return by;
}
char Sread(void){
while(Serial.available()<1){}
return Serial.read();
}
void outHex(unsigned int n, char l){ // call with the number to be printed, and # of nibbles expected.
for(char count = l-1; count > 0;count--){ // quick and dirty to add zeros to the hex value
if(((n >> (count*4)) & 0x0f) == 0) // if MSB is 0
Serial.print(F("0")); //prepend a 0
else
break; //exit the for loop
}
Serial.print(n, HEX);
}
// end of file