├── README.md └── TPI_Programmer.ino /README.md: -------------------------------------------------------------------------------- 1 | "# TPI_Programmer" 2 | 3 | Project is based on http://junkplusarduino.blogspot.jp/p/attiny10-resources.html. I tried to find a way to contact them for my updates, but could not find their contact info. 4 | 5 | TPI programmer for ATtiny4/5/9/10/20/40/102/104 6 | 7 | Make the connections as shown here http://junkplusarduino.blogspot.jp/p/attiny10-resources.html. 8 | 9 | To use: 10 | 11 | ***** Buad rate must be set to 9600 **** 12 | - Upload to arduino and power off 13 | - Connect ATtiny10 as shown 14 | - Power on and open the serial monitor 15 | - If things are working so far you should 16 | see "NVM enabled" and "ATtiny10/20/40 connected". 17 | - Input one-letter commands via serial monitor: 18 | 19 | D = dump memory. Displays all current memory on the chip 20 | 21 | E = erase chip. Erases all program memory automatically done at time of programming 22 | 23 | P = write program. After sending this, paste the program from the hex file into the serial monitor. 24 | 25 | S = set fuse. follow the instructions to set one of the three fuses. 26 | 27 | C = clear fuse. follow the instructions to clear one of the three fuses. 28 | 29 | H = Toggle High Voltage Programming 30 | 31 | T = Toggle +12v enabled by High, or Low 32 | 33 | R/r = Quick reset 34 | 35 | - Finally, power off the arduino and remove the Attiny10/20/40 36 | 37 | thanks to pcm1723 for tpitest.pde upon which this is based 38 | -------------------------------------------------------------------------------- /TPI_Programmer.ino: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | * TPI programmer for ATtiny4/5/9/10/20/40 3 | * 4 | * Make the connections as shown below. 5 | * 6 | * To use: 7 | ***** Buad rate must be set to 9600 **** 8 | * 9 | * - Upload to arduino and power off 10 | * - Connect ATtiny10 as shown 11 | * - Power on and open the serial monitor 12 | * - If things are working so far you should 13 | * see "NVM enabled" and "ATtiny10/20/40 connected". 14 | * - Input one-letter commands via serial monitor: 15 | * 16 | * D = dump memory. Displays all current memory 17 | * on the chip 18 | * 19 | * E = erase chip. Erases all program memory 20 | * automatically done at time of programming 21 | * 22 | * P = write program. After sending this, paste 23 | * the program from the hex file into the 24 | * serial monitor. 25 | * 26 | * S = set fuse. follow the instructions to set 27 | * one of the three fuses. 28 | * 29 | * C = clear fuse. follow the instructions to clear 30 | * one of the three fuses. 31 | * 32 | * H = Toggle High Voltage Programming 33 | * 34 | * T = Toggle +12v enabled by High, or Low 35 | * 36 | * R/r = Quick reset 37 | * 38 | * - Finally, power off the arduino and remove the 39 | * Attiny10/20/40 40 | * 41 | * 42 | * Arduino ATtiny10 * 43 | * ----------+ +---------------- * 44 | * (SS#) 10 |--[R]-----| 6 (RESET#/PB3) * 45 | * | | * 46 | * (MOSI) 11 |--[R]--+--| 1 (TPIDATA/PB0) * 47 | * | | | * 48 | * (MISO) 12 |--[R]--+ | * 49 | * | | * 50 | * (SCK) 13 |--[R]-----| 3 (TPICLK/PB1) * 51 | * ----------+ +---------------- * 52 | * * 53 | * ----------+ +---------------- * 54 | * (HVP) 9 |--- | 6 (RESET#/PB3) * 55 | * | | * 56 | * * 57 | * -[R]- = a 220 - 1K Ohm resistor * 58 | * * 59 | * this picture : 2011/12/08 by pcm1723 * 60 | * modified :2015/02/27 by KD * 61 | * * 62 | * thanks to pcm1723 for tpitest.pde upon which * 63 | * this is based * 64 | ************************************************** 65 | Updates: 66 | Dec 04, 2017: thejamestate@gmail.com 67 | * Added support for ATtiny102 and ATtiny104 68 | Jan 23, 2017: Ksdsksd@gmail.com 69 | * Thanks to InoueTaichi Fixed incorrect #define Tiny40 70 | 71 | Mar 05, 2015: Ksdsksd@gamil.com 72 | * Added notifications to setting and clearing the system flags. 73 | 74 | Feb 23, 2015: Ksdsksd@gamil.com 75 | * Changed the programmer Diagram, This is the config I use, and get a sucessful programming of a tiny10 at 9600 baud. 76 | 77 | Mar 22, 2014: Ksdsksd@gmail.com 78 | * Added the quick reset to high before resetting the device. 79 | * Added code to stop the SPI and float the pins for testing the device while connected. 80 | 81 | Mar 20, 2014: Ksdsksd@gmail.com 82 | * Added a quick reset by sending 'r' or 'R' via the serial monitor. 83 | * Added a High voltage programming option from pin 9, toggled by 'H' 84 | * Added a High/low option for providing 12v to the reset pin, toggled by 'T' 85 | 86 | Mar 17, 2014: Ksdsksd@gmail.com 87 | * Had some trouble with the nibbles being swapped when programming on the 10 & 20, 88 | added b1,b2 to hold the serial data before calling byteval() 89 | * Added Nat Blundell's patch to the code 90 | 91 | Apr 10, 2013: Ksdsksd@gmail.com 92 | * Applied Fix for setting and clearing flags 93 | 94 | Feb 7, 2013: Ksdsksd@gmail.com 95 | * Fixed programming timer, had intitial start at zero instead of current time. 96 | 97 | Dec 11, 2012: Ksdsksd@gmail.com 98 | * Added detect and programming for 4/5/9 99 | 100 | Dec 5-6, 2012: Ksdsksd@gmail.com 101 | * Incorperated read, and verify into program. Now have no program size limitation by using 328p. 102 | * Changed the outHex routines consolidated them into 1, number to be printed, and number of nibbles 103 | * Added a type check to distinguish between Tiny10/20/40 104 | * Added an auto word size check to ensure that there is the proper amount of words written for a 10/20/40 105 | * Removed Read program, Verify, and Finish from options 106 | * Changed baud rate to 19200 for delay from data written to the chip, to prevent serial buffer overrun. 107 | 108 | Oct 5, 2012: Ksdsksd@gmail.com 109 | *** Noticed that when programming, the verification fails 110 | at times by last 1-2 bytes programmed, and the Tiny would act erratic. 111 | Quick fix was adding 3 NOP's to the end the Tiny's code, and ignoring the errors, the Tiny then performed as expected. 112 | 113 | Oct 4, 2012: Ksdsksd@gmail.com 114 | * Moved all Serial printed strings to program space 115 | * Added code to detect Tiny20 116 | */ 117 | 118 | #include 119 | #include "pins_arduino.h" 120 | 121 | // define the instruction set bytes 122 | #define SLD 0x20 123 | #define SLDp 0x24 124 | #define SST 0x60 125 | #define SSTp 0x64 126 | #define SSTPRH 0x69 127 | #define SSTPRL 0x68 128 | // see functions below //////////////////////////////// 129 | // SIN 0b0aa1aaaa replace a with 6 address bits 130 | // SOUT 0b1aa1aaaa replace a with 6 address bits 131 | // SLDCS 0b1000aaaa replace a with address bits 132 | // SSTCS 0b1100aaaa replace a with address bits 133 | /////////////////////////////////////////////////////// 134 | #define SKEY 0xE0 135 | #define NVM_PROGRAM_ENABLE 0x1289AB45CDD888FFULL // the ULL means unsigned long long 136 | 137 | #define NVMCMD 0x33 138 | #define NVMCSR 0x32 139 | #define NVM_NOP 0x00 140 | #define NVM_CHIP_ERASE 0x10 141 | #define NVM_SECTION_ERASE 0x14 142 | #define NVM_WORD_WRITE 0x1D 143 | 144 | #define HVReset 9 145 | 146 | #define Tiny4_5 10 147 | #define Tiny9 1 148 | #define Tiny10 1 149 | #define Tiny20 2 150 | #define Tiny40 4 151 | #define Tiny102 1 152 | #define Tiny104 1 153 | 154 | #define TimeOut 1 155 | #define HexError 2 156 | #define TooLarge 3 157 | // represents the current pointer register value 158 | unsigned short adrs = 0x0000; 159 | 160 | // used for storing a program file 161 | uint8_t data[16]; //program data 162 | unsigned int progSize = 0; //program size in bytes 163 | 164 | // used for various purposes 165 | long startTime; 166 | int timeout; 167 | uint8_t b, b1, b2, b3; 168 | boolean idChecked; 169 | boolean correct; 170 | char type; // type of chip connected 1 = Tiny10, 2 = Tiny20 171 | char HVP = 0; 172 | char HVON = 0; 173 | 174 | int counti = 0; 175 | 176 | void setup(){ 177 | // set up serial 178 | Serial.begin(9600); // you cant increase this, it'll overrun the buffer 179 | // set up SPI 180 | /* SPI.begin(); 181 | SPI.setBitOrder(LSBFIRST); 182 | SPI.setDataMode(SPI_MODE0); 183 | SPI.setClockDivider(SPI_CLOCK_DIV32); 184 | 185 | */ start_tpi(); 186 | 187 | pinMode(HVReset, OUTPUT); 188 | // initialize memory pointer register 189 | setPointer(0x0000); 190 | 191 | timeout = 20000; 192 | idChecked = false; 193 | } // end setup() 194 | 195 | 196 | void hvserial() 197 | { 198 | if(HVP) 199 | Serial.println(F("***High Voltage Programming Enabled***")); 200 | else 201 | Serial.println(F("High Voltage Programming Disabled")); 202 | 203 | Serial.print(F("Pin 9 ")); 204 | Serial.print(HVON?F("HIGH"):F("LOW")); 205 | Serial.print(F(" supplies 12v")); 206 | 207 | } 208 | 209 | 210 | void hvReset(char highLow) 211 | { 212 | if(HVP) 213 | { 214 | if(HVON) //if high enables 12v 215 | highLow = !highLow; // invert the typical reset 216 | digitalWrite(HVReset, highLow); 217 | } 218 | else 219 | digitalWrite(SS, highLow); 220 | } 221 | 222 | void quickReset() 223 | { 224 | digitalWrite(SS,HIGH); 225 | delay(1); 226 | digitalWrite(SS,LOW); 227 | delay(10); 228 | digitalWrite(SS,HIGH); 229 | } 230 | 231 | void start_tpi() { 232 | SPI.begin(); 233 | SPI.setBitOrder(LSBFIRST); 234 | SPI.setDataMode(SPI_MODE0); 235 | SPI.setClockDivider(SPI_CLOCK_DIV32); 236 | 237 | // enter TPI programming mode 238 | hvReset(LOW); 239 | // digitalWrite(SS, LOW); // assert RESET on tiny 240 | delay(1); // t_RST min = 400 ns @ Vcc = 5 V 241 | 242 | SPI.transfer(0xff); // activate TPI by emitting 243 | SPI.transfer(0xff); // 16 or more pulses on TPICLK 244 | SPI.transfer(0xff); // while holding TPIDATA to "1" 245 | 246 | writeCSS(0x02, 0x04); // TPIPCR, guard time = 8bits (default=128) 247 | send_skey(NVM_PROGRAM_ENABLE); // enable NVM interface 248 | // wait for NVM to be enabled 249 | 250 | while((readCSS(0x00) & 0x02) < 1){ 251 | // wait 252 | } 253 | Serial.println(F("NVM enabled")); 254 | } 255 | 256 | void loop(){ 257 | if(!idChecked){ 258 | // start_tpi(); 259 | checkID(); 260 | idChecked = true; 261 | finish(); 262 | } 263 | // when ready, send ready signal '.' and wait 264 | Serial.print(F("\n>")); 265 | while(Serial.available() < 1){ 266 | // wait 267 | } 268 | start_tpi(); 269 | 270 | // the first byte is a command 271 | //** 'P' = program the ATtiny using the read program 272 | //** 'D' = dump memory to serial monitor 273 | //** 'E' = erase chip. erases current program memory.(done automatically by 'P') 274 | //** 'S' = set fuse 275 | //** 'C' = clear fuse 276 | 277 | char comnd = Sread(); 278 | 279 | switch( comnd ){ 280 | case 'r': 281 | case'R': 282 | quickReset(); 283 | break; 284 | 285 | case 'D': 286 | dumpMemory(); 287 | break; 288 | 289 | case 'H': 290 | HVP = !HVP; 291 | hvserial(); 292 | break; 293 | 294 | case 'T': 295 | HVON = !HVON; 296 | hvserial(); 297 | break; 298 | 299 | 300 | case 'P': 301 | if(!writeProgram()){ 302 | startTime = millis(); 303 | while(millis()-startTime < 8000) 304 | Serial.read();// if exited due to error, disregard all other serial data 305 | } 306 | 307 | break; 308 | 309 | case 'E': 310 | eraseChip(); 311 | break; 312 | 313 | case 'S': 314 | setConfig(true); 315 | break; 316 | 317 | case 'C': 318 | setConfig(false); 319 | break; 320 | 321 | default: 322 | Serial.println(F("Received unknown command")); 323 | } 324 | 325 | finish(); 326 | 327 | 328 | } 329 | void ERROR_pgmSize(void) 330 | { 331 | Serial.println(F("program size is 0??")); 332 | } 333 | 334 | void ERROR_data(char i) 335 | { 336 | Serial.println(F("couldn't receive data:")); 337 | switch(i){ 338 | case TimeOut: 339 | Serial.println(F("timed out")); 340 | break; 341 | case HexError: 342 | Serial.println(F("hex file format error")); 343 | break; 344 | case TooLarge: 345 | Serial.println(F("program is too large")); 346 | break; 347 | 348 | default: 349 | break; 350 | } 351 | 352 | } 353 | 354 | 355 | // print the register, SRAM, config and signature memory 356 | void dumpMemory(){ 357 | unsigned int len; 358 | uint8_t i; 359 | // initialize memory pointer register 360 | setPointer(0x0000); 361 | 362 | Serial.println(F("Current memory state:")); 363 | if(type != Tiny4_5) 364 | len = 0x400 * type; //the memory length for a 10/20/40 is 1024/2048/4096 365 | else 366 | len = 0x200; //tiny 4/5 has 512 bytes 367 | len += 0x4000; 368 | 369 | while(adrs < len){ 370 | // read the byte at the current pointer address 371 | // and increment address 372 | tpi_send_byte(SLDp); 373 | b = tpi_receive_byte(); // get data byte 374 | 375 | // read all the memory, but only print 376 | // the register, SRAM, config and signature memory 377 | if ((0x0000 <= adrs && adrs <= 0x005F) // register/SRAM 378 | |(0x3F00 <= adrs && adrs <= 0x3F01) // NVM lock bits 379 | |(0x3F40 <= adrs && adrs <= 0x3F41) // config 380 | |(0x3F80 <= adrs && adrs <= 0x3F81) // calibration 381 | |(0x3FC0 <= adrs && adrs <= 0x3FC3) // ID 382 | |(0x4000 <= adrs && adrs <= len-1) ) { // program 383 | // print +number along the top 384 | if ((0x00 == adrs) 385 | |(0x3f00 == adrs) // NVM lock bits 386 | |(0x3F40 == adrs) // config 387 | |(0x3F80 == adrs) // calibration 388 | |(0x3FC0 == adrs) // ID 389 | |(0x4000 == adrs) ) { 390 | Serial.println(); 391 | if(adrs == 0x0000){ Serial.print(F("registers, SRAM")); } 392 | if(adrs == 0x3F00){ Serial.print(F("NVM lock")); } 393 | if(adrs == 0x3F40){ Serial.print(F("configuration")); } 394 | if(adrs == 0x3F80){ Serial.print(F("calibration")); } 395 | if(adrs == 0x3FC0){ Serial.print(F("device ID")); } 396 | if(adrs == 0x4000){ Serial.print(F("program")); } 397 | Serial.println(); 398 | for (i = 0; i < 5; i++) 399 | Serial.print(F(" ")); 400 | for (i = 0; i < 16; i++) { 401 | Serial.print(F(" +")); 402 | Serial.print(i, HEX); 403 | } 404 | } 405 | // print number on the left 406 | if (0 == (0x000f & adrs)) { 407 | Serial.println(); 408 | outHex(adrs, 4); 409 | Serial.print(F(": ")); // delimiter 410 | } 411 | outHex(b, 2); 412 | Serial.print(F(" ")); 413 | } 414 | adrs++; // increment memory address 415 | if(adrs == 0x0060){ 416 | // skip reserved memory 417 | setPointer(0x3F00); 418 | } 419 | } 420 | Serial.println(F(" ")); 421 | } // end dumpMemory() 422 | 423 | 424 | // receive and translate the contents of a hex file, Program and verify on the fly 425 | boolean writeProgram(){ 426 | char datlength[] = "00"; 427 | char addr[] = "0000"; 428 | char something[] = "00"; 429 | char chksm[] = "00"; 430 | unsigned int currentByte = 0; 431 | progSize = 0; 432 | uint8_t linelength = 0; 433 | boolean fileEnd = false; 434 | unsigned short tadrs; 435 | tadrs = adrs = 0x4000; 436 | correct = true; 437 | unsigned long pgmStartTime = millis(); 438 | eraseChip(); // erase chip 439 | char words = (type!=Tiny4_5?type:1); 440 | char b1, b2; 441 | // read in the data and 442 | while(!fileEnd){ 443 | startTime = millis(); 444 | while(Serial.available() < 1){ 445 | if(millis()-startTime > timeout){ 446 | ERROR_data(TimeOut); 447 | return false; 448 | } 449 | if(pgmStartTime == 0) 450 | pgmStartTime = millis(); 451 | } 452 | if(Sread() != ':'){ // maybe it was a newline?? 453 | if(Sread() != ':'){ 454 | ERROR_data(HexError); 455 | return false; 456 | } 457 | } 458 | // read data length 459 | 460 | datlength[0] = Sread(); 461 | datlength[1] = Sread(); 462 | linelength = byteval(datlength[0], datlength[1]); 463 | 464 | // read address. if "0000" currentByte = 0 465 | addr[0] = Sread(); 466 | addr[1] = Sread(); 467 | addr[2] = Sread(); 468 | addr[3] = Sread(); 469 | if(linelength != 0x00 && addr[0]=='0' && addr[1]=='0' && addr[2]=='0' && addr[3]=='0') 470 | currentByte = 0; 471 | 472 | // read type thingy. "01" means end of file 473 | something[0] = Sread(); 474 | something[1] = Sread(); 475 | if(something[1] == '1'){ 476 | fileEnd = true; 477 | } 478 | 479 | if(something[1] == '2'){ 480 | for (int i = 0; i<=linelength; i++){ 481 | Sread(); 482 | Sread(); 483 | } 484 | 485 | } 486 | else{ 487 | // read in the data 488 | for(int k=0; k timeout){ 491 | ERROR_data(TimeOut); 492 | return false; 493 | } 494 | } 495 | b1=Sread(); 496 | b2=Sread(); 497 | data[currentByte] = byteval(b1, b2); 498 | currentByte++; 499 | progSize++; 500 | if(progSize > (type!=Tiny4_5?type*1024:512)){ 501 | ERROR_data(TooLarge); 502 | return 0; 503 | } 504 | 505 | 506 | if(fileEnd) //has the end of the file been reached? 507 | while(currentByte < 2 * words){// append zeros to align the word count to program 508 | data[currentByte] = 0; 509 | currentByte++; 510 | } 511 | 512 | 513 | 514 | if( currentByte == 2 * words ){// is the word/Dword/Qword here? 515 | 516 | currentByte = 0; // yes, reset counter 517 | setPointer(tadrs); // point to the address to program 518 | writeIO(NVMCMD, NVM_WORD_WRITE); 519 | for(int i = 0; i<2 * words; i+=2){// loop for each word size depending on micro 520 | 521 | // now write a word to program memory 522 | tpi_send_byte(SSTp); 523 | tpi_send_byte(data[i]); // LSB first 524 | tpi_send_byte(SSTp); 525 | tpi_send_byte(data[i+1]); // then MSB 526 | SPI.transfer(0xff); //send idle between words 527 | SPI.transfer(0xff); //send idle between words 528 | } 529 | while((readIO(NVMCSR) & (1<<7)) != 0x00){} // wait for write to finish 530 | 531 | writeIO(NVMCMD, NVM_NOP); 532 | SPI.transfer(0xff); 533 | SPI.transfer(0xff); 534 | 535 | 536 | //verify written words 537 | setPointer(tadrs); 538 | for (int c = 0; c < 2 * words; c++){ 539 | tpi_send_byte(SLDp); 540 | b = tpi_receive_byte(); // get data byte 541 | 542 | if(b != data[c]){ 543 | correct = false; 544 | Serial.println(F("program error:")); 545 | Serial.print(F("byte ")); 546 | outHex(adrs, 4); 547 | Serial.print(F(" expected ")); 548 | outHex(data[c],2); 549 | Serial.print(F(" read ")); 550 | outHex(b,2); 551 | Serial.println(); 552 | 553 | if(!correct) 554 | return false; 555 | } 556 | } 557 | tadrs += 2 * words; 558 | } 559 | } 560 | 561 | // read in the checksum. 562 | startTime = millis(); 563 | while(Serial.available() == 0){ 564 | if(millis()-startTime > timeout){ 565 | ERROR_data(TimeOut); 566 | return false; 567 | } 568 | } 569 | chksm[0] = Sread(); 570 | chksm[1] = Sread(); 571 | 572 | } 573 | } 574 | // the program was successfully written 575 | Serial.print(F("Successfully wrote program: ")); 576 | Serial.print(progSize, DEC); 577 | Serial.print(F(" of ")); 578 | if(type != Tiny4_5) 579 | Serial.print(1024 * type, DEC); 580 | else 581 | Serial.print(512, DEC); 582 | Serial.print(F(" bytes\n in ")); 583 | Serial.print((millis()-pgmStartTime)/1000.0,DEC); 584 | Serial.print(F(" Seconds")); 585 | // digitalWrite(SS, HIGH); // release RESET 586 | 587 | return true; 588 | } 589 | 590 | 591 | void eraseChip(){ 592 | // initialize memory pointer register 593 | setPointer(0x4001); // need the +1 for chip erase 594 | 595 | // erase the chip 596 | writeIO(NVMCMD, NVM_CHIP_ERASE); 597 | tpi_send_byte(SSTp); 598 | tpi_send_byte(0xAA); 599 | tpi_send_byte(SSTp); 600 | tpi_send_byte(0xAA); 601 | tpi_send_byte(SSTp); 602 | tpi_send_byte(0xAA); 603 | tpi_send_byte(SSTp); 604 | tpi_send_byte(0xAA); 605 | while((readIO(NVMCSR) & (1<<7)) != 0x00){ 606 | // wait for erasing to finish 607 | } 608 | Serial.println(F("chip erased")); 609 | } 610 | 611 | void setConfig(boolean val){ 612 | // get current config byte 613 | setPointer(0x3F40); 614 | tpi_send_byte(SLD); 615 | b = tpi_receive_byte(); 616 | 617 | Serial.println(F("input one of these letters")); 618 | Serial.println(F("c = system clock output")); 619 | Serial.println(F("w = watchdog timer on")); 620 | Serial.println(F("r = disable reset")); 621 | Serial.println(F("x = cancel. don't change anything")); 622 | 623 | while(Serial.available() < 1){ 624 | // wait 625 | } 626 | char comnd = Serial.read(); 627 | setPointer(0x3F40); 628 | writeIO(NVMCMD, (val ? NVM_WORD_WRITE : NVM_SECTION_ERASE) ); 629 | 630 | if(comnd == 'c'){ 631 | tpi_send_byte(SSTp); 632 | if(val){ 633 | tpi_send_byte(b & 0b11111011); 634 | }else{ 635 | tpi_send_byte(b | 0x04); 636 | } 637 | tpi_send_byte(SSTp); 638 | tpi_send_byte(0xFF); 639 | }else if(comnd == 'w'){ 640 | tpi_send_byte(SSTp); 641 | if(val){ 642 | tpi_send_byte(b & 0b11111101); 643 | }else{ 644 | tpi_send_byte(b | 0x02); 645 | } 646 | tpi_send_byte(SSTp); 647 | tpi_send_byte(0xFF); 648 | }else if(comnd == 'r'){ 649 | tpi_send_byte(SSTp); 650 | if(val){ 651 | tpi_send_byte(b & 0b11111110); 652 | }else{ 653 | tpi_send_byte(b | 0x01); 654 | } 655 | tpi_send_byte(SSTp); 656 | tpi_send_byte(0xFF); 657 | }else if(comnd == 'x'){ 658 | // do nothing 659 | }else{ 660 | Serial.println(F("received unknown command. Cancelling")); 661 | } 662 | while((readIO(NVMCSR) & (1<<7)) != 0x00){ 663 | // wait for write to finish 664 | } 665 | writeIO(NVMCMD, NVM_NOP); 666 | SPI.transfer(0xff); 667 | SPI.transfer(0xff); 668 | if(comnd != 'x'){ 669 | 670 | Serial.print(F("\n\nSuccessfully ")); 671 | if(val) 672 | Serial.print(F("Set ")); 673 | else 674 | Serial.print(F("Cleared ")); 675 | 676 | Serial.print(F("\"")); 677 | if(comnd == 'w') 678 | Serial.print(F("Watchdog")); 679 | else if(comnd == 'c') 680 | Serial.print(F("Clock Output")); 681 | else if(comnd == 'r') 682 | Serial.print(F("Reset")); 683 | 684 | Serial.println(F("\" Flag\n")); 685 | 686 | } 687 | } 688 | 689 | void finish(){ 690 | writeCSS(0x00, 0x00); 691 | SPI.transfer(0xff); 692 | SPI.transfer(0xff); 693 | hvReset(HIGH); 694 | // digitalWrite(SS, HIGH); // release RESET 695 | delay(1); // t_RST min = 400 ns @ Vcc = 5 V 696 | SPI.end(); 697 | DDRB &= 0b11000011; //tri-state spi so target can be tested 698 | PORTB &= 0b11000011; 699 | } 700 | 701 | void checkID(){ 702 | // check the device ID 703 | uint8_t id1, id2, id3; 704 | setPointer(0x3FC0); 705 | 706 | tpi_send_byte(SLDp); 707 | id1 = tpi_receive_byte(); 708 | tpi_send_byte(SLDp); 709 | id2 = tpi_receive_byte(); 710 | tpi_send_byte(SLDp); 711 | id3 = tpi_receive_byte(); 712 | if(id1==0x1E && id2==0x8F && id3==0x0A){ 713 | Serial.print(F("ATtiny4")); 714 | type = Tiny4_5; 715 | }else if(id1==0x1E && id2==0x8F && id3==0x09){ 716 | Serial.print(F("ATtiny5")); 717 | type = Tiny4_5; 718 | }else if(id1==0x1E && id2==0x90 && id3==0x08){ 719 | Serial.print(F("ATtiny9")); 720 | type = Tiny9; 721 | }else if(id1==0x1E && id2==0x90 && id3==0x03){ 722 | Serial.print(F("ATtiny10")); 723 | type = Tiny10; 724 | }else if(id1==0x1E && id2==0x91 && id3==0x0f){ 725 | Serial.print(F("ATtiny20")); 726 | type = Tiny20; 727 | }else if(id1==0x1E && id2==0x92 && id3==0x0e){ 728 | Serial.print(F("ATtiny40")); 729 | type = Tiny40; 730 | }else if(id1==0x1E && id2==0x90 && id3==0x0c){ 731 | Serial.print(F("ATtiny102")); 732 | type = Tiny102; 733 | }else if(id1==0x1E && id2==0x90 && id3==0x0b){ 734 | Serial.print(F("ATtiny104")); 735 | type = Tiny104; 736 | }else{ 737 | Serial.print(F("Unknown chip")); 738 | } 739 | Serial.println(F(" connected")); 740 | } 741 | 742 | /* 743 | * send a byte in one TPI frame (12 bits) 744 | * (1 start + 8 data + 1 parity + 2 stop) 745 | * using 2 SPI data bytes (2 x 8 = 16 clocks) 746 | * (with 4 extra idle bits) 747 | */ 748 | void tpi_send_byte( uint8_t data ){ 749 | // compute partiy bit 750 | uint8_t par = data; 751 | par ^= (par >> 4); // b[7:4] (+) b[3:0] 752 | par ^= (par >> 2); // b[3:2] (+) b[1:0] 753 | par ^= (par >> 1); // b[1] (+) b[0] 754 | 755 | // REMEMBER: this is in LSBfirst mode and idle is high 756 | // (2 idle) + (1 start bit) + (data[4:0]) 757 | SPI.transfer(0x03 | (data << 3)); 758 | // (data[7:5]) + (1 parity) + (2 stop bits) + (2 idle) 759 | SPI.transfer(0xf0 | (par << 3) | (data >> 5)); 760 | } // end tpi_send_byte() 761 | 762 | /* 763 | * receive TPI 12-bit format byte data 764 | * via SPI 2 bytes (16 clocks) or 3 bytes (24 clocks) 765 | */ 766 | uint8_t tpi_receive_byte( void ){ 767 | //uint8_t b1, b2, b3; 768 | // keep transmitting high(idle) while waiting for a start bit 769 | do { 770 | b1 = SPI.transfer(0xff); 771 | } while (0xff == b1); 772 | // get (partial) data bits 773 | b2 = SPI.transfer(0xff); 774 | // if the first byte(b1) contains less than 4 data bits 775 | // we need to get a third byte to get the parity and stop bits 776 | if (0x0f == (0x0f & b1)) { 777 | b3 = SPI.transfer(0xff); 778 | } 779 | 780 | // now shift the bits into the right positions 781 | // b1 should hold only idle and start bits = 0b01111111 782 | while (0x7f != b1) { // data not aligned 783 | b2 <<= 1; // shift left data bits 784 | if (0x80 & b1) { // carry from 1st byte 785 | b2 |= 1; // set bit 786 | } 787 | b1 <<= 1; 788 | b1 |= 0x01; // fill with idle bit (1) 789 | } 790 | // now the data byte is stored in b2 791 | return( b2 ); 792 | } // end tpi_receive_byte() 793 | 794 | // send the 64 bit NVM key 795 | void send_skey(uint64_t nvm_key){ 796 | tpi_send_byte(SKEY); 797 | while(nvm_key){ 798 | tpi_send_byte(nvm_key & 0xFF); 799 | nvm_key >>= 8; 800 | } 801 | } // end send_skey() 802 | 803 | // sets the pointer address 804 | void setPointer(unsigned short address){ 805 | adrs = address; 806 | tpi_send_byte(SSTPRL); 807 | tpi_send_byte(address & 0xff); 808 | tpi_send_byte(SSTPRH); 809 | tpi_send_byte((address>>8) & 0xff); 810 | } 811 | 812 | // writes using SOUT 813 | void writeIO(uint8_t address, uint8_t value){ 814 | // SOUT 0b1aa1aaaa replace a with 6 address bits 815 | tpi_send_byte(0x90 | (address & 0x0F) | ((address & 0x30) << 1)); 816 | tpi_send_byte(value); 817 | } 818 | 819 | // reads using SIN 820 | uint8_t readIO(uint8_t address){ 821 | // SIN 0b0aa1aaaa replace a with 6 address bits 822 | tpi_send_byte(0x10 | (address & 0x0F) | ((address & 0x30) << 1)); 823 | return tpi_receive_byte(); 824 | } 825 | 826 | // writes to CSS 827 | void writeCSS(uint8_t address, uint8_t value){ 828 | tpi_send_byte(0xC0 | address); 829 | tpi_send_byte(value); 830 | } 831 | 832 | // reads from CSS 833 | uint8_t readCSS(uint8_t address){ 834 | tpi_send_byte(0x80 | address); 835 | return tpi_receive_byte(); 836 | } 837 | 838 | // converts two chars to one byte 839 | // c1 is MS, c2 is LS 840 | uint8_t byteval(char c1, char c2){ 841 | uint8_t by; 842 | if(c1 <= '9'){ 843 | by = c1-'0'; 844 | }else{ 845 | by = c1-'A'+10; 846 | } 847 | by = by << 4; 848 | if(c2 <= '9'){ 849 | by += c2-'0'; 850 | }else{ 851 | by += c2-'A'+10; 852 | } 853 | return by; 854 | } 855 | 856 | char Sread(void){ 857 | while(Serial.available()<1){} 858 | return Serial.read(); 859 | } 860 | 861 | 862 | void outHex(unsigned int n, char l){ // call with the number to be printed, and # of nibbles expected. 863 | for(char count = l-1; count > 0;count--){ // quick and dirty to add zeros to the hex value 864 | if(((n >> (count*4)) & 0x0f) == 0) // if MSB is 0 865 | Serial.print(F("0")); //prepend a 0 866 | else 867 | break; //exit the for loop 868 | } 869 | Serial.print(n, HEX); 870 | } 871 | 872 | // end of file 873 | --------------------------------------------------------------------------------