Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator - Electronics Forums

Author Topic: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator  (Read 14571 times)

ElectroNick

  • The forum moderator
  • Administrator
  • Full Member
  • *****
  • Posts: 154
  • Karma: +3/-0
  • The soldering iron is ON!
    • View Profile
    • Electronics Blog
Having just lost connection to my Attiny13 after burning a fuse for 128kHz internal watchdog oscillator, I think this is an issue important enough that deserves its own thread here in the forums.

This time I was using Arduino ISP programmer with the Arduino IDE and smeezekitty's core13 core. But I've had this same issue with the AVRISP MKII programmer as well as a different ATtiny core, so the issue is not with this particular setup but a more general one - ATtiny13 starts operating so slow that reliable ISP operation becomes difficult (impossible?).

Lacking a high-voltage programmer, I'm looking around for a more reliable fix to the issue using only ISP tools, such as the Arduino ISP.

Here is the boards.txt setting for Arduino IDE/core13 that I'm having issues with right now:

Code: [Select]
attiny13.name=Attiny13 @ 128 KHz (internal watchdog oscillator)
attiny13.upload.using=arduino:arduinoisp
attiny13.upload.maximum_size=1024
attiny13.upload.speed=250
attiny13.bootloader.low_fuses=0x7B
attiny13.bootloader.high_fuses=0xFF
attiny13.build.mcu=attiny13
attiny13.build.f_cpu=128000
attiny13.build.core=core13

Any attempt to burn fuses this way makes ATtiny13 invisible to the programmer and no further programming or re-burning of different clock fuses is possible.

I am using a "slow SPI Arduino ISP" sketch by smeezekitty, and the sketch itself also exhibits a strange behavior I haven't seen before: Arduino ISP has this nice "heartbeat" LED indicator that slowly pulsates when the ISP firmware is running. So, once I burn the 128kHz fuses into the Attiny13, the heartbeat on my Duemilanove stops!  Resetting Duemilanove or re-uploading the Slow SPI Arduino ISP sketch does nothing to restore its operation. This is really strange.

So, I'm looking for any input on the matter of de-bricking the ATtiny13s stuck on low clock frequency settings.

P.S. The Slow SPI Arduino ISP sketch follows:

Code: [Select]
// ArduinoISP version 04m3
// Copyright (c) 2008-2011 Randall Bohn
// If you require a license, see
//     http://www.opensource.org/licenses/bsd-license.php
//
// This sketch turns the Arduino into a AVRISP
// using the following arduino pins:
//
// pin name:    not-mega:         mega(1280 and 2560)
// slave reset: 10:               53
// MOSI:        11:               51
// MISO:        12:               50
// SCK:         13:               52
//
// Put an LED (with resistor) on the following pins:
// 9: Heartbeat   - shows the programmer is running
// 8: Error       - Lights up if something goes wrong (use red if that makes sense)
// 7: Programming - In communication with the slave
//
// 23 July 2011 Randall Bohn
// -Address Arduino issue 509 :: Portability of ArduinoISP
// http://code.google.com/p/arduino/issues/detail?id=509
//
// October 2010 by Randall Bohn
// - Write to EEPROM > 256 bytes
// - Better use of LEDs:
// -- Flash LED_PMODE on each flash commit
// -- Flash LED_PMODE while writing EEPROM (both give visual feedback of writing progress)
// - Light LED_ERR whenever we hit a STK_NOSYNC. Turn it off when back in sync.
// - Use pins_arduino.h (should also work on Arduino Mega)
//
// October 2009 by David A. Mellis
// - Added support for the read signature command
//
// February 2009 by Randall Bohn
// - Added support for writing to EEPROM (what took so long?)
// Windows users should consider WinAVR's avrdude instead of the
// avrdude included with Arduino software.
//
// January 2008 by Randall Bohn
// - Thanks to Amplificar for helping me with the STK500 protocol
// - The AVRISP/STK500 (mk I) protocol is used in the arduino bootloader
// - The SPI functions herein were developed for the AVR910_ARD programmer
// - More information at http://code.google.com/p/mega-isp

#include "pins_arduino.h"
#define RESET     SS

#define LED_HB    9
#define LED_ERR   8
#define LED_PMODE 7
#define PROG_FLICKER true

#define HWVER 2
#define SWMAJ 1
#define SWMIN 18


//#define BAUDRATE 19200
#define BAUDRATE 19200
//#define BAUDRATE 115200
// comment USE_SPI to use bitbang (digitalWrite())
//#define USE_SPI
// create clock on digital 9 using pwm (timer1), LED_HB must move
#define LADYADA_CLOCK

#ifdef LADYADA_CLOCK
// needs timer1 PWM
#define CLOCK_PIN 9
#undef LED_HB
#define LED_HB 6
#endif


// STK Definitions
#define STK_OK      0x10
#define STK_FAILED  0x11
#define STK_UNKNOWN 0x12
#define STK_INSYNC  0x14
#define STK_NOSYNC  0x15
#define CRC_EOP     0x20 //ok it is a space...



void pulse(int pin, int times);

void setup() {
  Serial.begin(19200);
  pinMode(LED_PMODE, OUTPUT);
  pulse(LED_PMODE, 2);
  pinMode(LED_ERR, OUTPUT);
  pulse(LED_ERR, 2);
  pinMode(LED_HB, OUTPUT);
  pulse(LED_HB, 2);
 
  #ifdef LADYADA_CLOCK
  // setup high freq PWM (timer 1)
  pinMode(CLOCK_PIN, OUTPUT);
  // 50% duty cycle -> 8 MHz
  // set up Timer 1
  TCCR1A = _BV (COM1A0);  // toggle OC1A on Compare Match
  TCCR1B = _BV(WGM12) | _BV(CS10);   // CTC, no prescaling
  OCR1A =  0;       // output every cycle
#endif
  pulse(LED_HB, 2);
 
 
 
}

int error=0;
int pmode=0;
// address for reading and writing, set by 'U' command
int here;
uint8_t buff[256]; // global block storage

#define beget16(addr) (*addr * 256 + *(addr+1) )
typedef struct param {
  uint8_t devicecode;
  uint8_t revision;
  uint8_t progtype;
  uint8_t parmode;
  uint8_t polling;
  uint8_t selftimed;
  uint8_t lockbytes;
  uint8_t fusebytes;
  int flashpoll;
  int eeprompoll;
  int pagesize;
  int eepromsize;
  int flashsize;
}
parameter;

parameter param;

// this provides a heartbeat on pin 9, so you can tell the software is running.
uint8_t hbval=128;
int8_t hbdelta=8;
void heartbeat() {
  if (hbval > 192) hbdelta = -hbdelta;
  if (hbval < 32) hbdelta = -hbdelta;
  hbval += hbdelta;
  analogWrite(LED_HB, hbval);
  delay(20);
}


void loop(void) {
  // is pmode active?
  if (pmode) digitalWrite(LED_PMODE, HIGH);
  else digitalWrite(LED_PMODE, LOW);
  // is there an error?
  if (error) digitalWrite(LED_ERR, HIGH);
  else digitalWrite(LED_ERR, LOW);

  // light the heartbeat LED
  heartbeat();
  if (Serial.available()) {
    avrisp();
  }
}

uint8_t getch() {
  while(!Serial.available());
  return Serial.read();
}
void fill(int n) {
  for (int x = 0; x < n; x++) {
    buff[x] = getch();
  }
}

#define PTIME 30
void pulse(int pin, int times) {
  do {
    digitalWrite(pin, HIGH);
    delay(PTIME);
    digitalWrite(pin, LOW);
    delay(PTIME);
  }
  while (times--);
}

void prog_lamp(int state) {
  if (PROG_FLICKER)
    digitalWrite(LED_PMODE, state);
}

void spi_init() {
  uint8_t x;
  SPCR = 0x53;
  x=SPSR;
  x=SPDR;
}

void spi_wait() {
  do {
  }
  while (!(SPSR & (1 << SPIF)));
}

uint8_t spi_send(uint8_t b) {
  uint8_t reply;
  SPDR=b;
  spi_wait();
  reply = SPDR;
  return reply;
}

uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
  uint8_t n;
  spi_send(a);
  n=spi_send(b);
  //if (n != a) error = -1;
  n=spi_send(c);
  return spi_send(d);
}

void empty_reply() {
  if (CRC_EOP == getch()) {
    Serial.print((char)STK_INSYNC);
    Serial.print((char)STK_OK);
  }
  else {
    error++;
    Serial.print((char)STK_NOSYNC);
  }
}

void breply(uint8_t b) {
  if (CRC_EOP == getch()) {
    Serial.print((char)STK_INSYNC);
    Serial.print((char)b);
    Serial.print((char)STK_OK);
  }
  else {
    error++;
    Serial.print((char)STK_NOSYNC);
  }
}

void get_version(uint8_t c) {
  switch(c) {
  case 0x80:
    breply(HWVER);
    break;
  case 0x81:
    breply(SWMAJ);
    break;
  case 0x82:
    breply(SWMIN);
    break;
  case 0x93:
    breply('S'); // serial programmer
    break;
  default:
    breply(0);
  }
}

void set_parameters() {
  // call this after reading paramter packet into buff[]
  param.devicecode = buff[0];
  param.revision   = buff[1];
  param.progtype   = buff[2];
  param.parmode    = buff[3];
  param.polling    = buff[4];
  param.selftimed  = buff[5];
  param.lockbytes  = buff[6];
  param.fusebytes  = buff[7];
  param.flashpoll  = buff[8];
  // ignore buff[9] (= buff[8])
  // following are 16 bits (big endian)
  param.eeprompoll = beget16(&buff[10]);
  param.pagesize   = beget16(&buff[12]);
  param.eepromsize = beget16(&buff[14]);

  // 32 bits flashsize (big endian)
  param.flashsize = buff[16] * 0x01000000
    + buff[17] * 0x00010000
    + buff[18] * 0x00000100
    + buff[19];

}

void start_pmode() {
  spi_init();
  // following delays may not work on all targets...
  pinMode(RESET, OUTPUT);
  digitalWrite(RESET, HIGH);
  pinMode(SCK, OUTPUT);
  digitalWrite(SCK, LOW);
  delay(50);
  digitalWrite(RESET, LOW);
  delay(50);
  pinMode(MISO, INPUT);
  pinMode(MOSI, OUTPUT);
  spi_transaction(0xAC, 0x53, 0x00, 0x00);
  pmode = 1;
}

void end_pmode() {
  pinMode(MISO, INPUT);
  pinMode(MOSI, INPUT);
  pinMode(SCK, INPUT);
  pinMode(RESET, INPUT);
  pmode = 0;
}

void universal() {
  int w;
  uint8_t ch;

  fill(4);
  ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]);
  breply(ch);
}

void flash(uint8_t hilo, int addr, uint8_t data) {
  spi_transaction(0x40+8*hilo,
  addr>>8 & 0xFF,
  addr & 0xFF,
  data);
}
void commit(int addr) {
  if (PROG_FLICKER) prog_lamp(LOW);
  spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0);
  if (PROG_FLICKER) {
    delay(PTIME);
    prog_lamp(HIGH);
  }
}

//#define _current_page(x) (here & 0xFFFFE0)
int current_page(int addr) {
  if (param.pagesize == 32)  return here & 0xFFFFFFF0;
  if (param.pagesize == 64)  return here & 0xFFFFFFE0;
  if (param.pagesize == 128) return here & 0xFFFFFFC0;
  if (param.pagesize == 256) return here & 0xFFFFFF80;
  return here;
}


void write_flash(int length) {
  fill(length);
  if (CRC_EOP == getch()) {
    Serial.print((char) STK_INSYNC);
    Serial.print((char) write_flash_pages(length));
  }
  else {
    error++;
    Serial.print((char) STK_NOSYNC);
  }
}

uint8_t write_flash_pages(int length) {
  int x = 0;
  int page = current_page(here);
  while (x < length) {
    if (page != current_page(here)) {
      commit(page);
      page = current_page(here);
    }
    flash(LOW, here, buff[x++]);
    flash(HIGH, here, buff[x++]);
    here++;
  }

  commit(page);

  return STK_OK;
}

#define EECHUNK (32)
uint8_t write_eeprom(int length) {
  // here is a word address, get the byte address
  int start = here * 2;
  int remaining = length;
  if (length > param.eepromsize) {
    error++;
    return STK_FAILED;
  }
  while (remaining > EECHUNK) {
    write_eeprom_chunk(start, EECHUNK);
    start += EECHUNK;
    remaining -= EECHUNK;
  }
  write_eeprom_chunk(start, remaining);
  return STK_OK;
}
// write (length) bytes, (start) is a byte address
uint8_t write_eeprom_chunk(int start, int length) {
  // this writes byte-by-byte,
  // page writing may be faster (4 bytes at a time)
  fill(length);
  prog_lamp(LOW);
  for (int x = 0; x < length; x++) {
    int addr = start+x;
    spi_transaction(0xC0, (addr>>8) & 0xFF, addr & 0xFF, buff[x]);
    delay(45);
  }
  prog_lamp(HIGH);
  return STK_OK;
}

void program_page() {
  char result = (char) STK_FAILED;
  int length = 256 * getch();
  length += getch();
  char memtype = getch();
  // flash memory @here, (length) bytes
  if (memtype == 'F') {
    write_flash(length);
    return;
  }
  if (memtype == 'E') {
    result = (char)write_eeprom(length);
    if (CRC_EOP == getch()) {
      Serial.print((char) STK_INSYNC);
      Serial.print(result);
    }
    else {
      error++;
      Serial.print((char) STK_NOSYNC);
    }
    return;
  }
  Serial.print((char)STK_FAILED);
  return;
}

uint8_t flash_read(uint8_t hilo, int addr) {
  return spi_transaction(0x20 + hilo * 8,
  (addr >> 8) & 0xFF,
  addr & 0xFF,
  0);
}

char flash_read_page(int length) {
  for (int x = 0; x < length; x+=2) {
    uint8_t low = flash_read(LOW, here);
    Serial.print((char) low);
    uint8_t high = flash_read(HIGH, here);
    Serial.print((char) high);
    here++;
  }
  return STK_OK;
}

char eeprom_read_page(int length) {
  // here again we have a word address
  int start = here * 2;
  for (int x = 0; x < length; x++) {
    int addr = start + x;
    uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF);
    Serial.print((char) ee);
  }
  return STK_OK;
}

void read_page() {
  char result = (char)STK_FAILED;
  int length = 256 * getch();
  length += getch();
  char memtype = getch();
  if (CRC_EOP != getch()) {
    error++;
    Serial.print((char) STK_NOSYNC);
    return;
  }
  Serial.print((char) STK_INSYNC);
  if (memtype == 'F') result = flash_read_page(length);
  if (memtype == 'E') result = eeprom_read_page(length);
  Serial.print(result);
  return;
}

void read_signature() {
  if (CRC_EOP != getch()) {
    error++;
    Serial.print((char) STK_NOSYNC);
    return;
  }
  Serial.print((char) STK_INSYNC);
  uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00);
  Serial.print((char) high);
  uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);
  Serial.print((char) middle);
  uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);
  Serial.print((char) low);
  Serial.print((char) STK_OK);
}
//////////////////////////////////////////
//////////////////////////////////////////


////////////////////////////////////
////////////////////////////////////
int avrisp() {
  uint8_t data, low, high;
  uint8_t ch = getch();
  switch (ch) {
  case '0': // signon
    error = 0;
    empty_reply();
    break;
  case '1':
    if (getch() == CRC_EOP) {
      Serial.print((char) STK_INSYNC);
      Serial.print("AVR ISP");
      Serial.print((char) STK_OK);
    }
    break;
  case 'A':
    get_version(getch());
    break;
  case 'B':
    fill(20);
    set_parameters();
    empty_reply();
    break;
  case 'E': // extended parameters - ignore for now
    fill(5);
    empty_reply();
    break;

  case 'P':
    start_pmode();
    empty_reply();
    break;
  case 'U': // set address (word)
    here = getch();
    here += 256 * getch();
    empty_reply();
    break;

  case 0x60: //STK_PROG_FLASH
    low = getch();
    high = getch();
    empty_reply();
    break;
  case 0x61: //STK_PROG_DATA
    data = getch();
    empty_reply();
    break;

  case 0x64: //STK_PROG_PAGE
    program_page();
    break;

  case 0x74: //STK_READ_PAGE 't'
    read_page();   
    break;

  case 'V': //0x56
    universal();
    break;
  case 'Q': //0x51
    error=0;
    end_pmode();
    empty_reply();
    break;

  case 0x75: //STK_READ_SIGN 'u'
    read_signature();
    break;

    // expecting a command, not CRC_EOP
    // this is how we can get back in sync
  case CRC_EOP:
    error++;
    Serial.print((char) STK_NOSYNC);
    break;

    // anything else we will return STK_UNKNOWN
  default:
    error++;
    if (CRC_EOP == getch())
      Serial.print((char)STK_UNKNOWN);
    else
      Serial.print((char)STK_NOSYNC);
  }
}




Electronics Forums


smeezekitty

  • Trusted Member
  • *
  • Posts: 14
  • Karma: +0/-0
  • I have a soldering iron and I'm not afraid to use it!
    • View Profile
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #1 on: December 04, 2012, 01:56:03 PM »
Having just lost connection to my Attiny13 after burning a fuse for 128kHz internal watchdog oscillator, I think this is an issue important enough that deserves its own thread here in the forums.
Try this
Code: [Select]
avrdude -c avrisp -P <arduino port> -p t13 -b 19200 -vAnd report the output.
Code: [Select]
This time I was using Arduino ISP programmer with the Arduino IDE and smeezekitty's [i]core13 [/i]core. But I've had this same issue with the AVRISP MKII programmer as well as a different ATtiny core, so the issue is not with this particular setup but a more general one - ATtiny13 starts operating so slow that reliable ISP operation becomes difficult (impossible?).
I do not know if the avr-isp MKII supports variable SPI clocks and I am too lazy to look.
The slow SPI arduino program should work ok and I have had no problem operating attiny13s at very low clock rates (not 128 / 8 though)
Quote
Any attempt to burn fuses this way makes ATtiny13 invisible to the programmer and no further programming or re-burning of different clock fuses is possible.
I needed to put a 0.56uf capacitor from RESET to Ground on the Arduino the make the ISP sketch work reliably.
Quote
I am using a "slow SPI Arduino ISP" sketch by smeezekitty, and the sketch itself also exhibits a strange behavior I haven't seen before: Arduino ISP has this nice "heartbeat" LED indicator that slowly pulsates when the ISP firmware is running. So, once I burn the 128kHz fuses into the Attiny13, the heartbeat on my Duemilanove stops!  Resetting Duemilanove or re-uploading the Slow SPI Arduino ISP sketch does nothing to restore its operation. This is really strange.
I never really tested the LED behavior in the moded sketch.


ElectroNick

  • The forum moderator
  • Administrator
  • Full Member
  • *****
  • Posts: 154
  • Karma: +3/-0
  • The soldering iron is ON!
    • View Profile
    • Electronics Blog
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #2 on: December 04, 2012, 02:15:26 PM »
Try this
Code: [Select]
avrdude -c avrisp -P <arduino port> -p t13 -b 19200 -vAnd report the output.
Here is what I'm getting so far:

Code: [Select]
avrdude -c avrisp -P /dev/ttyUSB0 -p t13 -b 19200 -v

avrdude: Version 5.11.1, compiled on Oct 30 2011 at 10:37:28
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2009 Joerg Wunsch

         System wide configuration file is "/etc/avrdude.conf"
         User configuration file is "/home/dima/.avrduderc"
         User configuration file does not exist or is not a regular file, skipping

         Using Port                    : /dev/ttyUSB0
         Using Programmer              : avrisp
         Overriding Baud Rate          : 19200
avrdude: stk500_getsync(): not in sync: resp=0x15

avrdude done.  Thank you.

Just for kicks, here is how I connected via AVRISP MKII last time (250 baud) , thought still no go on Arduino ISP. 

Code: [Select]
avrdude -c avrisp -P /dev/ttyUSB0 -p t13 -b 250 -v

avrdude: Version 5.11.1, compiled on Oct 30 2011 at 10:37:28
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2009 Joerg Wunsch

         System wide configuration file is "/etc/avrdude.conf"
         User configuration file is "/home/dima/.avrduderc"
         User configuration file does not exist or is not a regular file, skipping

         Using Port                    : /dev/ttyUSB0
         Using Programmer              : avrisp
         Overriding Baud Rate          : 250
avrdude: serial_baud_lookup(): Using non-standard baud rate: 250avrdude: stk500_getsync(): not in sync: resp=0xf0

avrdude done.  Thank you.


Note a different response, interesting... Although to be honest, I don't think Arduino ISP understands different serial speeds than 19200 - there does not seem to be any provision for that. So, this different response is probably just a serial comm error between the PC and the Arduino.

I'll dust off my AVRISP MKII programmer in a bit, rewire the Attiny13  and check again with it.

smeezekitty

  • Trusted Member
  • *
  • Posts: 14
  • Karma: +0/-0
  • I have a soldering iron and I'm not afraid to use it!
    • View Profile
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #3 on: December 04, 2012, 02:51:49 PM »
Try this
Code: [Select]
avrdude -c avrisp -P <arduino port> -p t13 -b 19200 -vAnd report the output.
Here is what I'm getting so far:

Code: [Select]
avrdude -c avrisp -P /dev/ttyUSB0 -p t13 -b 19200 -v

avrdude: Version 5.11.1, compiled on Oct 30 2011 at 10:37:28
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2009 Joerg Wunsch

         System wide configuration file is "/etc/avrdude.conf"
         User configuration file is "/home/dima/.avrduderc"
         User configuration file does not exist or is not a regular file, skipping

         Using Port                    : /dev/ttyUSB0
         Using Programmer              : avrisp
         Overriding Baud Rate          : 19200
avrdude: stk500_getsync(): not in sync: resp=0x15

avrdude done.  Thank you.
Make sure the wiring is OK. Also make sure you have installed the reset cap on the Arduino.
You can try adjusting the software SPI delays.
Quote
Just for kicks, here is how I connected via AVRISP MKII last time (250 baud) , thought still no go on Arduino ISP. 

Code: [Select]
avrdude -c avrisp -P /dev/ttyUSB0 -p t13 -b 250 -v

avrdude: Version 5.11.1, compiled on Oct 30 2011 at 10:37:28
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2009 Joerg Wunsch

         System wide configuration file is "/etc/avrdude.conf"
         User configuration file is "/home/dima/.avrduderc"
         User configuration file does not exist or is not a regular file, skipping

         Using Port                    : /dev/ttyUSB0
         Using Programmer              : avrisp
         Overriding Baud Rate          : 250
avrdude: serial_baud_lookup(): Using non-standard baud rate: 250avrdude: stk500_getsync(): not in sync: resp=0xf0

avrdude done.  Thank you.


Note a different response, interesting... Although to be honest, I don't think Arduino ISP understands different serial speeds than 19200 - there does not seem to be any provision for that. So, this different response is probably just a serial comm error between the PC and the Arduino.
The ArduinoISP is default to 19200 only.

ElectroNick

  • The forum moderator
  • Administrator
  • Full Member
  • *****
  • Posts: 154
  • Karma: +3/-0
  • The soldering iron is ON!
    • View Profile
    • Electronics Blog
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #4 on: December 04, 2012, 05:48:50 PM »
Make sure the wiring is OK. Also make sure you have installed the reset cap on the Arduino.
You can try adjusting the software SPI delays.

OK, I am now able to burn fuse for 128kHz (no divider) and upload sketches fine. All with Arduino ISP, did not even break out my AVRISP MKII yet. Here is what I had to do:

  • Add external 9V to the Arduino board (I was still concerned about the longish USB cables which I could not avoid using
  • Remove the reset capacitor from Arduino (I had 1µF there)
  • Upload the Slow SPI Arduino ISP sketch again

After I uploaded it, the heartbeat LED started pulsing and I can now burn any fuses from 128kHz (no divider) to 9.6MHz (no divider).

I am a little  apprehensive of trying to burn the fuse for the lowest possible frequency internal clock @ 128kHz with divider 8 (16kHz) -  no Attiny13 core I know of supports this frequency anyway - but I did recover from that setting yesterday once. It took me several times to run the "Burn Bootloader" as I described in this other post  but it worked - I just don't have time to do it now. If anyone finds themselves in this position and recover, please post here, I would be most interested to learn if this setup worked for the lowest speed.


I also wanted to mention that the ATtiny13 can be bricked for yet another reason that can still be recovered with ISP - the configuration fuses can be set for external clock (low fuse 0x78 for example) and then the ATtiny13 would really be dead, not just slow, if you don't actually have that external clock source.  This is not hopeless yet though because the host Arduino (the ISP programmer) can generate a clock signal for the Attiny13 and use it to start it up, communicate with it and set the proper fuse.

In my troubleshooting yesterday I did come across an ArduinoISP sketch which provides the clock output in such situation. The clock output should be connected to the Attiny13 external clock input at pin 2. Note that this sketch has a different wiring than the "classic" or smeezekitty's "Slow SPI" ones - read the header of the sketch to connect the ATtiny13 and the status LEDs properly. It basically tells you to connect Arduino pin 9 to Attiny pin 2 and move the heartbeat LED to Arduino Pin 6 - the rest of the wiring is the same.

Code: [Select]
// ArduinoISP version 04m3
// Copyright (c) 2008-2011 Randall Bohn
// If you require a license, see
//     http://www.opensource.org/licenses/bsd-license.php
//
// This sketch turns the Arduino into a AVRISP
// using the following arduino pins:
//
// pin name:    not-mega:         mega(1280 and 2560)
// slave reset: 10:               53
// MOSI:        11:               51
// MISO:        12:               50
// SCK:         13:               52
//
// Put an LED (with resistor) on the following pins:
// 9: Heartbeat   - shows the programmer is running
// 8: Error       - Lights up if something goes wrong (use red if that makes sense)
// 7: Programming - In communication with the slave
//
// 23 July 2011 Randall Bohn
// -Address Arduino issue 509 :: Portability of ArduinoISP
// http://code.google.com/p/arduino/issues/detail?id=509
//
// October 2010 by Randall Bohn
// - Write to EEPROM > 256 bytes
// - Better use of LEDs:
// -- Flash LED_PMODE on each flash commit
// -- Flash LED_PMODE while writing EEPROM (both give visual feedback of writing progress)
// - Light LED_ERR whenever we hit a STK_NOSYNC. Turn it off when back in sync.
// - Use pins_arduino.h (should also work on Arduino Mega)
//
// October 2009 by David A. Mellis
// - Added support for the read signature command
//
// February 2009 by Randall Bohn
// - Added support for writing to EEPROM (what took so long?)
// Windows users should consider WinAVR's avrdude instead of the
// avrdude included with Arduino software.
//
// January 2008 by Randall Bohn
// - Thanks to Amplificar for helping me with the STK500 protocol
// - The AVRISP/STK500 (mk I) protocol is used in the arduino bootloader
// - The SPI functions herein were developed for the AVR910_ARD programmer
// - More information at http://code.google.com/p/mega-isp

#include "pins_arduino.h"
#define RESET     SS

#define LED_HB    9
#define LED_ERR   8
#define LED_PMODE 7
#define PROG_FLICKER true

#define HWVER 2
#define SWMAJ 1
#define SWMIN 18


//#define BAUDRATE 19200
#define BAUDRATE 19200
//#define BAUDRATE 115200
// comment USE_SPI to use bitbang (digitalWrite())
//#define USE_SPI
// create clock on digital 9 using pwm (timer1), LED_HB must move
#define LADYADA_CLOCK

#ifdef LADYADA_CLOCK
// needs timer1 PWM
#define CLOCK_PIN 9
#undef LED_HB
#define LED_HB 6
#endif


// STK Definitions
#define STK_OK      0x10
#define STK_FAILED  0x11
#define STK_UNKNOWN 0x12
#define STK_INSYNC  0x14
#define STK_NOSYNC  0x15
#define CRC_EOP     0x20 //ok it is a space...



void pulse(int pin, int times);

void setup() {
  Serial.begin(19200);
  pinMode(LED_PMODE, OUTPUT);
  pulse(LED_PMODE, 2);
  pinMode(LED_ERR, OUTPUT);
  pulse(LED_ERR, 2);
  pinMode(LED_HB, OUTPUT);
  pulse(LED_HB, 2);
 
  #ifdef LADYADA_CLOCK
  // setup high freq PWM (timer 1)
  pinMode(CLOCK_PIN, OUTPUT);
  // 50% duty cycle -> 8 MHz
  OCR1A = 0;
  ICR1 = 1;
  // OC1A output, fast PWM
  TCCR1A = _BV(WGM11) | _BV(COM1A1);
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // no clock prescale
#endif
  pulse(LED_HB, 2);
 
 
 
}

int error=0;
int pmode=0;
// address for reading and writing, set by 'U' command
int here;
uint8_t buff[256]; // global block storage

#define beget16(addr) (*addr * 256 + *(addr+1) )
typedef struct param {
  uint8_t devicecode;
  uint8_t revision;
  uint8_t progtype;
  uint8_t parmode;
  uint8_t polling;
  uint8_t selftimed;
  uint8_t lockbytes;
  uint8_t fusebytes;
  int flashpoll;
  int eeprompoll;
  int pagesize;
  int eepromsize;
  int flashsize;
}
parameter;

parameter param;

// this provides a heartbeat on pin 9, so you can tell the software is running.
uint8_t hbval=128;
int8_t hbdelta=8;
void heartbeat() {
  if (hbval > 192) hbdelta = -hbdelta;
  if (hbval < 32) hbdelta = -hbdelta;
  hbval += hbdelta;
  analogWrite(LED_HB, hbval);
  delay(20);
}


void loop(void) {
  // is pmode active?
  if (pmode) digitalWrite(LED_PMODE, HIGH);
  else digitalWrite(LED_PMODE, LOW);
  // is there an error?
  if (error) digitalWrite(LED_ERR, HIGH);
  else digitalWrite(LED_ERR, LOW);

  // light the heartbeat LED
  heartbeat();
  if (Serial.available()) {
    avrisp();
  }
}

uint8_t getch() {
  while(!Serial.available());
  return Serial.read();
}
void fill(int n) {
  for (int x = 0; x < n; x++) {
    buff[x] = getch();
  }
}

#define PTIME 30
void pulse(int pin, int times) {
  do {
    digitalWrite(pin, HIGH);
    delay(PTIME);
    digitalWrite(pin, LOW);
    delay(PTIME);
  }
  while (times--);
}

void prog_lamp(int state) {
  if (PROG_FLICKER)
    digitalWrite(LED_PMODE, state);
}

void spi_init() {
  uint8_t x;
  SPCR = 0x53;
  x=SPSR;
  x=SPDR;
}

void spi_wait() {
  do {
  }
  while (!(SPSR & (1 << SPIF)));
}

uint8_t spi_send(uint8_t b) {
  uint8_t reply;
  SPDR=b;
  spi_wait();
  reply = SPDR;
  return reply;
}

uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
  uint8_t n;
  spi_send(a);
  n=spi_send(b);
  //if (n != a) error = -1;
  n=spi_send(c);
  return spi_send(d);
}

void empty_reply() {
  if (CRC_EOP == getch()) {
    Serial.print((char)STK_INSYNC);
    Serial.print((char)STK_OK);
  }
  else {
    error++;
    Serial.print((char)STK_NOSYNC);
  }
}

void breply(uint8_t b) {
  if (CRC_EOP == getch()) {
    Serial.print((char)STK_INSYNC);
    Serial.print((char)b);
    Serial.print((char)STK_OK);
  }
  else {
    error++;
    Serial.print((char)STK_NOSYNC);
  }
}

void get_version(uint8_t c) {
  switch(c) {
  case 0x80:
    breply(HWVER);
    break;
  case 0x81:
    breply(SWMAJ);
    break;
  case 0x82:
    breply(SWMIN);
    break;
  case 0x93:
    breply('S'); // serial programmer
    break;
  default:
    breply(0);
  }
}

void set_parameters() {
  // call this after reading paramter packet into buff[]
  param.devicecode = buff[0];
  param.revision   = buff[1];
  param.progtype   = buff[2];
  param.parmode    = buff[3];
  param.polling    = buff[4];
  param.selftimed  = buff[5];
  param.lockbytes  = buff[6];
  param.fusebytes  = buff[7];
  param.flashpoll  = buff[8];
  // ignore buff[9] (= buff[8])
  // following are 16 bits (big endian)
  param.eeprompoll = beget16(&buff[10]);
  param.pagesize   = beget16(&buff[12]);
  param.eepromsize = beget16(&buff[14]);

  // 32 bits flashsize (big endian)
  param.flashsize = buff[16] * 0x01000000
    + buff[17] * 0x00010000
    + buff[18] * 0x00000100
    + buff[19];

}

void start_pmode() {
  spi_init();
  // following delays may not work on all targets...
  pinMode(RESET, OUTPUT);
  digitalWrite(RESET, HIGH);
  pinMode(SCK, OUTPUT);
  digitalWrite(SCK, LOW);
  delay(50);
  digitalWrite(RESET, LOW);
  delay(50);
  pinMode(MISO, INPUT);
  pinMode(MOSI, OUTPUT);
  spi_transaction(0xAC, 0x53, 0x00, 0x00);
  pmode = 1;
}

void end_pmode() {
  pinMode(MISO, INPUT);
  pinMode(MOSI, INPUT);
  pinMode(SCK, INPUT);
  pinMode(RESET, INPUT);
  pmode = 0;
}

void universal() {
  int w;
  uint8_t ch;

  fill(4);
  ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]);
  breply(ch);
}

void flash(uint8_t hilo, int addr, uint8_t data) {
  spi_transaction(0x40+8*hilo,
  addr>>8 & 0xFF,
  addr & 0xFF,
  data);
}
void commit(int addr) {
  if (PROG_FLICKER) prog_lamp(LOW);
  spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0);
  if (PROG_FLICKER) {
    delay(PTIME);
    prog_lamp(HIGH);
  }
}

//#define _current_page(x) (here & 0xFFFFE0)
int current_page(int addr) {
  if (param.pagesize == 32)  return here & 0xFFFFFFF0;
  if (param.pagesize == 64)  return here & 0xFFFFFFE0;
  if (param.pagesize == 128) return here & 0xFFFFFFC0;
  if (param.pagesize == 256) return here & 0xFFFFFF80;
  return here;
}


void write_flash(int length) {
  fill(length);
  if (CRC_EOP == getch()) {
    Serial.print((char) STK_INSYNC);
    Serial.print((char) write_flash_pages(length));
  }
  else {
    error++;
    Serial.print((char) STK_NOSYNC);
  }
}

uint8_t write_flash_pages(int length) {
  int x = 0;
  int page = current_page(here);
  while (x < length) {
    if (page != current_page(here)) {
      commit(page);
      page = current_page(here);
    }
    flash(LOW, here, buff[x++]);
    flash(HIGH, here, buff[x++]);
    here++;
  }

  commit(page);

  return STK_OK;
}

#define EECHUNK (32)
uint8_t write_eeprom(int length) {
  // here is a word address, get the byte address
  int start = here * 2;
  int remaining = length;
  if (length > param.eepromsize) {
    error++;
    return STK_FAILED;
  }
  while (remaining > EECHUNK) {
    write_eeprom_chunk(start, EECHUNK);
    start += EECHUNK;
    remaining -= EECHUNK;
  }
  write_eeprom_chunk(start, remaining);
  return STK_OK;
}
// write (length) bytes, (start) is a byte address
uint8_t write_eeprom_chunk(int start, int length) {
  // this writes byte-by-byte,
  // page writing may be faster (4 bytes at a time)
  fill(length);
  prog_lamp(LOW);
  for (int x = 0; x < length; x++) {
    int addr = start+x;
    spi_transaction(0xC0, (addr>>8) & 0xFF, addr & 0xFF, buff[x]);
    delay(45);
  }
  prog_lamp(HIGH);
  return STK_OK;
}

void program_page() {
  char result = (char) STK_FAILED;
  int length = 256 * getch();
  length += getch();
  char memtype = getch();
  // flash memory @here, (length) bytes
  if (memtype == 'F') {
    write_flash(length);
    return;
  }
  if (memtype == 'E') {
    result = (char)write_eeprom(length);
    if (CRC_EOP == getch()) {
      Serial.print((char) STK_INSYNC);
      Serial.print(result);
    }
    else {
      error++;
      Serial.print((char) STK_NOSYNC);
    }
    return;
  }
  Serial.print((char)STK_FAILED);
  return;
}

uint8_t flash_read(uint8_t hilo, int addr) {
  return spi_transaction(0x20 + hilo * 8,
  (addr >> 8) & 0xFF,
  addr & 0xFF,
  0);
}

char flash_read_page(int length) {
  for (int x = 0; x < length; x+=2) {
    uint8_t low = flash_read(LOW, here);
    Serial.print((char) low);
    uint8_t high = flash_read(HIGH, here);
    Serial.print((char) high);
    here++;
  }
  return STK_OK;
}

char eeprom_read_page(int length) {
  // here again we have a word address
  int start = here * 2;
  for (int x = 0; x < length; x++) {
    int addr = start + x;
    uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF);
    Serial.print((char) ee);
  }
  return STK_OK;
}

void read_page() {
  char result = (char)STK_FAILED;
  int length = 256 * getch();
  length += getch();
  char memtype = getch();
  if (CRC_EOP != getch()) {
    error++;
    Serial.print((char) STK_NOSYNC);
    return;
  }
  Serial.print((char) STK_INSYNC);
  if (memtype == 'F') result = flash_read_page(length);
  if (memtype == 'E') result = eeprom_read_page(length);
  Serial.print(result);
  return;
}

void read_signature() {
  if (CRC_EOP != getch()) {
    error++;
    Serial.print((char) STK_NOSYNC);
    return;
  }
  Serial.print((char) STK_INSYNC);
  uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00);
  Serial.print((char) high);
  uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);
  Serial.print((char) middle);
  uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);
  Serial.print((char) low);
  Serial.print((char) STK_OK);
}
//////////////////////////////////////////
//////////////////////////////////////////


////////////////////////////////////
////////////////////////////////////
int avrisp() {
  uint8_t data, low, high;
  uint8_t ch = getch();
  switch (ch) {
  case '0': // signon
    error = 0;
    empty_reply();
    break;
  case '1':
    if (getch() == CRC_EOP) {
      Serial.print((char) STK_INSYNC);
      Serial.print("AVR ISP");
      Serial.print((char) STK_OK);
    }
    break;
  case 'A':
    get_version(getch());
    break;
  case 'B':
    fill(20);
    set_parameters();
    empty_reply();
    break;
  case 'E': // extended parameters - ignore for now
    fill(5);
    empty_reply();
    break;

  case 'P':
    start_pmode();
    empty_reply();
    break;
  case 'U': // set address (word)
    here = getch();
    here += 256 * getch();
    empty_reply();
    break;

  case 0x60: //STK_PROG_FLASH
    low = getch();
    high = getch();
    empty_reply();
    break;
  case 0x61: //STK_PROG_DATA
    data = getch();
    empty_reply();
    break;

  case 0x64: //STK_PROG_PAGE
    program_page();
    break;

  case 0x74: //STK_READ_PAGE 't'
    read_page();   
    break;

  case 'V': //0x56
    universal();
    break;
  case 'Q': //0x51
    error=0;
    end_pmode();
    empty_reply();
    break;

  case 0x75: //STK_READ_SIGN 'u'
    read_signature();
    break;

    // expecting a command, not CRC_EOP
    // this is how we can get back in sync
  case CRC_EOP:
    error++;
    Serial.print((char) STK_NOSYNC);
    break;

    // anything else we will return STK_UNKNOWN
  default:
    error++;
    if (CRC_EOP == getch())
      Serial.print((char)STK_UNKNOWN);
    else
      Serial.print((char)STK_NOSYNC);
  }
}


Unfortunately, I could not figure out who to credit properly for this "recovery clock" ArduinoISP version. If you know, please drop me a message, I'll add credits here.

smeezekitty

  • Trusted Member
  • *
  • Posts: 14
  • Karma: +0/-0
  • I have a soldering iron and I'm not afraid to use it!
    • View Profile
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #5 on: December 12, 2012, 11:19:53 PM »
I think the slow ISP sketch you have on there is the wrong version

ElectroNick

  • The forum moderator
  • Administrator
  • Full Member
  • *****
  • Posts: 154
  • Karma: +3/-0
  • The soldering iron is ON!
    • View Profile
    • Electronics Blog
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #6 on: December 12, 2012, 11:30:00 PM »
I think the slow ISP sketch you have on there is the wrong version
Are you talking about the Arduino ISP sketch in my last post? That one is not the "Slow SPI" you gave me, that one is a version of Arduino ISP that creates its own clock signal to clock the Attiny that was accidentally set to expect an external clock and therefore completely dead to the world, no matter how slow you're talking to it. You could of course put some oscillator on it but in case you don't have one, there's an app for that, too - the Arduino ISP "Recovery Clock" version.
Cheers!

smeezekitty

  • Trusted Member
  • *
  • Posts: 14
  • Karma: +0/-0
  • I have a soldering iron and I'm not afraid to use it!
    • View Profile
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #7 on: December 13, 2012, 12:01:42 AM »
I think the slow ISP sketch you have on there is the wrong version
Are you talking about the Arduino ISP sketch in my last post? That one is not the "Slow SPI" you gave me, that one is a version of Arduino ISP that creates its own clock signal to clock the Attiny that was accidentally set to expect an external clock and therefore completely dead to the world, no matter how slow you're talking to it. You could of course put some oscillator on it but in case you don't have one, there's an app for that, too - the Arduino ISP "Recovery Clock" version.
Cheers!
I mean the one in the first post.
I glanced over it and did not see the code for the software ISP.

ElectroNick

  • The forum moderator
  • Administrator
  • Full Member
  • *****
  • Posts: 154
  • Karma: +3/-0
  • The soldering iron is ON!
    • View Profile
    • Electronics Blog
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #8 on: December 13, 2012, 12:17:49 AM »
I mean the one in the first post.
I glanced over it and did not see the code for the software ISP.
It's got
Code: [Select]
void spi_init() {
  uint8_t x;
  SPCR = 0x53;
  x=SPSR;
  x=SPDR;
}

void spi_wait() {
  do {
  }
  while (!(SPSR & (1 << SPIF)));
}

uint8_t spi_send(uint8_t b) {
  uint8_t reply;
  SPDR=b;
  spi_wait();
  reply = SPDR;
  return reply;
}

uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
  uint8_t n;
  spi_send(a);
  n=spi_send(b);
  //if (n != a) error = -1;
  n=spi_send(c);
  return spi_send(d);
}
- still not that?
What's the current one tho? Can you post it here as code? Or zip attachment?
Cheers!

smeezekitty

  • Trusted Member
  • *
  • Posts: 14
  • Karma: +0/-0
  • I have a soldering iron and I'm not afraid to use it!
    • View Profile
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #9 on: December 13, 2012, 03:56:17 AM »
I mean the one in the first post.
I glanced over it and did not see the code for the software ISP.
It's got
Code: [Select]
void spi_init() {
  uint8_t x;
  SPCR = 0x53;
  x=SPSR;
  x=SPDR;
}

void spi_wait() {
  do {
  }
  while (!(SPSR & (1 << SPIF)));
}

uint8_t spi_send(uint8_t b) {
  uint8_t reply;
  SPDR=b;
  spi_wait();
  reply = SPDR;
  return reply;
}

uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
  uint8_t n;
  spi_send(a);
  n=spi_send(b);
  //if (n != a) error = -1;
  n=spi_send(c);
  return spi_send(d);
}
- still not that?
What's the current one tho? Can you post it here as code? Or zip attachment?
Cheers!
I think it is still wrong.
Correct one here: http://pastebin.com/Aw5BD0zy
BTW What is your location? It is going on 1AM here on the west coast of the USA.

ElectroNick

  • The forum moderator
  • Administrator
  • Full Member
  • *****
  • Posts: 154
  • Karma: +3/-0
  • The soldering iron is ON!
    • View Profile
    • Electronics Blog
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #10 on: December 13, 2012, 12:48:34 PM »
I think it is still wrong.
Correct one here: http://pastebin.com/Aw5BD0zy
BTW What is your location? It is going on 1AM here on the west coast of the USA.
Hmm... That's the exact pastebin address I got it from. I have to double check though. Do you mind if I post the code here. It may be easier to look at.

I am in Pennsylvania but I'm online at any random time of the day, including sometimes at 1AM PST  ;D but only when I can't figure out when my Attiny13 chip ain't workin'  ;)

smeezekitty

  • Trusted Member
  • *
  • Posts: 14
  • Karma: +0/-0
  • I have a soldering iron and I'm not afraid to use it!
    • View Profile
Re: Un-bricking Attiny13 stuck on 128kHz internal watchdog oscillator
« Reply #11 on: December 13, 2012, 02:02:30 PM »
I think it is still wrong.
Correct one here: http://pastebin.com/Aw5BD0zy
BTW What is your location? It is going on 1AM here on the west coast of the USA.
Hmm... That's the exact pastebin address I got it from. I have to double check though. Do you mind if I post the code here. It may be easier to look at.
I don't mind. You can pretty much do whatever you want with it.
Quote
I am in Pennsylvania but I'm online at any random time of the day, including sometimes at 1AM PST  ;D but only when I can't figure out when my Attiny13 chip ain't workin'  ;)
PA? Wow I thought it was late over here. 1 AM is my bed time and I very seldom stay up much past that. I thought maybe you were in Europe or something like that.

 

Related Topics

  Subject / Started by Replies Last post
22 Replies
25413 Views
Last post December 04, 2012, 11:37:03 PM
by ElectroNick