Arduino at 11.0592MHz. Modify Timer0, delay() ... functions

Hello

I am using the Arduino Uno at 11.0592MHz. I recognized that the delayMicroseconds() function is about 27% to fast. The reason is, that the code in wiring.c assumes that the clock peed is now 8MHz.

Now I try to fix it. I found different posts but I am not sure what is the most Arduino compatible way. What can you recommend?

  1. Multiply the variable "us" in wiring.c with 1.27 if the F_CPU is 11.0592MHz? Easy but only affects delayMicroseconds and not millis(), micros(), delay(), etc.
  2. Change the prescale factor maybe from 64 to ???

Other ideas or guides?

Thank you in advance.
Felix

pro.name=Arduino Pro or Pro Mini (3.3V, 8 MHz) w/ ATmega168

pro.upload.protocol=arduino

pro.upload.maximum_size=14336

pro.upload.speed=19200

pro.bootloader.low_fuses=0xc6

pro.bootloader.high_fuses=0xdd

pro.bootloader.extended_fuses=0x00

pro.bootloader.path=atmega

pro.bootloader.file=ATmegaBOOT_168_pro_8MHz.hex

pro.bootloader.unlock_bits=0x3F

pro.bootloader.lock_bits=0x0F

pro.build.mcu=atmega168

pro.build.f_cpu=8000000L

pro.build.core=arduino

pro.build.variant=standard

I would hope it would be a simple job of altering the _cpu= setting in the boards.txt but suspect someone will (politely) tell us why this is not so. You may run into issues if your using Serial though as I think the baud rate settings expect either 8 or 16 MHz.

You may run into issues if your using Serial though as I think the baud rate settings expect either 8 or 16 MHz.

Nope, the baudrates will even be more precise with the 11.0592 crystal as it is the baudrate friendly one. Just change the

# pro.build.f_cpu=8000000L
to
# pro.build.f_cpu=11059200L

as Riva suggests.

Hi

To change the FCPU is the first step. After this you can use your Arduino sketch. But you have to hack some more code.

  1. For example the SoftwareSerial library has to be modified because is is written for 8MHz, 16Mhz and 20MHz. Here I have a post about it: Arduino at 11.0592Mhz; Modify SoftwareSerial - Device Hacking - Arduino Forum

  2. The timing functions are wrong as weel (about 27.6%) because they are also only written for 8MHz, 16Mhz and 20MHz. The file which is needed here is the following file: hardware/arduino/cores/arduino/wiring.c

This is a potencial intersting line:

#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))

Then later in the delaymicrosecond() the bit shifts for the "us" variable.

/* Delay for the given number of microseconds.  Assumes a 8 or 16 MHz clock. */
void delayMicroseconds(unsigned int us)
{
	// calling avrlib's delay_us() function with low values (e.g. 1 or
	// 2 microseconds) gives delays longer than desired.
	//delay_us(us);
#if F_CPU >= 20000000L
	// for the 20 MHz clock on rare Arduino boards

	// for a one-microsecond delay, simply wait 2 cycle and return. The overhead
	// of the function call yields a delay of exactly a one microsecond.
	__asm__ __volatile__ (
		"nop" "\n\t"
		"nop"); //just waiting 2 cycle
	if (--us == 0)
		return;

	// the following loop takes a 1/5 of a microsecond (4 cycles)
	// per iteration, so execute it five times for each microsecond of
	// delay requested.
	us = (us<<2) + us; // x5 us

	// account for the time taken in the preceeding commands.
	us -= 2;

#elif F_CPU >= 16000000L
	// for the 16 MHz clock on most Arduino boards

	// for a one-microsecond delay, simply return.  the overhead
	// of the function call yields a delay of approximately 1 1/8 us.
	if (--us == 0)
		return;

	// the following loop takes a quarter of a microsecond (4 cycles)
	// per iteration, so execute it four times for each microsecond of
	// delay requested.
	us <<= 2;

	// account for the time taken in the preceeding commands.
	us -= 2;
#else
	// for the 8 MHz internal clock on the ATmega168

	// for a one- or two-microsecond delay, simply return.  the overhead of
	// the function calls takes more than two microseconds.  can't just
	// subtract two, since us is unsigned; we'd overflow.
	if (--us == 0)
		return;
	if (--us == 0)
		return;

	// the following loop takes half of a microsecond (4 cycles)
	// per iteration, so execute it twice for each microsecond of
	// delay requested.
	us <<= 1;
    
	// partially compensate for the time taken by the preceeding commands.
	// we can't subtract any more than this or we'd overflow w/ small delays.
	us--;
#endif

	// busy wait
	__asm__ __volatile__ (
		"1: sbiw %0,1" "\n\t" // 2 cycles
		"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
	);
}

Furthermore in wiring_pulse.c the following line:

return clockCyclesToMicroseconds(width * 21 + 16);

Cheers

A short comment to the baud rate. Some UART devices needs 115200baud. This is not really nice at 16MHz because the error rate is too high. So I am using 11.0592MHz. Here a graphic from the ATmega328 datasheet:

I found a nice post about the topic of the millis function and the prescale. Still I don't know how to fix the issue for the 11.0592MHz. But this post is nice.

From the delaymicroseconds function you see, that a Arduino with a FCPU of 11.0592MHz uses the 8MHz delays. So 27.6% to fast.

FelixGroup:
Still I don't know how to fix the issue for the 11.0592MHz.

Felix, did you find any solution in the meantime? :slight_smile:

The wiring.c part of the Arduino core makes a bunch of assumptions:
The clock rate is evenly divisible by 1,000,000.
The microsecond per clock overflow modulo 1000 is divisible by 8. This results in a value below 125 so the overflow accumulator fits in 8 bits.

I think the only way to make the Arduino core work with all clock rates is to do a major re-write of the wiring.c file.

For some things you can use routines from the AVR LibC:

// Helper macros for baud rate calculations
#include <util/setbaud.h>

// Delays in milliseconds and microseconds
#include <util/delay.h>
void _delay_ms[/iurl] (double __ms);
void _delay_us(double __us);

// Delays in clock cycles
#include <util/delay_basic.h>
void _delay_loop_1(uint8_t __count);
void _delay_loop_2(uint16_t __count);