Is it possible to change the clock speed at the fly?

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For instance when the microcontroller runs at 3.3V feed it with an 8MHz clock speed, and when the microcontroller is fed with 5V upping the clock speed to 16MHz?

One would of course take into account F_CPU in the software, but that could be handled with a protocol between cpu and supply, but would the hardware accept it and is there more in the software that should be taking care of?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In theory, you can do it with the XDIV register (on AVR's that have it). However the 8 & 16MHz limits with regards to voltage levels apply to the incoming clock signal as well, not just the speed at which the core is running.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The modern AVRs have a build in clock prescaler. Usually you can divide the clock by 1, 2, 4, 8, 16, 32, 64, 128 and 256. Search the datasheet for the CLKPR register.

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I read over that section I guess.

So it is possible to have a 16MHz clock source attached to the microcontroller, use the CLKPR register to divide by 2 when running at 3.3V, and then disable the clock prescaler (or put it to 1:1) when the supply is at 5V, right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, but you should program the CKDIV8 fuse to ensure that the controller always runs with a valid clock after power on.

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which is factory programmed, I just read the datasheet on that topic.

But there’s no mention about F_CPU. Obviously one need to take into account the CLKPR bit’s to have the right settings for the timers and all other affected peripherals like the UART UBRR baud rate register.

Danke schön Sebastian,
Thanks glitch.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not sure I'm understanding all you are saying, but just in case there is a misconception: F_CPU isn't evaluated at run-time. It is used by the compiler at compile time. It is a #define and the value of it is string-substituted in the source code (by the C preprocessor) before the compiler proper starts the translation into machine code.

Putting it another way: You cannot alter F_CPU as such at run time.

Thus, you cannot get reliable functionality out of eg _delay_ms() if you change the AVR clock speed at run time. _delay_ms() has already been compiled into a fixed set of machine code, based on F_CPU yes, but it is "fixed".

One way to solve this would be to have separate source files for alternate functions the different clock speeds. In each of those files you encapsulate eg _delay_ms() or anything else you use that depends on F_CPU, with a separate #define F_CPU in each of these variant files.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes Sebastian, but one could simply add a multiplier in the _delay_ms routine (or calling it in a loop), a multiplier equal to the CLKPR value. The same for the calculation of the baud rate bits for example, so no need I think for separate source files.

Edit: obviously not a multiplier equal to the CLKPR, but a divisor if it’s for a delay.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Je m’exprime mal ces derniers temps, too much on my head lately(sorry for the French).

What I’ll do if I need the _delay_ms or _delay_µs, is to use it always in a loop, and when the clock prescaler changes, I’ll change the loop variable consequently.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

fleemy, please read the documentation of the _delay() functions in the avr-libc manual. You should note this information from the manual:

Quote:
In order for these functions to work as intended, compiler optimizations must be enabled, and the delay time must be an expression that is a known constant at compile-time. If these requirements are not met, the resulting delay will be much longer (and basically unpredictable), and applications that otherwise do not use floating-point calculations will experience severe code bloat by the floating-point library routines linked into the application.

So like Johann already said, if you need to use delay() loops you should implement 2 delay functions. One for each possible CPU frequencies:

#ifdef F_CPU
    #undef F_CPU
#endif

#define F_CPU   8000000

#include 
#include 

void delay_ms_8MHz (uint8_t ms)
{
    while (ms > 0)
    {
        _delay_ms (1);
        ms--;
    }
}

//--------------------------------

#ifdef F_CPU
    #undef F_CPU
#endif

#define F_CPU   16000000

void delay_ms_16MHz (uint8_t ms)
{
    while (ms > 0)
    {
        _delay_ms (1);
        ms--;
    }
}

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Um, ignoring the delay issues, I think there's something more fundamental here...

If you wake up the processor to work at (say) 8MHz at 3.3v, and you detect that the voltage input has risen to 5v, you can rack the speed up to 16MHz. But if the voltage no drops to 3.3v, your processor is still trying to work at 16MHz and not necessarily doing anything expected, if indeed anything at all. You'd have to rely on a watchdog to force a reset, and there's no guarantee that either your application can cope with a reset, or that any stored RAM data will remain intact.

Do-able, but not, I think, and elegant solution.

Perhaps I'm missing something obvious.

Neil

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No S-sohn, fleemys solution is quite OK (if no higher precision is needed on the delays). Say he wants a delay of 10 ms, both at 1 MHz and 8 MHz. He codes

void delay10ms(int cpuFreq)
{
   for (int i= 1; i <= cpuFreq; i++) {
      _delay_ms(10);
   }
}

and compiles this with F_CPU = 1 MHz. At execution time, if running at 1 MHz he calls

delay10ms(1);

and running at 8 MHz he calls

delay10ms(8);

Get it?

IMHO the solution is simplicity, robustness and brilliance combined. Some loss of precision will of course be generated, but in cases where this does not matter...

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was thinking on something like this:

ifdef F_CPU
    #undef F_CPU
#endif

#define F_CPU   62500

void delay_clkpr(int8_t delay)
int16_t		i_ clkpr = (256 >>(CLKPR & 0x0F)) * delay;
{
   for (int16_t  i= 0; i < i_ clkpr; i++) {
      _delay_ms(1);
   }
}

Could that work?
I’ll probably lose some precision with the loop, but I’ll look what code it puts out in the ASM window.

barnacle wrote:
Um, ignoring the delay issues, I think there's something more fundamental here...

If you wake up the processor to work at (say) 8MHz at 3.3v, and you detect that the voltage input has risen to 5v, you can rack the speed up to 16MHz. But if the voltage no drops to 3.3v, your processor is still trying to work at 16MHz and not necessarily doing anything expected, if indeed anything at all. You'd have to rely on a watchdog to force a reset, and there's no guarantee that either your application can cope with a reset, or that any stored RAM data will remain intact.

Do-able, but not, I think, and elegant solution.

Perhaps I'm missing something obvious.

Neil

I tested once an AT90CAN128 @ 16MHz (without testing the CAN connection, but with the UART running), with the STK500 lowering step by step the voltage, at 3.3V it was still running and when going lower, the OLED display was the first to fade out, the CPU still running.

Maybe I could add a big cap after a schottky diode at the microcontroller supply, place another on the LDO ground reference, and one at the supply for the rest of the circuit, I should then have the supply to the ECU isolated from the rest and have the right voltage on both.
The cap should give me enough time to alter the CLKPR register.

Thanks for the responses so far.