Search code examples
avrattiny

Why is #define F_CPU have no effect on AVR code _delay_ms_() function?


I am programming an ATtiny85 with MS_Visual_Studio with extension Visual_Micro using an Arduino1.6/1.8 Gemma board definition. The programmer is Sparkfun Tiny Programmer.
Here is the very simple code utilized. ( Thanks to InsideGadgets YouTube channel " Change the ATtiny85 clock speed on the fly"). It couldn't get any simpler.

Yet, despite the fact that I change the #define F_CPU 1000000 to #define F_CPU 8000000, there is absolutely no effect on the LED flash cycling period, which is about 2 seconds.
The ATtiny85 act as if the clock was 1MHz, despite any changes to F_CPU.
I tested CLKPR = 3 over CLKPR = 0, which changes the prescaler from a factor of 1 to 8.
This should make the delay 8 times longer, which it does. I searched for some details about the F_CPU definition but could not find any explanation for this behavior.

The Question: Why is there no effect on the _delay_ms(1000) after the F_CPU has been defined from 1MHz to 8MHz?

#define F_CPU 1000000  
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int main(void) {
    DDRB |= (1 << PB0);

    while (1) {
        PORTB |= (1 << PB0);  //  Toggle LED 
        _delay_ms(1000);      //  delay 1 second
        PORTB &= ~(1 << PB0);
        _delay_ms(1000);      //  delay 1 second

        cli();
        CLKPR = (1 << CLKPCE); // set for changing prescaler
                               // Change clock prescaler to 0000
        CLKPR = 0;             // divider by 1
//      CLKPR = 3;             // divider by 8
        sei();
    }                          // complete Loop is a 2 seconds period
return 0;
}

Solution

  • The F_CPU just lets the _delay_ms() macro calculate how many cycles each second takes. You need this because the delay macros are very simple and do not know what the prescaller happens to be set to at the moment they are called at runtime.

    So if you are going to be changing the prescaller on the fly, you might want to make two different delay functions so you can make it clear in your code what is going on.

    It might look like...

    #define F_CPU 1000000                   // We start with 8x prescaler on 8Mhz clock
    
    #define delay_ms_1Mhz(x) (_delay_ms(x))   // Delay when prescaller set to 8x
    #define delay_ms_8Mhz(x) (_delay_ms(x*8)) // Delay when prescaller set to 1x so we need to burn 8x as many cycles
    

    ...and then decide which to call depending on what the prescaler is set to at that point in your code.