diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bcd22b..349d01a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,7 @@ target_link_libraries(ds5-bridge hardware_timer hardware_flash hardware_spi + hardware_adc pico_btstack_classic # pico_cyw43_arch_threadsafe_background pico_cyw43_arch_poll diff --git a/src/cmd.cpp b/src/cmd.cpp index a7ea06e..fd0af35 100644 --- a/src/cmd.cpp +++ b/src/cmd.cpp @@ -14,6 +14,39 @@ #include "device/usbd.h" #include "pico/time.h" #include "slots.h" +#include "hardware/clocks.h" +#include "hardware/adc.h" +#include "hardware/vreg.h" + +uint16_t cpu_temp_raw_smoothed() { + // One-time ADC bring-up. This is the only place the ADC is initialised + // now (oled.cpp's CPU screen calls through here too). Runs on core0 + // under the cooperative main loop; adc_select_input(4) is set before + // every read, so the shared ADC needs no locking. + static bool adc_ready = false; + if (!adc_ready) { + adc_init(); + adc_set_temp_sensor_enabled(true); + adc_ready = true; + } + adc_select_input(4); + + // The temp sensor has a shallow slope (-1.721 mV/C) and ~1 LSB ≈ 0.47 C, + // so a lone 12-bit sample swings several tenths of a degree frame to + // frame. Average a big block to kill that... + constexpr int kSamples = 256; + uint32_t acc = 0; + for (int i = 0; i < kSamples; i++) acc += adc_read(); + const float mean = (float)acc / (float)kSamples; + + // ...then a slow EMA so the displayed value glides to the true die + // temperature rather than mirroring the latest block. Seeded on the + // first call so it doesn't ramp up from zero. + static float ema = -1.0f; + if (ema < 0.0f) ema = mean; + else ema += (mean - ema) * 0.15f; + return (uint16_t)(ema + 0.5f); +} bool is_pico_cmd(uint8_t report_id) { if (report_id == 0xf6 || @@ -21,7 +54,8 @@ bool is_pico_cmd(uint8_t report_id) { report_id == 0xf8 || report_id == 0xf9 || report_id == 0xfa || - report_id == 0xfb + report_id == 0xfb || + report_id == 0xfc ) { return true; } @@ -93,6 +127,40 @@ uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer, uint16_t reqlen) { memcpy(buffer + 14, &hci_errs, 4); return want; } + if (report_id == 0xfc) { + // OLED Edition: CPU / Clock telemetry for the web emulator. 11 bytes: + // [0..3] set_khz uint32 configured clk_sys (SYS_CLOCK_KHZ) + // [4..7] real_khz uint32 measured clk_sys (cached, see below) + // [8] vcode uint8 vreg_get_voltage() raw enum code + // [9..10] temp_raw uint16 ADC ch4 12-bit reading + // The web side does the volts/temperature math (same formulas as + // render_screen_cpu) so the firmware HID path stays float-free. + constexpr uint16_t want = 11; + if (reqlen < want) { + printf("[HID] 0xfc reqlen=%u too small for cpu payload (%u)\n", reqlen, want); + return 0; + } + const uint32_t set_khz = (uint32_t)SYS_CLOCK_KHZ; + + // clk_sys is fixed at boot and frequency_count_khz() busy-waits a few + // ms — measure exactly once (lazily) and cache. Doing it here on the + // first poll keeps it off the boot path; one ~ms stall in a single + // GET_REPORT is acceptable. + static uint32_t cached_real_khz = 0; + if (cached_real_khz == 0) { + cached_real_khz = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS); + } + + const uint16_t temp_raw = cpu_temp_raw_smoothed(); + + const uint8_t vcode = (uint8_t)vreg_get_voltage(); + + memcpy(buffer + 0, &set_khz, 4); + memcpy(buffer + 4, &cached_real_khz, 4); + buffer[8] = vcode; + memcpy(buffer + 9, &temp_raw, 2); + return want; + } return 0; } diff --git a/src/cmd.h b/src/cmd.h index 13d075c..38005dd 100644 --- a/src/cmd.h +++ b/src/cmd.h @@ -11,4 +11,11 @@ bool is_pico_cmd(uint8_t report_id); uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer,uint16_t reqlen); void pico_cmd_set(uint8_t report_id, uint8_t const *buffer,uint16_t bufsize); +// Smoothed RP2350 on-die temperature sensor reading (ADC input 4, 12-bit +// raw). A single sample is very noisy; this averages a large block and runs +// a slow EMA so the value converges to the true die temperature instead of +// chasing per-sample noise. Single source of truth — the OLED CPU screen and +// the 0xfc web telemetry both call this so device and web always agree. +uint16_t cpu_temp_raw_smoothed(); + #endif //DS5_BRIDGE_CMD_H