diff --git a/firmware/application/apps/ui_dfu_menu.cpp b/firmware/application/apps/ui_dfu_menu.cpp index 24ee12593..e7fa7bb1c 100644 --- a/firmware/application/apps/ui_dfu_menu.cpp +++ b/firmware/application/apps/ui_dfu_menu.cpp @@ -59,7 +59,7 @@ void DfuMenu::paint(Painter& painter) { text_info_line_7.set(to_string_dec_uint(shared_memory.m4_stack_usage, 6)); text_info_line_8.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6)); text_info_line_9.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6)); - text_info_line_10.set(to_string_dec_uint(chTimeNow() / 1000, 6)); + text_info_line_10.set(to_string_dec_uint(chTimeNow() / CH_FREQUENCY, 6)); constexpr auto margin = 5; diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index 9bc19fcf1..795dd4b0e 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -1012,10 +1012,17 @@ void SetTouchscreenThresholdView::focus() { void SetTouchscreenThresholdView::on_frame_sync() { if (!in_auto_detect) return; + uint32_t time_now = chTimeNow(); int32_t time_diff = time_now - time_start_auto_detect; - text_wait_timer.set("ETA " + to_string_dec_uint((10 - time_diff / 1000) <= 0 ? 0 : 10 - time_diff / 1000) + "s"); - if (time_diff >= 10001 && !auto_detect_succeed_consumed) { // 10s + + // Calculate elapsed seconds using CH_FREQUENCY + uint32_t elapsed_seconds = time_diff / CH_FREQUENCY; + int32_t remaining_seconds = 10 - (int32_t)elapsed_seconds; + if (remaining_seconds < 0) remaining_seconds = 0; + + text_wait_timer.set("ETA " + to_string_dec_uint(remaining_seconds) + "s"); + if (elapsed_seconds >= 10 && !auto_detect_succeed_consumed) { // 10s in_auto_detect = false; text_wait_timer.hidden(true); text_hint.set("OK, press save and reboot"); diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index b93777b8c..7bf280bac 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -428,7 +428,7 @@ void ClockManager::portapack_tcxo_enable() { /* Delay >10ms at 96MHz clock speed for reference oscillator to start. */ /* Delay an additional 1ms (arbitrary) for the clock generator to detect a signal. */ - volatile uint32_t delay = 240000 + 24000; + volatile uint32_t delay = 2400000 + 24000; while (delay--); } @@ -668,37 +668,37 @@ ClockManager::ReferenceSource ClockManager::detect_reference_source() { } ClockManager::Reference ClockManager::choose_reference() { -#ifdef PRALINE - const auto detected_reference = detect_reference_source(); - - if ((detected_reference == ReferenceSource::External) || - (detected_reference == ReferenceSource::PortaPack)) { - const auto frequency = measure_gp_clkin_frequency(); - if ((frequency >= 9850000) && (frequency <= 10150000)) { - return {detected_reference, 10000000}; - } - } -#else +#ifndef PRALINE if (hackrf_r9) { gpio_control::r9_clkin_en.setActive(); - volatile uint32_t delay = 240000 + 24000; + // Allow extra time for slower TCXOs on clone boards to stabilize before measurement + volatile uint32_t delay = 240000 + 240000; while (delay--); } +#endif + + // Determine reference source (respects user config and Si5351 loss-of-signal) const auto detected_reference = detect_reference_source(); + // If an external or PortaPack source is detected, verify its actual frequency if ((detected_reference == ReferenceSource::External) || (detected_reference == ReferenceSource::PortaPack)) { const auto frequency = measure_gp_clkin_frequency(); + + // Check if the measured frequency is within the valid 10 MHz range if ((frequency >= 9850000) && (frequency <= 10150000)) { return {detected_reference, 10000000}; } } +#ifndef PRALINE if (hackrf_r9) { + // Disable r9 clock input if the 10 MHz validation failed gpio_control::r9_clkin_en.setInactive(); } #endif + // Fallback: Disable PortaPack TCXO and default to the HackRF 25 MHz crystal portapack_tcxo_disable(); return {ReferenceSource::Xtal, 25000000}; } diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index a259f3315..e9bb43786 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -1130,13 +1130,18 @@ void SystemView::toggle_overlay() { } void SystemView::paint_overlay() { - static bool last_paint_state = false; + // Static variable to store the timestamp of the last update + static systime_t last_update_time = 0; + if (overlay_active) { - // paint background only every other second - if ((((chTimeNow() >> 10) & 0x01) == 0x01) == last_paint_state) + // Update exactly once per second (CH_FREQUENCY equals 1 second of ticks) + // This replaces the old hardcoded bit-shift logic for better portability + if ((chTimeNow() - last_update_time) < CH_FREQUENCY) return; - last_paint_state = !last_paint_state; + // One second has passed, save the new timestamp + last_update_time = chTimeNow(); + if (overlay_active == 1 && overlay) overlay->set_dirty(); else if (overlay_active == 2 && overlay2) diff --git a/firmware/application/usb_serial_shell.cpp b/firmware/application/usb_serial_shell.cpp index 9e226d973..21f5e8476 100644 --- a/firmware/application/usb_serial_shell.cpp +++ b/firmware/application/usb_serial_shell.cpp @@ -1180,7 +1180,7 @@ static void cmd_sysinfo(BaseSequentialStream* chp, int argc, char* argv[]) { "M4 stack: " + to_string_dec_uint(shared_memory.m4_stack_usage) + "\r\n" + "M0 cpu%: " + to_string_dec_uint(shared_memory.m4_performance_counter) + "\r\n" + "M4 miss: " + to_string_dec_uint(shared_memory.m4_buffer_missed) + "\r\n" + - "uptime: " + to_string_dec_uint(chTimeNow() / 1000) + "\r\n"; + "uptime: " + to_string_dec_uint(chTimeNow() / CH_FREQUENCY) + "\r\n"; fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)info.c_str(), info.length()); return; diff --git a/firmware/baseband/debug.cpp b/firmware/baseband/debug.cpp index 156a5de46..8e834d53c 100644 --- a/firmware/baseband/debug.cpp +++ b/firmware/baseband/debug.cpp @@ -125,15 +125,18 @@ void update_performance_counters() { if (performance_counter_active == 0x00) return; - static bool last_paint_state = false; - if ((((chTimeNow() >> 10) & 0x01) == 0x01) == last_paint_state) + static systime_t last_update_time = 0; + + // The MS2ST(1000) guarantees that this is exactly 1 second, regardless of the system clock setting. + if ((chTimeNow() - last_update_time) < MS2ST(1000)) return; // Idle thread state is sometimes unuseable if (chThdGetTicks(chSysGetIdleThread()) > 0x10000000) return; - last_paint_state = !last_paint_state; + // Update the last update time + last_update_time = chTimeNow(); if (performance_counter_active == 0x01) { auto utilisation = get_cpu_utilisation_in_percent(); diff --git a/firmware/common/i2cdevmanager.cpp b/firmware/common/i2cdevmanager.cpp index 9e39a0aa1..5111ae87b 100644 --- a/firmware/common/i2cdevmanager.cpp +++ b/firmware/common/i2cdevmanager.cpp @@ -318,29 +318,33 @@ void I2CDevManager::create_thread() { msg_t I2CDevManager::timer_fn(void* arg) { (void)arg; uint16_t curr_timer = 0; // seconds since thread start + while (1) { systime_t start_time = chTimeNow(); bool changed = false; - // check if i2c scan needed + + // Check if i2c scan is needed if (force_scan || (scan_interval != 0 && curr_timer % scan_interval == 0)) { changed = changed | scan(); force_scan = false; } + + // Update connected devices based on their own intervals for (size_t i = 0; i < devlist.size(); i++) { if (devlist[i].addr != 0 && devlist[i].dev && devlist[i].dev->query_interval != 0) { - if ((curr_timer % devlist[i].dev->query_interval) == 0) { // only if it is device's interval - devlist[i].dev->update(); // updates it's data, and broadcasts it. if there is any error it will handle in it, and later we can remove it + if ((curr_timer % devlist[i].dev->query_interval) == 0) { + devlist[i].dev->update(); } } } - // remove all unneeded items + // Remove all unneeded items (dead devices or devices throwing too many errors) chMtxLock(&mutex_list); size_t cnt = devlist.size(); devlist.erase(std::remove_if(devlist.begin(), devlist.end(), [](const I2DevListElement& x) { if (x.addr == 0) return true; - if (x.dev && x.dev->need_del == true) return true; // self destruct on too many errors - return false; // won't remove the unidentified ones, so we can list them, and not trying all the time with them + if (x.dev && x.dev->need_del == true) return true; + return false; }), devlist.end()); chMtxUnlock(); @@ -350,11 +354,24 @@ msg_t I2CDevManager::timer_fn(void* arg) { I2CDevListChangedMessage msg{}; EventDispatcher::send_message(msg); } - systime_t end_time = chTimeNow(); - systime_t delta = (end_time > start_time) ? end_time - start_time : 100; // wont calculate overflow, just guess. - if (delta > 950) delta = 950; // ensure minimum 50 milli sleep - chThdSleepMilliseconds(1000 - delta); // 1sec timer + systime_t end_time = chTimeNow(); + + // 1. Calculate elapsed ticks safely handling overflow + uint32_t delta_ticks = end_time - start_time; + + // 2. Keep EVERYTHING in ticks (No MS conversion!) to prevent truncation drift + if (delta_ticks < CH_FREQUENCY) { + // Calculate exactly how many ticks are missing to complete 1 full second + uint32_t sleep_ticks = CH_FREQUENCY - delta_ticks; + + // Sleep using native ticks instead of milliseconds + chThdSleep(sleep_ticks); + } else { + // Safety fallback: if processing took longer than 1 second, sleep 50ms to yield CPU + chThdSleepMilliseconds(50); + } + ++curr_timer; } return 0; diff --git a/firmware/common/jtag.hpp b/firmware/common/jtag.hpp index 3ef42627c..4a2a554a0 100644 --- a/firmware/common/jtag.hpp +++ b/firmware/common/jtag.hpp @@ -59,8 +59,10 @@ class JTAG { void runtest_ms(const size_t count) { auto starttime = chTimeNow(); + // We convert the count in milliseconds to system ticks: + auto duration_ticks = MS2ST(count) + 1; - while ((chTimeNow() - starttime) < (count + 1)) + while ((chTimeNow() - starttime) < duration_ticks) target.clock(0, 0); }