Other CPU frequencies support (4MHz, 2MHz, etc)

Hi,
It would be nice to have a support for other frequencies than 8 and 16MHz (I mean for the millis, micros, PWM, etc.).
There is a lot of applications which will work fine with 4 or 2MHz, with less current. This may require a small change in wiring lib (timer prescaler changes) I guess.
Ideally, running at any frequency from 1-20MHz would be nice to have supported.
P.

pito:
Hi,
It would be nice to have a support for other frequencies than 8 and 16MHz (I mean for the millis, micros, PWM, etc.).
There is a lot of applications which will work fine with 4 or 2MHz, with less current. This may require a small change in wiring lib (timer prescaler changes) I guess.
Ideally, running at any frequency from 1-20MHz would be nice to have supported.
P.

Actually a arduino board can be made to operate at any clock speed by editing the boards.txt file in the arduino core folder. However the problem is with the bootloader code that is installed into the chip, as it is hard-coded to a specific baudrate that is only valid for either the 8 or 16Mhz clock rates. So the first hurdle to running at non-standard arduino clock rates is to figure out how you are going to get a modified bootloader installed into the chip to begin with. If you program your chip with a ICSP programmer then you eliminate the need for a bootloader at all and the task is a little more straight forward to accomplish.

Lefty

Lefty, absolutely no problem with bootloaders. I've did mine for 328, 32, 1284 any clock, any baudrate. As I've indicated the "issue" is the millis, micros, delay, pwm etc. as this is tuned for 8&16MHz, afaik.

pito:
Lefty, absolutely no problem with bootloaders. I've did mine for 328, 32, 1284 any clock, any baudrate. As I've indicated the "issue" is the millis, micros, delay, pwm etc. as this is tuned for 8&16MHz, afaik.

Those shouldn't be an issue as those functions utilize the f_cpu = xxxx value that is defined in the boards.txt for each of the board types. The Uno board type values are shown here:

##############################################################

uno.name=Arduino Uno
uno.upload.protocol=stk500
uno.upload.maximum_size=32256
uno.upload.speed=115200
uno.bootloader.low_fuses=0xff
uno.bootloader.high_fuses=0xde
uno.bootloader.extended_fuses=0x05
uno.bootloader.path=optiboot
uno.bootloader.file=optiboot_atmega328.hex
uno.bootloader.unlock_bits=0x3F
uno.bootloader.lock_bits=0x0F
uno.build.mcu=atmega328p
uno.build.f_cpu=16000000L
uno.build.core=arduino

##############################################################

Edit the "uno.build.f_cpu=16000000L" line to whatever speed you chip is going to be clocked at and all the Arduino core functions should use the value when the libraries are compiled.

Lefty

Edit the "uno.build.f_cpu=16000000L" line
Lefty, I know that, of course.
So let me ask otherwise: when using other frequencies as the 8&16MHz will the millis, micros, delay, pwm or other related functions work properly (imagine 3.57MHz crystal)?
PS: bootloader, boards.txt are set correctly for the actual frequency.

Let me answer you question this way, with questions for you. Does not the arduino support 'standard' boards where some run at 8 Mhz and some at 16Mhz. Doesn't millis(), pwm, delay, etc work fine in sketches used in those boards? Does that not suggest that the Arduino compilation process can handle variable clock speeds for the standard arduino library functions?

Lefty

Look at wiring.c and you will see that the timer0 period in microseconds, modulo, 1000 has to be a multiple of 8. The modulo (a value between 0 and 999) is divided by 8 to fit it in a byte.

// the fractional number of milliseconds per timer0 overflow. we shift right
// by three to fit these numbers into a byte. (for the clock speeds we care
// about - 8 and 16 MHz - this doesn't lose precision.)
#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
#define FRACT_MAX (1000 >> 3)

static unsigned char timer0_fract = 0;

The last line of the micros() function also assumes an integer number of clock cycles per microsecond (1MHz, 2MHz, 3MHz...) AND that this number divides evenly into 64 (1MHz, 2MHz, 4MHz, 8MHz, 16MHz). Any other value causes calculations to get truncated.

The delay() function uses the micros() function so it will also be off for odd clock rates.

Also look at "delayMicroseconds()" to see that it will only work at 16MHz and 8MHz. If F_CPU is >= 16MHz it uses the 16MHz version, otherwise it uses the 8MHz version.

John, thanks! I see millis does ~6sek within 30min @7.37MHz. Basically it is not a big issue as for longer periods we can use rtc. I saw the timer prescalers are hardwired to /64 for pwm, this might also be not optimal when running at lower clocks..
I am thinking on implementing a "zero drift clock" algorithm for millis, which is precise for ANY xtal frequency (provided you know it well) as it does not count the number of interrupts but counts the number of clock cycles.
For example:

#define TIMER0_FREQUENCY 7372845 //number of clocks per second for atmega, Xtal freq=7.372845MHz
...
then do init the timer:

void Init(void)
{
Ticker = TIMER0_FREQUENCY; // initialize clock counter to number of clocks per second
setup_timer_0(); // initialize Timer0 to interrupt
// ie. exactly every 65536 clock cycles (256*256)
enable_interrupts( INT_TIMER0 ); // Start RTC
}
...

and the isr:

void TIMER0_isr()
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt
if ( Ticker < 65536 ) // If second has expired
{ Ticker += TIMER0_FREQUENCY; // Increment ticker by clocks per second
Seconds++; // Increment number of seconds
}
}

This does represent a PRECISE second in average, the precision depends ONLY on TIMER0_FREQUENCY value. So you can use any crystal value ..
However, that needs to be resized for millis, somehow.

This may help...

delay essentially sums (integrates) time using micros. A small error in micros results in a very large error in delay.

PWM below 3 MHz is essentially unusable. A divisor of 8 (instead of 64) is required.

I vaguely recall that tone does not work correctly below a certain processor frequency. I do remember that, at 1 MHz, the generated tone has clicks and pops (solved by using timer output).

I also vaguely recall that the baud rate calculation does not work correctly below a certain processor frequency.

With the exception of delayMicroseconds, the Tiny Core has very good support for 1 MHz (and, in theory, other power-of-two frequencies). A diff between it and the Arduino 0022 core should highlight the changes that were made.

..just thinking:
a) microseconds are needed for special purposes only and +/- 20% does not represent a problem so it can be generated by a simple loop
b) millis are used for small range, maybe 10-2000ms, precision is not a paramount
c) seconds are used for longer periods (ie. data loggers, clocks) and precision IS important - you may use the above algorithm which is as precise as the Xtal VALUE is known (no other dependencies), so you can use it for "any periods"..
Of course uart is important (8 and 16MHZ are not optimal for uart settings) so 1.843, 3.86, 7.37, 11.059MHz and other values enable use of 115k and higher speeds easily (not possible with 8 and 16MHz).
PWM?. SPI is ok, as well as I2C.

I agree - it would be nice to make these functions work better / properly with other CPU speeds. Feel free to submit patches to the Google Code issue list (Google Code Archive - Long-term storage for Google Code Project Hosting.), particularly if they don't change the behavior for the currently supported CPU speeds (8 and 16 MHz). In particular, I'd love to see better support for 1 MHz clocks, since that's the default speed for most of the chips if you don't change the fuses.

I spent several days trying to get Arduino cores to work correctly with a 20MHz crystal. Every time I stomped out one problem, another appeared. I finally gave up.

It shouldn't be difficult; but it'll depend on what you're willing to give up. There used to be code for the timer overflow that checked whether the interesting algorithms it now uses would work, and used older, slower, code if not. Or you could accept that the basic clock tick would be larger than 1ms.

As CB points out, other areas (notably PWM) were never written to work at many clock rates.

westfw:
There used to be code for the timer overflow that checked whether the interesting algorithms it now uses would work, and used older, slower, code if not.

This?
http://arduino.cc/forum/index.php/topic,70475.0.html

I was thinking of this: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1216294585
It claims to have generated an appropriate error message at 20MHz, at least...
Note that various timing issues with millis() and the timer0 tick have come and gone since then. Before reverting to old code, make sure you understand why it works differently now. (for instance, the original code that only kept tick counts updated in the ISR (and I think had fully clockrate-independent code in millis() to come up with the right value) had serious wrap/32bit overflow issues...

In Teensyduino, I implemented a Tools > CPU Speed menu. The crystal is always 16 MHz, but selecting something other than 16 (eg, 8, 4, 2, 1) reconfigures for that speed using the on-chip clock prescaler. Teensyduino has delayMicroseconds(), delay(), millis(), and micros() (and also extensions elapsedMillis and elapedMicros) that work at those 5 speeds. It also has speed-dependent initialization of the PWM and ADC clocks so they work as expected at all speeds.

Do don't need to buy a $16 Teensy to get this code (but of course, I wouldn't mind if you did... all this software development is funded by Teensy sales). The Teensyduino installer is a free download. After you run the installer, Teensyduino's core is placed in your Arduino, in hardware/teensy/cores/teensy (on a Mac, control-click and choose "show package contents"). Inside you can find this code, and probably even get much of it to compile and work pretty easily on an Arduino Uno. The delayMicroseconds() function, while built from a lot of optimized inline assembly, should be fairly self-contained and ought to work on any AVR chip.

The code in the IDE which implements the CPU Speed menu was contributed as a patch over 1 year ago. Sadly, it never was included in Arduino.

http://code.google.com/p/arduino/issues/detail?id=257