ATTiny10Programmer Arduino Code
The following listing shows the complete source for the code that runs on the Arduino and drives the programmer circuit. The Arduino project file for this code is also available as download at the bottom of the page.
#include <TimerOne.h>
#include <avr/io.h>
/*
* ATTiny10 TPI High Voltage Programmer (uses charge pump to create HV)
*/
// +-\/-+
// TPIDAT / PB0 | | 6 PB3 / RESET
// Gnd | | 5 Vcc
// TPICLK / PB1 | | 4 PB2
// +----+
unsigned char flashMem[1024];
#define BIT_TIME 1
#define VCC 2 // Pin 5 on ATTiny10
#define TPIDAT 3 // Pin 1 on ATTiny10
#define TPICLK 4 // Pin 3 on ATTiny10
// Define IO Registers
#define NVMCMD 0x33
#define NVMCSR 0x32
#define NVM Commands
#define NO_OPERATION 0x00
#define CHIP_ERASE 0x10
#define SECTION_ERASE 0x14
#define WORD_WRITE 0x1D
// Define Port C Direct I/O pins for Charge Pump
#define P1 0x20 // Port C Pin A5
#define P2 0x08 // Port C Pin A3
#define PWR 0x10 // Port C Pin A4
#define GND 0x02 // Port C Pin A1
#define REF 404 // 12 volt reference (adjust to suit)
// Define 12 sense pin
#define AIN A2 // Pin A2
#define REF 404 // 12 volt reference
// Variables used by Charge pump
volatile char phase = 0;
volatile char onOff = 0;
volatile char pwrOn = 0;
void ticker () {
if (onOff) {
DDRC = P1 | P2 | PWR | GND;
int volts = analogRead(AIN);
if (volts < REF) {
if (phase) {
PORTC = P1 | PWR;
} else {
PORTC = P2 | PWR;
}
phase ^= 1;
} else {
pwrOn = 1;
}
} else {
pwrOn = 0;
DDRC = GND;
PORTC = GND;
}
}
void setup() {
Serial.begin(57600);
pinMode(VCC, OUTPUT);
pinMode(TPICLK, OUTPUT);
pinMode(TPIDAT, INPUT);
digitalWrite(VCC, LOW); // VCC off
digitalWrite(TPICLK, HIGH); // Clk high
digitalWrite(TPIDAT, HIGH); // Enable pullup
// Setup timer interrupt for charge pump
analogReference(DEFAULT);
Timer1.initialize(500);
Timer1.attachInterrupt(ticker);
}
void pulseClock () {
digitalWrite(TPICLK, LOW);
delayMicroseconds(BIT_TIME);
digitalWrite(TPICLK, HIGH);
delayMicroseconds(BIT_TIME);
}
void sendFrame (unsigned char data) {
unsigned char tmp = data;
// Write start bit
pinMode(TPIDAT, OUTPUT);
digitalWrite(TPIDAT, LOW);
pulseClock();
unsigned char check = 0;
// Write data bits
for (unsigned char ii = 0; ii < 8; ii++) {
digitalWrite(TPIDAT, tmp & 1);
check ^= tmp & 1;
tmp >>= 1;
pulseClock();
}
// Write check bit
digitalWrite(TPIDAT, check);
pulseClock();
// Write stop bits
digitalWrite(TPIDAT, HIGH);
pulseClock();
pinMode(TPIDAT, INPUT);
pulseClock();
}
unsigned char readBit () {
digitalWrite(TPICLK, LOW);
delayMicroseconds(BIT_TIME);
unsigned char data = digitalRead(TPIDAT) ? 1 : 0;
digitalWrite(TPICLK, HIGH);
delayMicroseconds(BIT_TIME);
return data;
}
unsigned char readFrame () {
pinMode(TPIDAT, INPUT);
for (int ii = 0; ii < 256; ii++) {
if (readBit() == 0) {
// Start bit found
unsigned char data = 0;
unsigned char check = 0;
for (unsigned char jj = 0; jj < 8; jj++) {
unsigned char tmp = readBit();
data >>= 1;
data |= tmp != 0 ? 0x80 : 0x00;
check ^= tmp & 1;
}
unsigned char tmp = readBit();
readBit();
readBit();
if (tmp == check)
return data;
return 0xFF;
}
}
Serial.println("timeout");
// Timeout, or error
return 0xFF;
}
unsigned char enterProgMode () {
sendFrame(0xE0); // SKEY Opcode
sendFrame(0xFF); // Send 8 Byte Key
sendFrame(0x88);
sendFrame(0xD8);
sendFrame(0xCD);
sendFrame(0x45);
sendFrame(0xAB);
sendFrame(0x89);
sendFrame(0x12);
for (unsigned char ii = 0; ii < 100; ii++) {
if ((readCtlSpace(0x00) & 0x02) != 0) {
return 1; // enabled
}
}
return 0; // timeout
}
void setPointerReg (unsigned int add) {
sendFrame(0x68); // SSTPR 0
sendFrame(add & 0xFF);
sendFrame(0x69); // SSTPR 1
sendFrame(add >> 8);
}
unsigned char readAndInc () {
sendFrame(0x24); // SLD++
return readFrame();
}
void writeAndInc (unsigned char data) {
sendFrame(0x64); // SST++
sendFrame(data);
}
unsigned char readIoSpace (unsigned char add) {
sendFrame(0x10 | ((add & 0x30) << 1) | (add & 0x0F)); // SIN
return readFrame();
}
void writeIoSpace(unsigned char add, unsigned char data) {
sendFrame(0x90 | ((add & 0x30) << 1) | (add & 0x0F)); // SOUT
sendFrame(data);
}
unsigned char readCtlSpace (unsigned char add) {
sendFrame(0x80 | (add & 0x0F)); // SLDCS
return readFrame();
}
void nvmWait () {
while ((readIoSpace(0x32) & 0x80) != 0)
delay(BIT_TIME); // Arbitrary delay
}
void printHex (unsigned char val) {
Serial.print("0x");
if (val < 16)
Serial.print("0");
Serial.print(val, HEX);
}
void powerOn () {
digitalWrite(VCC, HIGH); // VCC on
delay(128); // Wait 128 ms
onOff = 1; // 12v On
while (pwrOn == 0)
;
delay(10); // Wait 10 ms
digitalWrite(TPIDAT, HIGH); // Data high
pinMode(TPIDAT, OUTPUT);
for (unsigned char ii = 0; ii < 32; ii++) {
pulseClock();
}
}
void powerOff () {
onOff = 0; // 12v Off
delay(128); // Wait 128 ms
digitalWrite(VCC, LOW); // VCC off
}
/*
* 0x1E, 0x8F, 0x0A ATTiny4
* 0x1E, 0x8F, 0x09 ATTiny5
* 0x1E, 0x90, 0x08 ATTiny9
* 0x1E, 0x90, 0x03 ATTiny10 (verified)
*/
void printSignature () {
powerOn();
if (enterProgMode()) {
// Read device signature
Serial.println();
Serial.print("SIG: ");
setPointerReg(0x3FC0); // Device Id Bytes
unsigned char sig[3];
for (unsigned char ii = 0; ii < 3; ii++) {
if (ii > 0)
Serial.print(", ");
printHex(sig[ii] = readAndInc());
}
if (sig[0] == 0x1E) {
if (sig[1] == 0x8f) {
if (sig[2] == 0x0A) {
Serial.println(" - ATTiny4");
} else if (sig[2] == 0x09) {
Serial.println(" - ATTiny5");
}
} else if (sig[1] == 0x90) {
if (sig[2] == 0x08) {
Serial.println(" - ATTiny9");
} else if (sig[2] == 0x03) {
Serial.println(" - ATTiny10");
}
}
}
Serial.println();
}
powerOff();
}
void printFuses () {
powerOn();
if (enterProgMode()) {
// Read device signature
Serial.print("FUSES: ");
setPointerReg(0x3F40); // Coniguration Byte
printHex(readAndInc());
Serial.println();
}
powerOff();
}
void writeFlash (unsigned char fuseByte) {
powerOn();
if (enterProgMode()) {
// Erase all flash bytes
writeIoSpace(NVMCMD, CHIP_ERASE);
setPointerReg(0x4001);
writeAndInc(0x00); // Write dummy high byte
nvmWait();
writeIoSpace(NVMCMD, NO_OPERATION);
nvmWait();
// Erase Config Section
writeIoSpace(NVMCMD, SECTION_ERASE);
setPointerReg(0x3F41);
writeAndInc(0x00); // Write dummy high byte
nvmWait();
writeIoSpace(NVMCMD, NO_OPERATION);
nvmWait();
// Write config byte (Note: bits are inverted, so '0' value is ON)
writeIoSpace(NVMCMD,WORD_WRITE);
setPointerReg(0x3F40);
writeAndInc(fuseByte | 0xF8); // Config Byte
writeAndInc(0xFF);
writeIoSpace(NVMCMD, NO_OPERATION);
nvmWait();
// Write program to flash memory
writeIoSpace(NVMCMD,WORD_WRITE);
setPointerReg(0x4000);
for (int ii = 0; ii < sizeof(flashMem); ii += 2) {
writeAndInc(flashMem[ii]); // Low byte
writeAndInc(flashMem[ii + 1]); // High byte
nvmWait();
}
writeIoSpace(NVMCMD, NO_OPERATION);
nvmWait();
}
powerOff();
}
unsigned int newDigit (unsigned int cVal, unsigned char digit) {
cVal <<= 4;
if (digit >= 'A' && digit <= 'F') {
cVal += digit - 'A' + 10;
} else {
cVal += digit - '0';
}
return cVal;
}
void loop() {
unsigned char state = 0;
unsigned char len, type, data, check, fuse;
unsigned int add;
while (1) {
if (Serial.available() > 0) {
unsigned char cc = Serial.read();
if (cc == ':') {
state = 1;
len = 0;
check = 0;
add = 0;
} else if (cc == '*') {
fuse = 0xFF;
state = 20; // Program fuses
} else if (cc == 'T') {
Serial.println("Ready");
} else if (cc == 'S') {
printSignature();
printFuses();
} else {
switch (state) {
case 1: // Length MSD
case 2: // Length LSD
len = newDigit(len, cc);
state++;
break;
case 3: // Address MSD
case 4:
case 5:
case 6: // Address LSD
add = newDigit(add, cc);
state++;
break;
case 7: // Type MSD
type = newDigit(0, cc);
state++;
break;
case 8: // Type LSD
type = newDigit(type, cc);
if (len > 0)
state++;
else
state = 11;
break;
case 9: // Data byte MSD
data = newDigit(0, cc);
state++;
break;
case 10: // Data byte LSD
data = newDigit(data, cc);
if (type == 0)
flashMem[add++] = data;
if (--len > 0)
state = 9; // Get another data byte
else
state = 11; // Get checksum
break;
case 11: // Checksum MSD
case 12: // Checksum LSD
check = newDigit(check, cc);
state++;
break;
case 13: // Wait for CR/LF
if (cc == 0x0A || cc == 0x0D) {
switch (type) {
case 0: // Data record
Serial.print(".");
break;
case 1: // EOF Record
writeFlash(fuse);
Serial.println("Done");
break;
case 2: // Start of Record (actually extended segment address record)
// Set buffer space to unprogrammed flash value (0xFF)
for (int ii = 0; ii < sizeof(flashMem); ii++) {
flashMem[ii] = 0xFF;
}
fuse = 0xFF;
break;
}
state = 0;
}
break;
case 20: // Fuse LS Byte
fuse = newDigit(fuse, cc);
state = 0;
break;
}
}
}
}
}