/**************************************************
|
|
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
|
|
|