Raspberry Pi / ESP8266 / Rpi 2040 / Pico / Microchip/ Teensy .. More! Programmatically Getting the Clock Speed for a multitude of devices.

Here is a quick reference to getting the clock speed in Khz for all the models of Raspberry Pi!

Raspberry Pi / ESP8266 / Rpi 2040 / Pico / Microchip/ Teensy .. More! Programmatically Getting the Clock Speed for a multitude of devices.

A quick guide for your reference purposes.

Here is a clean C code example that retrieves the current ARM CPU clock speed (in MHz) on Raspberry Pi models, including the Zero, 3, 4, and 5.

The most reliable and portable method across recent Raspberry Pi OS releases (including Bookworm and later) is to read from the kernel's cpufreq subsystem via sysfs:

/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq

This file reports the current frequency in kHz. Divide by 1000 to obtain MHz.

/*
 * Raspberry Pi current CPU clock speed reader (in MHz)
 * Works on Raspberry Pi Zero, Zero 2 W, 3, 3B+, 4, 5 (and most later models)
 * Uses the standard cpufreq sysfs interface present in modern Raspberry Pi OS.
 *
 * Compile with:  gcc -Wall -O2 -o rpi-cpu-freq rpi-cpu-freq.c
 * Run with:      ./rpi-cpu-freq
 */

#include <stdio.h>
#include <stdlib.h>

#define PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"

int main(void) {
    FILE *fp;
    unsigned long freq_khz;

    fp = fopen(PATH, "r");
    if (fp == NULL) {
        fprintf(stderr, "Error: cannot open %s\n", PATH);
        fprintf(stderr, "       This file is missing on very old kernels or non-RPi systems.\n");
        fprintf(stderr, "       Alternative (less precise): use vcgencmd measure_clock arm\n");
        return EXIT_FAILURE;
    }

    if (fscanf(fp, "%lu", &freq_khz) != 1) {
        fprintf(stderr, "Error: failed to read frequency from %s\n", PATH);
        fclose(fp);
        return EXIT_FAILURE;
    }

    fclose(fp);

    double freq_mhz = (double)freq_khz / 1000.0;

    printf("Current ARM CPU frequency: %.0f MHz  (%.3f MHz exact)\n",
           freq_mhz, freq_mhz);

    return EXIT_SUCCESS;
}

Important Notes by Model

Raspberry Pi Zero / Zero W / 1
Usually fixed ~700–1000 MHz (depending on overclock / governor). The sysfs path works on recent OS images.

Raspberry Pi 3 / 3B+
Dynamic 600–1400 MHz (ondemand / schedutil governor). Sysfs gives accurate real-time value.

Raspberry Pi 4
Dynamic 600–1500 MHz (or 1750/1800+ with overclock). Sysfs is reliable.

Raspberry Pi 5
Dynamic ~1000–2400 MHz (or higher with overclock). Sysfs interface remains fully supported and is the recommended method.

Alternative (using vcgencmd) – when sysfs is unavailable

On very old images or stripped-down systems, the sysfs path may be missing. In that case you can call vcgencmd (requires popen):

// ... inside main() ...
FILE *pipe = popen("vcgencmd measure_clock arm", "r");
if (pipe) {
    unsigned long freq_hz;
    if (fscanf(pipe, "frequency(%*d)=%lu", &freq_hz) == 1) {
        printf("ARM frequency: %.0f MHz\n", (double)freq_hz / 1e6);
    }
    pclose(pipe);
}

This method is slightly slower and less elegant, but works on nearly all Raspberry Pi OS installations.

For most current use cases (2024–2026), prefer the sysfs method shown in the main example — it is faster, requires no external processes, and provides consistent results across Pi Zero through Pi 5.

Here are concise C code examples for programmatically retrieving the current CPU (system) clock speed on the requested platforms. These focus on the most common development environments for each.

ESP8266 (Espressif Non-OS SDK or Arduino Core)

The ESP8266 supports 80 MHz or 160 MHz. Use the official SDK function system_get_cpu_freq().

/*
 * ESP8266 current CPU clock speed (MHz)
 * Compatible with Espressif Non-OS SDK and Arduino-ESP8266 core
 *
 * Compile (Non-OS SDK): include <user_interface.h>
 * Compile (Arduino):    #include <user_interface.h>
 */

#include <stdio.h>
#include "user_interface.h"     // system_get_cpu_freq()

int main(void) {
    uint8_t freq_mhz = system_get_cpu_freq();

    printf("Current CPU frequency: %u MHz\n", freq_mhz);

    // Possible values: 80 or 160 (set via system_update_cpu_freq())
    return 0;
}

Notes:

  • Default: 80 MHz.
  • Change with system_update_cpu_freq(160) (returns bool success).
  • In Arduino IDE, ensure #include <user_interface.h> is present.

RP2040 (Raspberry Pi Pico / Pico 2, Raspberry Pi Pico C/C++ SDK)

The RP2040 system clock (clk_sys) is typically 125 MHz by default, configurable up to 133 MHz officially (or 200 MHz in recent SDK versions with voltage adjustment).

/*
 * RP2040 (Pico) current system clock frequency (Hz → MHz)
 * Uses official Pico SDK (2.0.0+)
 *
 * Required: #include "pico/stdlib.h"
 * Build with: cmake Pico SDK project
 */

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/clocks.h"

int main(void) {
    stdio_init_all();

    // Returns frequency in Hz
    uint32_t freq_hz = clock_get_hz(clk_sys);

    printf("Current system clock frequency: %.0f MHz  (exact: %u Hz)\n",
           freq_hz / 1e6f, freq_hz);

    while (true) {
        tight_loop_contents();  // or sleep_ms(1000); for periodic print
    }
}

Notes:

  • clk_sys is the main system / CPU clock.
  • Use clock_get_hz(clk_usb) or clock_get_hz(clk_adc) for other domains if needed.
  • Overclocking (e.g., 200 MHz) requires set_sys_clock_khz(200000, true) and appropriate VREG voltage.

Microchip MCUs (various families)

Microchip covers many architectures (8-bit PIC, AVR, 32-bit PIC32, SAM ARM Cortex-M). There is no single universal function — retrieval depends on the family.

PIC32MX / PIC32MZ (32-bit MIPS-based, Harmony or legacy MPLAB XC32)

Read from the OSCCON register or precomputed macros.

// PIC32 example (MPLAB XC32)
#include <xc.h>
#include <stdio.h>

// Assuming PLL configuration is already set (via #pragma config or code)

uint32_t get_sysclk_hz(void) {
    // Typical calculation (adjust for your PLL / divider settings)
    // Example: 8 MHz XTAL → PLL → 80 MHz SYSCLK
    return 80000000UL;          // Replace with actual configured value

    // Or read dynamically if needed (more complex on some models):
    // uint32_t pll_mul = (OSCCONbits.PLLMULT + 1) * 2;  // example fragment
}

int main(void) {
    uint32_t freq_hz = get_sysclk_hz();
    printf("System clock: %.0f MHz\n", freq_hz / 1e6);
    return 0;
}

Microchip SAM (Cortex-M based, e.g., SAMD21, SAME51)

Use Atmel START / MPLAB Harmony or direct register access.

// SAMD21 example (common in Arduino MKR / Adafruit M0)
#include <samd21.h>     // or sam.h / samd21g18a.h depending on device
#include <stdio.h>

uint32_t get_cpu_freq_hz(void) {
    // GCLK0 is typically used as main CPU clock source
    // Return configured value (set in GCLK_GENCTRL or system init)
    return 48000000UL;      // Default 48 MHz DFLL; read GCLK_GENCTRL[0].GENEN & source
}

int main(void) {
    uint32_t freq = get_cpu_freq_hz();
    printf("CPU clock: %.0f MHz\n", freq / 1e6f);
    while (1) { }
}

General Microchip guidance:

  • 8-bit PIC (e.g., PIC16/PIC18): Usually fixed or set via OSCCONbits.IRCF; no runtime query function — use #define F_CPU 8000000UL.
  • AVR (e.g., ATmega328, tinyAVR): #define F_CPU 16000000UL — compile-time constant only.
  • SAM (ARM): Check GCLK->GENCTRL[0].reg or use CMSIS SystemCoreClock variable (updated by SystemInit() / SystemCoreClockUpdate()).
  • PIC32: Rely on configuration bits or compute from FNOSC / PLL settings in OSCCON.

For production code on Microchip parts, the clock is almost always a compile-time constant derived from fuse/config bits and initialization code. Runtime reading is rarely needed except for debug or dynamic adjustment.

These examples provide the most direct and commonly used methods for each platform. Adjust includes and build settings according to your specific toolchain (Arduino, Pico SDK, ESP-IDF/Non-OS, MPLAB XC8/XC32, Atmel Studio, etc.).

Here are additional examples for several widely used microcontrollers and development boards that were not previously covered. These focus on popular platforms where the CPU/system clock frequency can be queried programmatically in C, often via vendor SDKs, HAL layers, or direct register access.

I have selected STM32 (ARM Cortex-M, STMicroelectronics), nRF52 series (Nordic Semiconductor, e.g., nRF52840), Teensy 4.x (i.MX RT1062), and ESP32 (Espressif) as representative and commonly requested platforms.

STM32 (using STM32Cube HAL, e.g., STM32F4, STM32L4, STM32H7 series)

The HAL library maintains SystemCoreClock (updated automatically after clock configuration) or provides explicit getter functions.

/*
 * STM32 system clock frequency (SYSCLK) in Hz → MHz
 * Requires STM32Cube HAL (e.g., stm32f4xx_hal.h or equivalent)
 * Most accurate after SystemClock_Config() or HAL_RCC_ClockConfig()
 *
 * Typical include: #include "stm32f4xx_hal.h" (adjust for your series)
 */

#include <stdio.h>
#include "stm32f4xx_hal.h"      // Change to match your MCU family (f4/f7/l4/h7/g0 etc.)

int main(void) {
    HAL_Init();                 // Required for HAL
    // ... Your SystemClock_Config() here (CubeMX generated) ...

    // Option 1: Direct variable (updated by HAL)
    uint32_t sysclk_hz = SystemCoreClock;

    // Option 2: Explicit HAL getter (often identical to SystemCoreClock)
    // uint32_t sysclk_hz = HAL_RCC_GetSysClockFreq();

    // Option 3: AHB clock (HCLK), usually same as CPU clock on most STM32
    // uint32_t hclk_hz   = HAL_RCC_GetHCLKFreq();

    printf("Current system clock frequency: %.0f MHz  (exact: %lu Hz)\n",
           sysclk_hz / 1e6f, sysclk_hz);

    while (1) {
        // Application loop
    }
}

Notes:

  • Call SystemCoreClockUpdate() manually if clock settings change at runtime and HAL did not update it.
  • Typical ranges: 16–180 MHz (F4), up to 480 MHz+ (H7 with overclock).

nRF52840 / nRF52 series (Nordic nRF5 SDK or Zephyr)

The high-frequency clock (HFCLK) is usually 64 MHz when enabled; the CPU runs at HFCLK/1 or HFCLK/4 depending on configuration.

/*
 * nRF52840 / nRF52 current CPU clock frequency
 * Using Nordic nRF5 SDK (direct register access)
 * HFCLK typically 64 MHz; CPU can be divided
 */

#include <stdint.h>
#include <stdio.h>
#include "nrf.h"                // nrf52840.h or nrf.h

uint32_t get_cpu_freq_hz(void) {
    if ((NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk) == 0) {
        return 0;               // HFCLK not running
    }

    uint32_t src = (NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_SRC_Msk)
                   >> CLOCK_HFCLKSTAT_SRC_Pos;

    uint32_t base_hz = (src == CLOCK_HFCLKSTAT_SRC_Xtal) ? 64000000UL : 0;

    // Check if CPU is divided (rare, usually /1)
    if (NRF_CLOCK->HFCLKRCTRL & CLOCK_HFCLKRCTRL_RCTRL_Msk) {
        // Divided by 4 (low-power mode)
        return base_hz / 4;
    }

    return base_hz;             // Normally 64 MHz
}

int main(void) {
    // ... Clock init code here (usually sd_softdevice_enable or manual) ...

    uint32_t freq_hz = get_cpu_freq_hz();
    printf("Current CPU frequency: %.0f MHz\n", freq_hz / 1e6f);

    while (1) {}
}

Notes:

  • In most applications, HFCLK = 64 MHz (crystal) or 64 MHz (synthesized from 32 MHz internal).
  • SoftDevice may manage this; check sd_clock_hfclk_is_running() in S132/S140 SDK.

Teensy 4.x (i.MX RT1062, Arduino/Teensyduino core)

The ARM Cortex-M7 runs at 600 MHz by default (configurable up to ~1 GHz with overclock).

/*
 * Teensy 4.x (i.MX RT1062) current CPU frequency
 * Teensyduino / Arduino environment
 */

#include <stdio.h>
#include <imxrt.h>              // i.MX RT register definitions (Teensy core)

void setup() {
    Serial.begin(115200);
    while (!Serial) {}

    // CCM_CBCDR = Clock Root Divider Register
    // ARM clock = IPG clock * (ARM_PODF + 1)
    // But simpler: most users read predefined variable or CCM
    uint32_t arm_freq = F_CPU;  // Compile-time default (600000000)

    // For runtime verification (approximate, requires knowing config)
    // CCM->CBCMR & CCM_CBCMR_LPM = low power mode affect dividers
    printf("Configured CPU frequency: %.0f MHz\n", arm_freq / 1e6f);

    // Advanced: read CCM_CBCDR (ARM_PODF divider)
    uint32_t podf = (CCM->CBCDR >> CCM_CBCDR_ARM_PODF_SHIFT)
                    & CCM_CBCDR_ARM_PODF_MASK;
    uint32_t approx_freq = 600000000UL / (podf + 1);  // Assuming 600 base
    printf("Approximate runtime CPU frequency: %.0f MHz\n", approx_freq / 1e6f);
}

void loop() {
    delay(2000);
}

Notes:

  • F_CPU is the compile-time constant (set in boards.txt or overclock tools).
  • For dynamic overclock, libraries like TeensyTimerTool or direct CCM/PLL manipulation are used; runtime read often relies on known configuration.

ESP32 (Espressif IDF or Arduino-ESP32)

Use the official IDF function for accurate CPU frequency.

/*
 * ESP32 current CPU clock frequency (MHz)
 * Espressif IDF or Arduino-ESP32 core
 */

#include <stdio.h>
#include "esp_system.h"         // IDF
// or #include <esp32-hal-cpu.h> in Arduino-ESP32

void app_main(void) {           // IDF entry point
    uint32_t freq_mhz = esp_clk_cpu_freq() / 1000000UL;

    printf("Current CPU frequency: %lu MHz\n", freq_mhz);

    // Alternative in Arduino-ESP32:
    // #include <esp32-hal-cpu.h>
    // uint32_t freq = getCpuFrequencyMhz();
}

Notes:

  • Common values: 240 MHz (default), 160/80 MHz (reduced power modes).
  • Dynamic frequency scaling (DFS) is supported; query reflects current state.

These examples cover major families beyond the previous set (Raspberry Pi, RP2040, ESP8266, Microchip). Clock retrieval is often tied to vendor-specific initialization code or HAL layers, as most microcontrollers configure clocks at startup and rarely change them dynamically outside of power-saving modes. Adjust includes and build environments to match your toolchain (STM32CubeIDE, PlatformIO, Arduino, ESP-IDF, etc.).

Linux Rocks Every Day