standard frequency support?

I wanted to try and centralize the various cpu mhz discussions before they get lost in the ether:

So we expect 8 and 16 mhz support, that's a given. There is some interest in 20mhz and 1mhz as well. I can't endorse just any frequency due to the testing required. Just adding a couple well defined frequencies may mean we have to re-evaluate the timer0 code as well as the more popular libraries.

We might need to adjust the timer0 scalar amount to support these frequencies without having to do a lot of math at runtime.

What else needs considering?

FYI, here is a delayMicroseconds that Don Kinzer had posted and tested at 20mhz:

/* Delay for the given number of microseconds.  Assumes a 8, 16 or 20 MHz clock.
 * Disables interrupts, which will disrupt the millis() function if used
 * too frequently. */
void delayMicroseconds(unsigned int us)
{
#define EXTRA_CYCLES
    uint8_t oldSREG;

    // 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 a 20 MHz clock add one extra cycle to the delay loop
#undef EXTRA_CYCLES
#define EXTRA_CYCLES    " nop" "\n\t" // 1 cycle

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

    // the loop below takes 0.25 microseconds (5 cycles)
    // per iteration, so execute it four times for each microsecond of
    // delay requested.
    us <<= 2;

    // partially compensate for the overhead of getting into and out of the loop
    us -= 3;

#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

    // disable interrupts, otherwise the timer 0 overflow interrupt that
    // tracks milliseconds will make us delay longer than we want.
    oldSREG = SREG;
    cli();

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

    // reenable interrupts.
    SREG = oldSREG;
}

We might need to adjust the timer0 scalar amount to support these frequencies without having to do a lot of math at runtime.

Although it might be a good idea to use a different prescaler at 1MHz, none of the available prescalers mitigates the problems that we've been discussing with respect to 20MHz. All of the available prescaler values are powers of 2 and the microsecond conversion range/resolution issue arises whenever the CPU frequency is not a factor of (prescaler * 10^6).

I think I understood that :slight_smile:

I have to admit I don't completely get it, but what about adding a fixed, frequency specific, value to TCNT0 on overflow? So instead of overflowing at 256 it is really overflowing at something more 20mhz friendly (something less that 256)?

so, perhaps in the special case of 20mhz, if you could in every other timer0 interrupt make the next interrupt happen in 64 TCNT0 increments (instead of the usual 256), then you would effectively have an interrupt at 320 overflows ?

ok, so micros would have to know about every other one too, that would be annoying.

Maybe if you add (again just for 20mhz), 96 to TCNT0 in every overflow, then the effective rollover would be 160 (instead of 256). Does that help?

You should get an interrupt every 512 microseconds at 20mhz that way, as opposed to the usual interrupt every 1024 microseconds.

Of course there be other possibilities too :slight_smile: Change the prescalar to 128 and you are back to 1024.

edit: oops, no 128 prescalar, just 8, 64, 256, 1024 (and 0).