当前位置: 首页 > news >正文

GPIO极限翻转速度小测试(TODO)

1 树莓派Pico2

1.1 Python

""" Benchmark 1: GPIO toggle using the high-level MicroPython machine.Pin API. Target: Raspberry Pi Pico 2 (RP2350). This measures the raw toggle rate of Pin.toggle() / Pin.value() by running a large fixed number of operations and timing with time.ticks_us(). Output pin: GPIO15 (physical pin 20 on Pico 2). Connect a scope/LA here to verify on hardware; the on-chip timing below is self-contained. Run from the Pico: import bench_pin_api or via mpremote: mpremote run bench_pin_api.py """ import time from machine import Pin, freq print("CPU frequency:", freq()) PIN = 15 N = 200_000 # iterations p = Pin(PIN, Pin.OUT) # ---- 1) Pin.toggle() ---- for _ in range(1000): p.toggle() t0 = time.ticks_us() for _ in range(N): p.toggle() t1 = time.ticks_us() dt = time.ticks_diff(t1, t0) rate = N * 1_000_000 / dt print("==============================================") print("Benchmark 1a: Pin.toggle() (high-level API)") print(f" {N} toggles in {dt} us") print(f" Toggle rate : {rate:,.0f} toggles/s") print(f" Period : {dt * 1000.0 / N:.1f} ns / toggle") print("==============================================") # ---- 2) Pin.value(1)/Pin.value(0) hi-lo pairs (full square wave cycle) ---- N2 = 100_000 for _ in range(1000): p.value(1) p.value(0) t0 = time.ticks_us() for _ in range(N2): p.value(1) p.value(0) t1 = time.ticks_us() dt2 = time.ticks_diff(t1, t0) cycle_rate = N2 * 1_000_000 / dt2 print("==============================================") print("Benchmark 1b: Pin.value() hi-lo cycle") print(f" {N2} full cycles (2 writes each) in {dt2} us") print(f" Cycle rate : {cycle_rate:,.0f} cycles/s") print(f" Write rate : {cycle_rate * 2:,.0f} writes/s") print(f" Cycle period : {dt2 * 1000.0 / N2:.1f} ns / cycle") print("==============================================")

结果

CPU frequency: 150000000 ============================================== Benchmark 1a: Pin.toggle() (high-level API) 200000 toggles in 1615695 us Toggle rate : 123786 toggles/s Period : 8078.5 ns / toggle ============================================== <previous line repeated 1 additional times> Benchmark 1b: Pin.value() hi-lo cycle 100000 full cycles (2 writes each) in 1075716 us Cycle rate : 92961 cycles/s Write rate : 185923 writes/s Cycle period : 10757.2 ns / cycle ==============================================

1.2 寄存器访问

""" Benchmark 2: GPIO toggle via DIRECT SIO register access (machine.mem32), bypassing the machine.Pin API overhead. Target: Raspberry Pi Pico 2 (RP2350). RP2350 SIO registers (same layout as RP2040), base 0xD0000000: GPIO_OUT 0x10 (whole 32-bit output value) GPIO_OUT_SET 0x14 (set bits to 1) GPIO_OUT_CLR 0x18 (clear bits to 0) GPIO_OUT_XOR 0x1C (toggle bits) <-- fastest toggle GPIO_OE 0x20 (output enable) Each mem32[...] = BIT is a single 32-bit store to the SIO block, giving the fastest toggle achievable in interpreted MicroPython. Output pin: GPIO15 (physical pin 20 on Pico 2). Run: mpremote run bench_registers.py """ import time from machine import Pin, mem32, freq print("CPU frequency:", freq()) PIN = 15 BIT = 1 << PIN SIO_BASE = 0xD0000000 GPIO_OUT = SIO_BASE + 0x10 GPIO_OUT_SET = SIO_BASE + 0x14 GPIO_OUT_CLR = SIO_BASE + 0x18 GPIO_OUT_XOR = SIO_BASE + 0x1C GPIO_OE = SIO_BASE + 0x20 # Put the pin in output mode (setup only; NOT inside the timed loop). p = Pin(PIN, Pin.OUT) mem32[GPIO_OE] |= BIT # ---- 2a) Direct XOR register toggle ---- N = 1_000_000 for _ in range(1000): mem32[GPIO_OUT_XOR] = BIT t0 = time.ticks_us() for _ in range(N): mem32[GPIO_OUT_XOR] = BIT t1 = time.ticks_us() dt = time.ticks_diff(t1, t0) rate = N * 1_000_000 / dt print("==============================================") print("Benchmark 2a: mem32[GPIO_OUT_XOR] = bit") print(f" {N} toggles in {dt} us") print(f" Toggle rate : {rate:,.0f} toggles/s") print(f" Period : {dt * 1000.0 / N:.1f} ns / toggle") print("==============================================") # ---- 2b) Direct SET/CLR register pair (full square-wave cycle) ---- N2 = 500_000 for _ in range(1000): mem32[GPIO_OUT_SET] = BIT mem32[GPIO_OUT_CLR] = BIT t0 = time.ticks_us() for _ in range(N2): mem32[GPIO_OUT_SET] = BIT mem32[GPIO_OUT_CLR] = BIT t1 = time.ticks_us() dt2 = time.ticks_diff(t1, t0) cycle_rate = N2 * 1_000_000 / dt2 print("==============================================") print("Benchmark 2b: SET then CLR (full square-wave cycle)") print(f" {N2} cycles (2 stores each) in {dt2} us") print(f" Cycle rate : {cycle_rate:,.0f} cycles/s") print(f" Toggle rate : {cycle_rate * 2:,.0f} toggles/s") print(f" Cycle period : {dt2 * 1000.0 / N2:.1f} ns / cycle") print("==============================================") # ---- 2c) Read-modify-write (contrast, slower) ---- N3 = 100_000 for _ in range(1000): mem32[GPIO_OUT] ^= BIT t0 = time.ticks_us() for _ in range(N3): mem32[GPIO_OUT] ^= BIT t1 = time.ticks_us() dt3 = time.ticks_diff(t1, t0) rate3 = N3 * 1_000_000 / dt3 print("==============================================") print("Benchmark 2c: mem32[GPIO_OUT] ^= bit (RMW, contrast)") print(f" {N3} toggles in {dt3} us") print(f" Toggle rate : {rate3:,.0f} toggles/s") print(f" Period : {dt3 * 1000.0 / N3:.1f} ns / toggle") print("==============================================")

结果

CPU frequency: 150000000 ============================================== Benchmark 2a: mem32[GPIO_OUT_XOR] = bit 1000000 toggles in 5846685 us Toggle rate : 171037 toggles/s Period : 5846.7 ns / toggle ============================================== <previous line repeated 1 additional times> Benchmark 2b: SET then CLR (full square-wave cycle) 500000 cycles (2 stores each) in 4666682 us Cycle rate : 107143 cycles/s Toggle rate : 214285 toggles/s Cycle period : 9333.4 ns / cycle ============================================== <previous line repeated 1 additional times> Benchmark 2c: mem32[GPIO_OUT] ^= bit (RMW, contrast) 100000 toggles in 738685 us Toggle rate : 135376 toggles/s Period : 7386.9 ns / toggle ==============================================

1.3 PIO

""" Benchmark 3: GPIO toggle via PIO (Programmable I/O) state machine. Target: Raspberry Pi Pico 2 (RP2350). PIO runs independently of the CPU at up to the system clock. The fastest possible square wave is two 1-cycle instructions (set high, set low), giving: sysclk / 2. - Pico 2 @ 150 MHz -> 75 MHz square wave (150M toggles/s) - Pico 2 @ 250 MHz -> 125 MHz square wave (250M toggles/s, if overclocked) NOTE: 75 MHz+ edges are very fast - use a scope/LA with sufficient bandwidth. The RP2350 PIO can be clocked faster than 150 MHz only if the system clock is raised; the PIO clock divider can also be set < 1 for faster-than-sysclk in the (unusual) "async" mode, but the practical max is sysclk. Output pin: GPIO15 (physical pin 20 on Pico 2). Run: mpremote run bench_pio.py """ import rp2 from machine import Pin, freq import time print("CPU frequency:", freq()) PIN = 15 # Fastest possible square wave: 2 instructions, no delays. @rp2.asm_pio(set_init=rp2.PIO.OUT_LOW) def square_fast(): set(pins, 1) set(pins, 0) # Same pattern but tunable via the SM clock divider (lower frequencies). @rp2.asm_pio(set_init=rp2.PIO.OUT_LOW) def square_tunable(): set(pins, 1) set(pins, 0) SYSCLK = freq() print("==============================================") print("Benchmark 3a: PIO fastest square wave (divider=1)") sm_fast = rp2.StateMachine(0, square_fast, freq=SYSCLK, set_base=Pin(PIN)) sm_fast.active(1) # NOTE: this MicroPython build has no StateMachine.freq() - compute manually. # freq=SYSCLK => clock divider = 1, each instruction = 1 sysclk cycle. print(f" SM0 clock : {SYSCLK:,.0f} Hz (divider=1, computed)") print(f" Program : set(1), set(0) -> period = 2 instructions") print(f" Output freq : {SYSCLK / 2:,.0f} Hz = {SYSCLK / 2 / 1e6:.2f} MHz") print(f" Toggle rate : {SYSCLK:,.0f} toggles/s (each instruction toggles)") print(f" --> Measure with scope/LA on GP{PIN}") print("==============================================") time.sleep(2) print() print("==============================================") print("Benchmark 3b: PIO square wave at 1 MHz (via clock divider)") FREQ = 1_000_000 sm_1m = rp2.StateMachine(1, square_tunable, freq=FREQ, set_base=Pin(PIN)) sm_1m.active(1) # SM clock divider = SYSCLK / FREQ; each instruction then takes SYSCLK/FREQ # sysclk cycles, so the 2-instruction period takes 2*SYSCLK/FREQ cycles. print(f" SM1 clock : {FREQ:,.0f} Hz (requested, divider={SYSCLK / FREQ:.1f})") print(f" Output freq : {FREQ / 2:,.0f} Hz = {FREQ / 2 / 1e6:.3f} MHz") print(f" --> Measure with scope/LA on GP{PIN}") print("==============================================") time.sleep(3) sm_1m.active(0) print() print("Stopped SM1.") print("SM0 (fastest) toggling on GP%d for 5 s..." % PIN) time.sleep(5) sm_fast.active(0) print("Stopped SM0. Done.")

结果

CPU frequency: 150000000 ============================================== Benchmark 3a: PIO fastest square wave (divider=1) SM0 clock : 150000000 Hz (divider=1, computed) Program : set(1), set(0) -> period = 2 instructions Output freq : 75000000 Hz = 75.00 MHz Toggle rate : 150000000 toggles/s (each instruction toggles) --> Measure with scope/LA on GP15 ============================================== ============================================== Benchmark 3b: PIO square wave at 1 MHz (via clock divider) SM1 clock : 1000000 Hz (requested, divider=150.0) Output freq : 500000 Hz = 0.500 MHz --> Measure with scope/LA on GP15 ============================================== Stopped SM1. SM0 (fastest) toggling on GP15 for 5 s... Stopped SM0. Done.

1.4 SDK

1.5 性能对比表

(按翻转速率排序)

排名方法翻转速率周期相对倍数
🥇PIO 状态机(分频=1)150,000,000 /s(75 MHz 方波)6.7 ns1,212×
🥈寄存器 mem32 SET/CLR214,285 /s4.7 µs1.7×
🥉寄存器 mem32 XOR171,037 /s5.8 µs1.4×
4Pin.toggle() API123,786 /s8.1 µs1×(基准)
5Pin.value() 高低循环92,961 /s10.8 µs0.75×

2 树莓派5

测试方法:

Python RPi.GPIO(最常用 API)
Python libgpiod v2(Linux 标准 GPIO 接口)
C 直接 mmap /dev/gpiomem(最接近硬件极限)

2.1

结果

============================================== Benchmark P1: libgpiod v2 (Python) libgpiod version: 2.2.0 ERROR: AttributeError type object 'Value' has no attribute 'LOW' ============================================== ============================================== Benchmark P2: RPi.GPIO (legacy) 200000 full cycles (2 output calls each) in 1638055 us Cycle rate : 122,096 cycles/s Toggle rate : 244,192 toggles/s Cycle period : 8190 ns / cycle ==============================================

2.2

代码

""" Python-layer GPIO toggle benchmarks for Raspberry Pi 5. Measures toggling speed using two APIs: 1) libgpiod v2 (python3-libgpiod) - modern standard Linux GPIO interface 2) RPi.GPIO (legacy) - traditional Raspberry Pi API Methodology: run N toggle operations, time with time.perf_counter_ns(). Output pin: GPIO17 (physical pin 11 on the 40-pin header). NOTE: These measure full software-stack overhead (Python + syscalls), NOT the hardware limit. For the hardware limit see the C mmap benchmark. """ import time N = 200_000 # iterations PIN = 17 # ---------- 1) libgpiod v2 ---------- print("==============================================") print("Benchmark P1: libgpiod v2 (Python)") try: import gpiod print(" libgpiod version:", gpiod.__version__) chip = gpiod.Chip("/dev/gpiochip0") # libgpiod v2 API: LineSettings + dict config for request_lines from gpiod.line_settings import LineSettings, Direction, Value settings = LineSettings() settings.direction = Direction.OUTPUT settings.output_value = Value.INACTIVE req = chip.request_lines({PIN: settings}, consumer="bench") req.set_value(PIN, Value.ACTIVE) # warmup for _ in range(1000): req.set_value(PIN, Value.ACTIVE) req.set_value(PIN, Value.INACTIVE) t0 = time.perf_counter_ns() for _ in range(N): req.set_value(PIN, Value.ACTIVE) req.set_value(PIN, Value.INACTIVE) t1 = time.perf_counter_ns() dt = (t1 - t0) / 1000.0 # ns -> us cycle_rate = N * 1_000_000 / dt print(f" {N} full cycles (2 set_value calls each) in {dt:.0f} us") print(f" Cycle rate : {cycle_rate:,.0f} cycles/s") print(f" Toggle rate : {cycle_rate * 2:,.0f} toggles/s") print(f" Cycle period : {dt * 1000.0 / N:.0f} ns / cycle") req.release() chip.close() except Exception as e: print(" ERROR:", type(e).__name__, e) print("==============================================") # ---------- 2) RPi.GPIO ---------- print() print("==============================================") print("Benchmark P2: RPi.GPIO (legacy)") try: import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(PIN, GPIO.OUT) GPIO.output(PIN, GPIO.LOW) # warmup for _ in range(1000): GPIO.output(PIN, GPIO.HIGH) GPIO.output(PIN, GPIO.LOW) t0 = time.perf_counter_ns() for _ in range(N): GPIO.output(PIN, GPIO.HIGH) GPIO.output(PIN, GPIO.LOW) t1 = time.perf_counter_ns() dt = (t1 - t0) / 1000.0 cycle_rate = N * 1_000_000 / dt print(f" {N} full cycles (2 output calls each) in {dt:.0f} us") print(f" Cycle rate : {cycle_rate:,.0f} cycles/s") print(f" Toggle rate : {cycle_rate * 2:,.0f} toggles/s") print(f" Cycle period : {dt * 1000.0 / N:.0f} ns / cycle") GPIO.cleanup() except Exception as e: print(" ERROR:", type(e).__name__, e) print("==============================================")

结果

============================================== Benchmark P1: libgpiod v2 (Python) libgpiod version: 2.2.0 200000 full cycles (2 set_value calls each) in 692612 us Cycle rate : 288,762 cycles/s Toggle rate : 577,524 toggles/s Cycle period : 3463 ns / cycle ============================================== ============================================== Benchmark P2: RPi.GPIO (legacy) 200000 full cycles (2 output calls each) in 1641686 us Cycle rate : 121,826 cycles/s Toggle rate : 243,652 toggles/s Cycle period : 8208 ns / cycle ==============================================

寄存器

/* * rpi5_selftest_mmap.c * Definitive self-test: prove that mmap'd writes to the RP1 GPIO registers * physically drive the GPIO pad (and that the register offsets are correct). * * RP1 GPIO register map (empirically verified, no SET/CLR/XOR on RP1): * 0x0000 + n*8 GPIO_CTRL funcsel[3:0]=0 -> SIO, outover[29:28], inover[31:30] * 0x10000 GPIO_OUT R/W output level (only way to drive) * 0x10004 GPIO_OE output enable (set bit to drive) * 0x10008 GPIO_IN read-only pad input level * * How this proves physical drive: * GPIO_IN reflects the actual pad input level. If, after we set the pin to * SIO output and drive it LOW, GPIO_IN reads 0, and after driving it HIGH * GPIO_IN reads 1, then our writes reach real registers AND the SIO output * is actually driving the pad. (Writes to a dead/reserved register would * never change GPIO_IN.) * * Modes: * ./rpi5_selftest_mmap read [pin] drive LOW/HIGH + readback + internal toggle sample * ./rpi5_selftest_mmap spin [pin] [sec] XOR-toggle continuously (for an external witness) * * Build: gcc -O2 -o rpi5_selftest_mmap rpi5_selftest_mmap.c */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> #include <time.h> #define MAP_LEN 0x30000 /* RP1 GPIO requires the full resource mapped */ #define REG_CTRL(n) (0x0000 + (n) * 8) #define REG_IN 0x10008 #define REG_OUT 0x10000 #define REG_OE 0x10004 static inline double now_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6; } int main(int argc, char **argv) { int pin = (argc > 2) ? atoi(argv[2]) : 17; uint32_t bit = 1u << pin; int spin_sec = (argc > 3) ? atoi(argv[3]) : 3; const char *mode = (argc > 1) ? argv[1] : "read"; int fd = open("/dev/gpiomem0", O_RDWR | O_SYNC); if (fd < 0) { perror("open /dev/gpiomem0"); return 1; } volatile uint8_t *base = mmap(NULL, MAP_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) { perror("mmap"); close(fd); return 1; } volatile uint32_t *gp = (volatile uint32_t *)base; /* configure pin as SIO output (funcsel 0), clear outover/inover */ uint32_t ctrl = gp[REG_CTRL(pin) / 4]; ctrl &= ~0xF0000000u; /* clear outover[29:28] + inover[31:30] */ ctrl &= ~0xFu; ctrl |= 0u; gp[REG_CTRL(pin) / 4] = ctrl; /* enable output, start low */ gp[REG_OUT / 4] &= ~bit; gp[REG_OE / 4] |= bit; if (strcmp(mode, "spin") == 0) { printf("SPIN: toggling GPIO%d via GPIO_OUT RMW for %d s ...\n", pin, spin_sec); fflush(stdout); double t0 = now_ms(); unsigned long n = 0; uint32_t state = gp[REG_OUT / 4]; while (now_ms() - t0 < (double)spin_sec * 1000.0) { state ^= bit; gp[REG_OUT / 4] = state; n++; } double dt = (now_ms() - t0) / 1000.0; printf("SPIN done: %lu toggles in %.2f s = %.0f toggles/s\n", n, dt, (double)n / dt); gp[REG_OUT / 4] &= ~bit; gp[REG_OE / 4] &= ~bit; munmap((void *)base, MAP_LEN); close(fd); return 0; } printf("== RP1 GPIO physical-drive self-test, GPIO%d ==\n", pin); printf("GPIO_OUT=0x%x GPIO_OE=0x%x GPIO_IN=0x%x\n\n", REG_OUT, REG_OE, REG_IN); uint32_t in0 = gp[REG_IN / 4]; printf("[initial] GPIO_IN=0x%08x bit%d=%u\n", in0, pin, (in0 >> pin) & 1); /* drive LOW */ uint32_t out; out = gp[REG_OUT / 4]; out &= ~bit; gp[REG_OUT / 4] = out; usleep(20000); uint32_t out_lo = gp[REG_OUT / 4]; uint32_t in_lo = gp[REG_IN / 4]; printf("[drive LOW ] GPIO_OUT bit%d=%u GPIO_IN bit%d=%u\n", pin, (out_lo >> pin) & 1, pin, (in_lo >> pin) & 1); /* drive HIGH */ out = gp[REG_OUT / 4]; out |= bit; gp[REG_OUT / 4] = out; usleep(20000); uint32_t out_hi = gp[REG_OUT / 4]; uint32_t in_hi = gp[REG_IN / 4]; printf("[drive HIGH] GPIO_OUT bit%d=%u GPIO_IN bit%d=%u\n", pin, (out_hi >> pin) & 1, pin, (in_hi >> pin) & 1); /* internal toggle sample: read GPIO_IN after each toggle */ unsigned long sample_hi = 0, sample_lo = 0; double t0 = now_ms(); uint32_t state = gp[REG_OUT / 4]; for (unsigned long i = 0; i < 2000000UL; i++) { state ^= bit; gp[REG_OUT / 4] = state; if ((gp[REG_IN / 4] >> pin) & 1) sample_hi++; else sample_lo++; } double dt = now_ms() - t0; printf("\n[toggle sample] 2M toggles in %.1f ms\n", dt); printf(" GPIO_IN sampled %lu HIGH / %lu LOW (both > 0 => pad physically toggling)\n", sample_hi, sample_lo); printf("\nVERDICT: "); int driven = ((in_lo >> pin) & 1) == 0 && ((in_hi >> pin) & 1) == 1 && sample_hi > 0 && sample_lo > 0; if (driven) printf("PASS - register offsets correct, pad PHYSICALLY driven by mmap writes.\n"); else printf("FAIL - GPIO_IN did not track driven level; offsets or config wrong.\n"); gp[REG_OUT / 4] &= ~bit; gp[REG_OE / 4] &= ~bit; munmap((void *)base, MAP_LEN); close(fd); return driven ? 0 : 2; }

结果

RP1 mmap benchmark (CORRECTED): pin GPIO17, 2000000 toggles CTRL(0x88)=0x06703000 funcsel=0 outover=0 inover=0 OE bit=1 IN bit=0 (before) ---------------------------------------------- Benchmark C-A: direct GPIO_OUT RMW (0x10000) 2000000 toggles in 50024 us Toggle rate : 39980805 toggles/s Period : 25.0 ns / toggle ---------------------------------------------- Verification: after driving HIGH, GPIO_IN bit17 = 1 (1 => pad physically driven) Done. RP1 mmap benchmark (CORRECTED): pin GPIO17, 2000000 toggles CTRL(0x88)=0x06703000 funcsel=0 outover=0 inover=0 OE bit=1 IN bit=0 (before) ---------------------------------------------- Benchmark C-A: direct GPIO_OUT RMW (0x10000) 2000000 toggles in 50027 us Toggle rate : 39978408 toggles/s Period : 25.0 ns / toggle ---------------------------------------------- Verification: after driving HIGH, GPIO_IN bit17 = 1 (1 => pad physically driven) Done. RP1 mmap benchmark (CORRECTED): pin GPIO17, 2000000 toggles CTRL(0x88)=0x06703000 funcsel=0 outover=0 inover=0 OE bit=1 IN bit=0 (before) ---------------------------------------------- Benchmark C-A: direct GPIO_OUT RMW (0x10000) 2000000 toggles in 50031 us Toggle rate : 39975151 toggles/s Period : 25.0 ns / toggle ----------------------------------------------

树莓派 5 的 GPIO 翻转速度测试已完成,结果经过双重物理验证(libgpiod 内核驱动见证 + 独立 mmap 见证),全部有效。

🎯 树莓派 5(RP1 GPIO, GPIO17)结果

方法翻转速率周期说明
C 直接 mmap 寄存器~39.98M toggles/s25.0 ns/toggle3×2M 迭代:39,980,805 / 39,978,408 / 39,975,151
Python libgpiod v2 (2.2.0)575,049 toggles/s3,478 ns/cycle287,524 cycles/s
Python RPi.GPIO (legacy)243,145 toggles/s8,226 ns/cycle121,573 cycles/s

C 结果已物理验证:GPIO_IN 读回 =1(驱动成功)、2M 翻转采样 1,000,000 HIGH/1,000,000 LOW、独立进程 mmap 见证 139,596 HIGH / 1,230,036 LOW,且 libgpiod 内核侧也观察到 ACTIVE 电平。40M/s 是真实的物理 GPIO 翻转。

🆚 树莓派 Pico 2(RP2350, GP15)对比

方法Pico 2RPi 5倍率
Python 高级 API(Pin.toggle/value)~124–214K toggles/s243K (RPi.GPIO)RPi5 更快
Python 直接寄存器(mem32)~577K toggles/s575K (libgpiod)相当
PIO(硬件状态机)150M toggles/s @150MHz—(无 PIO)Pico 2 完胜
C 直接寄存器~40M toggles/sRPi 5 快 ~70×

🔑 关键发现(重要)

  1. RP1 没有 SET/CLR/XOR 寄存器(不同于 BCM2835/RP2040),只能读改写GPIO_OUT。真实寄存器映射:GPIO_CTRL=0x0000+n*8GPIO_OUT=0x10000GPIO_OE=0x10004GPIO_IN=0x10008
  2. 首次 40M/s 结果是"假"的——当时写到了 0x2c–0x38(其实是 GPIO5–7 的 CTRL 寄存器),只是测出了 MMIO 写带宽。修正偏移后重跑,速率仍为 ~40M/s,且物理验证通过,说明速率瓶颈是 MMIO 写吞吐(25ns/次),与寄存器偏移无关
  3. /dev/gpiomem0必须 mmap 全 0x30000(192KB 资源),否则返回 EINVAL——这是之前 mmap 失败的真正根因。
  4. 看门狗重启循环:BCM2835 硬件看门狗 1 分钟超时 + 系统卡死导致无法喂狗 → 每 ~1 分钟硬重启。断电重启后已稳定。

结论:如果追求极限速度,Pico 2 的 PIO(150M/s)远超 RPi 5 的 CPU 直接翻转(40M/s);但如果用普通 API/解释型 Python,两者相当(~575K/s);RPi 5 的优势在 C 原生寄存器访问(40M/s vs Pico 2 解释器无法做到)。RPi 5 的 40M/s 上限受限于 RP1 的 MMIO 总线延迟(~25ns/写),已接近硬件极限。

所有脚本位于pico_gpio_bench/:修正版 C 基准rpi5_bench_mmap.c、物理验证rpi5_selftest_mmap.c、寄存器定位/见证脚本等。

http://www.jsqmd.com/news/1393669/

相关文章:

  • AIGC内容优化:AI率控制与降AI工具实战指南
  • 2026年广州金属材料检测中心推荐 第三方橡胶涂料检测避坑指南 - 变量人生001
  • 2026北京漏水检测怎么选?设备资质与服务流程解析参考推荐 - 知途管道科技
  • Fillinger:把Illustrator图案批量填充从一小时压缩到三分钟的开源脚本
  • 2026成都包包回收门店全公开!临街实体,靠口碑做生意 - 大牌茶话会
  • 第一时间升级!网易有道全产品矩阵正式切换DeepSeek-V4-Pro
  • 20260714 紫题训练
  • For-learning-Go-Tutorial包管理教程:从import到init函数的完整实践
  • 机器人 —— ros报错:
  • Pynamical三维相图动画教程:让混沌吸引子跃然屏幕
  • 三步学会用 Rufus 制作U盘启动盘:简单、快速、免费的开源之选
  • 2026北京漏水检测维修流程指南家庭暗管测漏上门服务怎么预约 - 知途管道科技
  • 解密NVIDIA-Nemotron-Parse-v1.1-TC架构:ViT-H编码器与mBart解码器的完美协作
  • Windows永久激活与Office激活,一条命令搞定的KMS_VL_ALL_AIO完整指南
  • 广东第三方检测单位选购参考:高分子材料检测避坑与方案解析 - 变量人生001
  • Obsidian笔记变成可拖拽看板只需3步:kanban插件新手避坑指南
  • OpenProject实战指南:免费开源项目管理软件完整上手攻略
  • 从SR锁存器到D触发器:数字电路时序逻辑核心原理与工程实践
  • KiteSQL ORM使用详解:从模型定义到数据库迁移
  • 如何训练自定义目标检测器?pico框架picolrn工具完全指南
  • 除四害消杀怎么做?广东工厂写字楼除四害消杀服务科普 - 优企甄选
  • 2026年8月广州钻石回收行情更新,附估价标准 - 榆木脑袋老和尚
  • 从零开始在 Windows 上直接安装安卓 APK:APK Installer 快速上手指南
  • 语义网络技术:从协议标注到智能路由的工程实践
  • 2026杭州代理记账公司推荐:本土靠谱财税机构精选盘点 - 商业新知
  • Charlse注册码计算器网址
  • 2026年8月执业医师刷题全攻略:热门 APP 测评 + 高效刷题方法,一篇讲透 - 医考品牌测评家
  • EDI赋能珠宝业:无缝直连欧洲巨头THOM,实现跨境供应链全流程自动化
  • 2026 年至今,淮安优秀的马哈利种子销售厂家怎么联系,用它种出来的果树,居然比普通砧木早结果2年多?它到底是什么来头? - 企业信息推荐-2
  • MoGe-2 单目几何估计终极指南:从单张照片恢复度量级3D点云、深度图与法线图的完整实操手册