Get The Crystal Speed Programaticaly, in Arduino

Hi

I wrote a program that will run on 16MHz and 8MHz arduino.
I need to detect, in code, the crystal speed.

How can I do it?

Thank you

F_CPU is defined for this. See the following lines from the optiboot bootloader:

#ifndef BAUD_RATE
#if F_CPU >= 8000000L
#define BAUD_RATE   115200L // Highest rate Avrdude win32 will support
#elsif F_CPU >= 1000000L
#define BAUD_RATE   9600L   // 19200 also supported, but with significant error
#elsif F_CPU >= 128000L
#define BAUD_RATE   4800L   // Good for 128kHz internal RC
#else
#define BAUD_RATE 1200L     // Good even at 32768Hz
#endif
#endif

F_CPU contains it. It's a pre-compiler define :)

How can I do it?

At run-time? You need some other external reference. Or a camera that reads the can markings.

F_CPU is set at compiletime. So if the code is the same, but compiled separately, checking F_CPU will work.

If you are using the same binary at the two different speeds, you have no way to tell at run time.

Thank you all. Checking at compile time is great - it will do the work..

AWOL: At run-time? You need some other external reference. Or a camera that reads the can markings.

I envisioned that the OP was asking how to know the CPU frequency at compile time, and mistakenly said "crystal speed" instead of "cpu frequency."

Your interpretation is more fun though. If they really just need to distinguish between 8MHz and 16MHz, then they could do that with a GPIO. They first drive it low as an output, then they turn it on as an input with a pullup and measure the amount of cycles it takes Rpu to pull up Cpin (from Figure 14-1 of the ATmega328P datasheet). If it's too quick, then you need an external capacitor. Again, he certainly won't get an accurate measurement of the crystal that way, but he should be able to tell 8MHz from 16MHz.

I have a feeling he actually did want the "cpu frequency," however, and was satisfied by the other answers above. I imagine he's also not actually changing the crystal on his board (which is a resonator if we want to get picky), but changing the frequency with the CLKPR register. In that case if he wants to tell whether he's at 8MHz or 16MHz at run time, he'd just read the CLKPR register.

nadav1:
Thank you all.
Checking at compile time is great - it will do the work…

No problem, I had no doubt that was what you wanted although to be nitpicky the cpu frequency and the resonator frequency aren’t the same thing.

For giggles I attempted my DIO idea to test if it’s be possible to detect your cpu frequency. It works! Although I had to add 22pf cap to PD3 in order for the pullup time to be reliably measured on my Arduino Uno clone.

#include <Arduino.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/power.h>

#include "Linduino.h"
#include "LT_printf.h"

#define TEST_PIN_MASK             (1 << 3) // Port D Pin 3
#define RUP                       (40e3)
#define CPIN                      (22e-12)
#define OSC_MAX                   (F_CPU * 1.1)
#define OSC_MIN                   (F_CPU * 0.9)
#define CYCLES_PER_PIN_READ       (2)
#define PIN_READS_MIN             (uint8_t)((RUP * CPIN)/(CYCLES_PER_PIN_READ/OSC_MIN))
#define PIN_READS_MAX             (uint8_t)((RUP * CPIN)/(CYCLES_PER_PIN_READ/OSC_MAX) + 0.5)
#define LENGTH(x)                 (sizeof(x)/sizeof(*x))

int8_t is_clock_correct(uint8_t *pin_reads)
{
  uint8_t test_buffer[16];
  uint8_t test_num;

  PORTD &= ~TEST_PIN_MASK;        // Make pin an output with pin low.
  DDRD |= TEST_PIN_MASK;
  cli();                          // Turn off interrupts
  DDRD &= ~TEST_PIN_MASK;         // Make pin an input
  PORTD |= TEST_PIN_MASK;         // Turn pullup on.
  test_buffer[0] = PIND;          // Record pin values asap.
  test_buffer[1] = PIND;
  test_buffer[2] = PIND;
  test_buffer[3] = PIND;
  test_buffer[4] = PIND;
  test_buffer[5] = PIND;
  test_buffer[6] = PIND;
  test_buffer[7] = PIND;
  test_buffer[8] = PIND;
  test_buffer[9] = PIND;
  test_buffer[10] = PIND;
  test_buffer[11] = PIND;
  test_buffer[12] = PIND;
  test_buffer[13] = PIND;
  test_buffer[14] = PIND;
  test_buffer[15] = PIND;
  sei();                          // Turn on interrupts
  for(test_num = 0; test_num < sizeof(test_buffer); test_num++)
  {
    if(test_buffer[test_num] & TEST_PIN_MASK) break;
  }
  *pin_reads = test_num;

  return (PIN_READS_MIN <= test_num) && (test_num <= PIN_READS_MAX);
}

void setup()
{
  clock_div_t test_clock_list[] = {clock_div_8, clock_div_4, clock_div_2, clock_div_1}; // Must end at the correct clock_div
  int8_t result[LENGTH(test_clock_list)];
  uint8_t pin_reads[LENGTH(test_clock_list)];
  uint8_t clock_num;

  for(clock_num = 0; clock_num < LENGTH(test_clock_list); clock_num++)
  {
    clock_prescale_set(test_clock_list[clock_num]);
    result[clock_num] = is_clock_correct(&pin_reads[clock_num]);
  }

  Serial.begin(115200);             //! Initialize the serial port to the PC
  while (!Serial);                  //! Wait for serial port to be opened in the case of Leonardo USB Serial

  LT_printf(F("At F_CPU = %l.6dMHz, PIN_READS_MIN = %d and PIN_READS_MAX = %d\r\n"), F_CPU, PIN_READS_MIN, PIN_READS_MAX);
  for(clock_num = 0; clock_num < LENGTH(test_clock_list); clock_num++)
  {
    LT_printf(F("result = %d, pin_reads = %d, with clock div of %d\r\n"), result[clock_num], pin_reads[clock_num], test_clock_list[clock_num]);
  }
}

void loop()
{
}

Here is the output:

At F_CPU = 16.000000MHz, PIN_READS_MIN = 6 and PIN_READS_MAX = 8
result = 0, pin_reads = 1, with clock div of 3
result = 0, pin_reads = 2, with clock div of 2
result = 0, pin_reads = 4, with clock div of 1
result = 1, pin_reads = 7, with clock div of 0

I can’t imagine why you’d ever want to do this with an Arduino (other than as a experiment).

Using the AVR in a product it might be useful, however. It’d allow you to use a variety of different crystals and the FW could then configure CLKPR so that the CPU frequency was correct when faster crystals were used.