PlatformIO Community

ATTiny 8MHz wrong timing in delay(), FastLED and NeoPixel

Hi,
I have a bare ATTiny85 and I want to control some 800KHz SK6812s. My problem is that both, NeoPixel and FastLED do have wrong timing (400KHz). Also delay(d) takes twice as long as it should. The ATTiny is running at 8MHz - it still has the default fuses:

avrdude: safemode: Fuses OK (E:FF, H:DF, L:62)

I tried everything to set the Frequency to 8MHz during compiling. This is my platformio.ini:

[env:attiny85]
platform = atmelavr
board = attiny85
upload_protocol = avrisp ; Arduino as ISP
upload_flags = 
    -PCOM8 
    -b19200
framework = arduino
build_flags =
    -DF_CPU=8000000L
board_build.mcu = attiny85
board_build.f_cpu = 8000000L
lib_deps = adafruit/Adafruit NeoPixel @ 1.10.0

This is my main.cpp:

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>

const uint8_t LED_PIN = PB0;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(3, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();
  strip.setBrightness(50);
  strip.show(); // Initialize all pixels to 'off'
}

void loop() {
  for(uint8_t i=0; i<3; i++) strip.setPixelColor(i, strip.Color(50, 0, 0)); strip.show(); delay(12); 
  for(uint8_t i=0; i<3; i++) strip.setPixelColor(i, strip.Color(0, 50, 0)); strip.show(); delay(12); 
  for(uint8_t i=0; i<3; i++) strip.setPixelColor(i, strip.Color(0, 0, 50)); strip.show(); delay(12); 
  for(uint8_t i=0; i<3; i++) strip.setPixelColor(i, strip.Color(50, 50, 50)); strip.show(); delay(12); 
  for(uint8_t i=0; i<3; i++) strip.setPixelColor(i, strip.Color(0, 0, 0)); strip.show(); delay(12); 
}

The compiler actually knows the frequency, because when I set it to 4000000L FastLED and NeoPixel complain during compiling that the CPU frequency is not supported.

However, with 8MHz, the timing is wrong…
What am I doing wrong?

Are you sure it takes twice as long? With the default fuse settings you have:

  • Internal RC oscillator at 8MHz
  • Divide (clock) by 8 enabled.

You are running at 1 MHz, not 8 MHz.

If you run the standard “blink” sketch on your attiny85, what do you see? I suspect you get an 8 second delay when you ask for 1.

There’s an excellent fuse calculator at AVR® Fuse Calculator – The Engbedded Blog and when you’ve chosen the options you want, it even shows you the avrdude commands to set them. With PlatformIO, you won’t need to do that manually, just set the fuses in the platformio.ini and pio run -t fuses.

So, to turn off the divide by 8 fuse bit, you need to set your LOW fuse to 0xE2 and that will get you an 8MHz clock. Add the following to your platformio,ini file:

board_fuses.lfuse = 0xE2
board_fuses.hfuse = 0xDF
board_fuses.efuse = 0xFF

Then program the fuses, with an ICSP device, using either:

  • pio run -t fuses

or, in VSCode:

  • Click the ant/alien head icon
  • Click to open Project Tasks on the left side
  • Click to open attiny85 (your environment name)
  • Click to open Platform
  • Choose “set fuses”

Now try the blink sketch again. It should delay for 1 second. If so, you can go back to your original projecy.

HTH

Cheers,
Norm.

Thank you,
I completely forgot about the clock prescaler.
I used

  cli(); // Disable interrupts
  CLKPR = (1<<CLKPCE); // Prescaler enable
  CLKPR = 0x00; // Clock division factor
  sei(); // Enable interrupts

to change the prescaler and now everything works as expected.
However, yes, I’m sure everything was just running twice as long as it should - as if the clock was 4MHz.
Just with the code above the FastLED/NeoPixel signal changed from 400kHz to 800kHz and the delays of 12ms in the code above went from 24ms to 12ms. I checked that with a scope.
So this is still kind a mystery to me.
I also checked a 2nd ATTiny85, same result…

Anyways… Wouldn’t the default attiny85 environment be set up so that the timinig is correct for the chip as it is delivered from the factory? I may do additional testing on that later…

Possibly! Given you had the default fuses set, the system clock was running at 8 MHz but divided down to 1 MHz by the fuse. Looking at the data sheet it appears that this affects the CLKPR register , setting bits CLKPS[3:0] to 0011 giving a divide by 8. Clearing the bits as you have done resets the clock to full speed.

If you found the clock running at 4 MHz, then that would imply a divide by 2 setting, CLKPS[3:0] = 0001, which is not the default. It might be interesting to set up your second ATtiny85 and run a short sketch to flash an LED the number of times speicified by CLKPS[3:0] in case the factory default has been quietly changed?

Well, maybe, maybe not. The Arduino IDE, when the support for ATTiny devices has been added, offers you a number of settings:

  • Internal 1 MHz
  • Internal 8 MHz
  • Internal 16 MHz
  • External 8 MHz
  • External 16 MHz
  • External 20 MHz

I’ve just created a new project for the ATtiny and it’s defaulting to 8 MHz, as in:

Building in release mode
avr-g++ -o .pio/build/attiny85/src/main.cpp.o ...  -DF_CPU=8000000L ...

(EDITED) So, definitely not the default as per the chip from the factory it’s configured for 8 MHz, which is the F_CPU but the CLKDIV8 fuse is dropping that to 1 MHz. I tested the chip with the option to specify the F_CPU as 1000000L and the timings were well off.

Plus, I’ve also checked the prescaler settings on a brand new, unused device with this code:

const int led = 0;
const int longSleep = 1000;
const int shortSleep = 100;

#include "Arduino.h"

uint8_t clkps3_0 = CLKPR & 0x0F;

// the setup routine runs once when you press reset:
void setup() {
    pinMode(led, OUTPUT);
}

// the loop routine runs over and over again forever
void loop() {

  for (uint8_t x = 0; x < clkps3_0; x++) {
    digitalWrite(led, HIGH);
    delay(shortSleep);
    digitalWrite(led, LOW);
    delay(shortSleep);
  }

  delay(longSleep);
}

This gives me 3 flashes, then a long delay, then 3 and so on. So, on my chip at least (and I’ve had a pile of these for some years), the default is definitely 0011 for CLKPS[3:0] which is definitely, divide by 8. Given this, I can’t understand why your chips would appear to be running at 4 MHz and not at 1 MHz. :thinking:

Cheers,
Norm.