SPI clock frequency disaster

I use the ArduinoISP example sketch to turn my Arduino Uno into an avrisp-compatible programmer, which I use to program other AVR microcontrollers with avrdude/avr-libc/etc. It works great!

Lately I've been working on a project that involves using ArduinoISP to program an ATtiny85. Like I said, it works great. Until I decided to set the ATtiny85's clock prescaler to 256. That is, I uploaded some code which, during its initialization, does this:

CLKPR = _BV(CLKPCE);
CLKPR = _BV(CLKPS3); // divide clock by 256

The fuses are configured to use the ATtiny85's internal clock source, which runs at 8Mhz, so with a 256 prescaler, the effective CPU frequency becomes 8Mhz/256 = 31.25kHz.

This is all well and good, and works as expected. The ATtiny85 runs very slow, and thereby conserves power. However, it is now too slow for me to program it! Avrdude can't even read the device signature anymore. I did not anticipate this :cold_sweat:

After searching around online, I found that it is apparently recommended not to set the SPI master's SPI clock to much more than 1/4 the clock of the SPI slave. In this case, the Arduino Uno, running ArduinoISP, is the master. My worry is that now its clock is much too fast compared to the clock of my ATtiny85, and that's why I can no longer program it.

Is this the case? I am kind of confused here. The 256 clock prescaler is only set when the ATtiny85 runs its user code... it's not set by fuses or anything (it couldn't be! fuses only let you set a prescaler of 1 or 8 ). I would have thought that when the programmer (ArduinoISP) brings the ATtiny85 into reset to program it, the ATtiny's clock would revert back to the full un-prescaled frequency, since it would not yet have executed the above CLKPR code. Or does the clock prescaler register carry over into the reset state?

In any case, if my problem really is that the ATtiny is now much too slow compared to the programmer, can anybody recommend a way to fix it? My guess is that I would have to make my Uno run equally slowly -- by setting its own CLKPR, and perhaps also by setting the SPR0 and SPR1 bits of its SPCR register... changing the Uno's CLKPR will of course bork all of the delay() calls (I guess I can dig into the Arduino headers and change F_CPU to compensate), and I'm kind of scared that making my Uno too slow will make it unprogrammable by its own bootloader -- does anybody know if this is the case?

Anyway, thanks for any suggestions!

thenendo:
I would have thought that when the programmer (ArduinoISP) brings the ATtiny85 into reset to program it, the ATtiny's clock would revert back to the full un-prescaled frequency, since it would not yet have executed the above CLKPR code. Or does the clock prescaler register carry over into the reset state?

Agree with your thinking. Am not aware that the CLKPR register is preserved through a reset. I've played just a little bit with changing CLKPR on the fly, have not taken it all the way to 256, but that shouldn't matter.

I just found a thread on AVR freaks, where someone had exactly the same problem:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=75867

That thread seems to imply that CLKPR does persist through reset. His solution was to use the -B option of avrdude, which is supposed to slow down the programmer. I tried it (with powers of two up to 32768), but at least with ArduinoISP, it doesn't help. Perhaps ArduinoISP is too simplistic of a programmer to handle that option. This is too bad, since I don't (yet) have a "real" programmer.

Then get one.

USBtiny supports the '-B' option and is very cheap.

I found the sketch where I was experimenting with CLKPR, I'll fiddle with it a bit and see what I can find. It was for the 328P, but I also happened to be fooling with an tiny85 last night, so should be able to check that too.

I'm also a fan of the USBtiny, has worked great for me, with the Arduino IDE and WinAVR, and with 328Ps, tiny85s and tiny2313s.

Yeah, I'll probably get a real programmer like USBTinyISP. However, since everything here is open source (USBTinyISP, ArduinoISP, avrdude), it shouldn't be too hard to modify ArduinoISP to support more of the STK protocol, including the -B "bitclock" option. Yet another project :slight_smile:

In any case, let this thread serve as a warning to others :stuck_out_tongue:

I just tested the sketch below with a m328P, and it would seem to indicate that CLKPR does not persist through reset. Results with ATtiny85 to follow, although I'll be even more surprised if it behaves differently. I also did look through the ATtiny25/45/85 datasheet and didn't see any mention of CLKPR persisting, I'd think that would deserve a mention.

//Sketch to test whether CLKPR register value persists through a reset.
//If D8 pin is grounded when the MCU is reset, clock will be set to 4MHz,
//else will be the standard 16MHz.

void setup(void) {
    pinMode(8, INPUT);      //switch on D8, push to lower clock frequency
    digitalWrite(8, HIGH);  //enable pullup resistor
    pinMode(13, OUTPUT);
    if (digitalRead(8) == LOW) {
       changePrescaler();    //change the clock to 4MHz
    }
}

void loop(void) {
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
    delay(1000);
}

void changePrescaler(void) {
    cli();
    CLKPR = 1 << CLKPCE;
    CLKPR = 2;
    sei();
}

Cool! How did you test what the clock freq was during reset?

Just by observing how fast the LED blinks. Results are the same with the ATtiny85, sketch below. I used the Arduino IDE with the Arduino-Tiny core, and a USBtinyISP programmer. So I might check to see if there is something else that might be going on.

//Sketch to test whether CLKPR register value persists through a reset
//on an ATtiny85. Fuses are set to factory default 62/DF/FF (L/H/E), which
//runs the clock at 8Mhz with the prescaler set to divide by 8.
//If PB2 pin is grounded when the MCU is reset, the prescaler is changed
//to divide by 32, so the clock frequency will be reduced by a factor of 4.

#define BUTTON 2            //switch on PB2
#define LED 0               //LED on PB0

void setup(void) {
    pinMode(BUTTON, INPUT);
    digitalWrite(BUTTON, HIGH);  //enable pullup resistor
    pinMode(LED, OUTPUT);
    if (digitalRead(BUTTON) == LOW) {
       changePrescaler();        //lower the clock frequency
    }
}

void loop(void) {
    digitalWrite(LED, HIGH);
    delay(1000);
    digitalWrite(LED, LOW);
    delay(1000);
}

void changePrescaler(void) {
    cli();
    CLKPR = 1 << CLKPCE;
    CLKPR = 5;
    sei();
}

Wait a minute. You can't blink the LED during reset. Of course the CLKPR register gets reset during bootup (that's what the div8/div1 fuse does). The question is: what is the clock rate during reset, i.e., while the reset pin is held low -- this is the state in which the chip is programmed. My problem seems to imply that whatever the clock rate was immediately before reset carries over into the reset state, and is the rate at which the device responds to ISP transmissions; then once the reset state ends (reset pin goes high), the device boots up with initial CLKPR determined by div8 fuse, and any subsequent user code might further adjust CLKPR. (Mine immediately sets it to divide by 256, which is the problem, since it carries over into the next reset when you're trying to program the thing.)

If one could time things to start programming in the very brief window between bootup and the CLKPR instruction (when the device is still running at the initial div8 rate), one might be able to program it. I'll have to try this when I get home...

Oh, I misunderstood (or misread)! Thought you meant after reset. Well certainly I've convinced myself that CLKPR is reset after MCU reset. But along the way I think I duplicated the situation you observed, and I think the answer to that is that the clock continues to run at whatever rate it was running at prior to the reset.

Thinking there might possibly be some clock initialization going on in the Arduino core (long shot I know, and it turns out not) I moved to WinAVR, program at bottom. Here's what I did:

  1. After initial load of the program, LED blinks on and off every second.
  2. Hold button on PB3 down, then reset, and the LED blinks on and off every 4 seconds, indicating slower clock frequency.
  3. If I do "avrdude -p t85 -v", it fails as follows.

Programmer Type : USBtiny
Description : USBtiny simple USB programmer, USBtinyISP - Inexpensive USB AVR Programmer
avrdude: programmer operation not supported

avrdude: Using SCK period of 10 usec
avrdude: initialization failed, rc=-1
Double check connections and try again, or use -F to override
this check.

However, step 3 above also resets the MCU, so if I reissue the same avrdude command a second time, it works!

Starting the procedure again from the top, if I change step 3 to "avrdude -p t85 -v -B 40", then it works the first time, but still resets the MCU and the clock frequency is back to 1MHz.

Not ever having used ArduinoISP, I don't know whether it supports the -B option. But certainly removing power should reset CLKPR as well, so I still wonder if something else is going on. Hope this helps. Interesting stuff!

//WinAVR program to test whether CLKPR register value persists through a reset
//on an ATtiny85. Fuses are set to factory default 62/DF/FF (L/H/E), which
//runs the clock at 8MHz with the prescaler set to divide by 8.
//If PB3 pin is grounded when the MCU is reset, the prescaler is changed
//to divide by 32, so the clock frequency will be reduced by a factor of 4.

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define msWait 1000
#define LED PORTB0
#define BUTTON PORTB3

int main(void) {
	DDRB |= _BV(LED);					//make the LED pin an output
	PORTB |= _BV(BUTTON);				//turn on the pullup for the button
	if ((PINB & _BV(BUTTON)) == 0) {  	//slow down the clock
		cli();
	 	CLKPR = _BV(CLKPCE);
	 	CLKPR = 5;
	 	sei();
	}
	 
	while(1) {
		PINB |= _BV(LED);				//toggle the LED
		_delay_ms(msWait);
	}
	return 0;
}

Presumably, the target's clock divisor is set based on the fuse settings when RESET is released. Try pulsing RESET...

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);

// May need a small delay here
digitalWrite(RESET, HIGH);
// May need a small delay here or may need to use direct I/O to speed this up
digitalWrite(RESET, LOW);

delay(50);
pinMode(MISO, INPUT);
pinMode(MOSI, OUTPUT);
spi_transaction(0xAC, 0x53, 0x00, 0x00);
pmode = 1;
}

I have been unwise to put this

CLKPR = (1<<CLKPCE);
 CLKPR = B00001000

first in the setup part of code to see how it affects power consumption. The effect is the microcontroller becomes 256 times slower and could not be programmed anymore. The solution is to use the -i switch on Avrdude. This slows down the programmer and programming is now possible

This did the trick for me using a parallel programmer:

avrdude -c dapa -p m328p -i 40 -e
avrdude -c dapa -p m328p -i 40 -U flash:w:blink.hex