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;

        }

      }

    }

  }

}