From 25a280c200a77dcd2b2ce004830de1d122b8ceea Mon Sep 17 00:00:00 2001 From: Jason <83615043+JJassonn69@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:07:34 +0545 Subject: [PATCH] refactor(mcu): remove redundant ADAR1000 T/R SPI paths (FPGA-owned) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-chirp T/R switching is owned by the FPGA plfm_chirp_controller driving adar_tr_x pins (TR_SOURCE=1 in REG_SW_CONTROL, already set by initializeSingleDevice). The MCU's SPI RMW path via fastTXMode/ fastRXMode/pulseTXMode/pulseRXMode/setADTR1107Control was: (a) architecturally redundant — raced the FPGA-driven TR line, (b) toggled the wrong bit (TR_SOURCE instead of TR_SPI), (c) in setFastSwitchMode(true) bundled a datasheet-violating PA+LNA-simultaneously-biased side effect. Removed methods and their backing state (fast_switch_mode_, switch_settling_time_us_). Call sites in executeChirpSequence / runRadarPulseSequence updated to rely on the FPGA chirp FSM (GPIOD_8 new_chirp trigger unchanged). Tests: adds CMSIS-Core DWT/CoreDebug/SystemCoreClock stubs to stm32_hal_mock so F-4.7's DWT-based delayUs() compiles under the host mock build. SystemCoreClock=0 makes the busy-wait exit immediately. --- .../ADAR1000_Manager.cpp | 128 +++++------- .../9_1_1_C_Cpp_Libraries/ADAR1000_Manager.h | 57 +++--- .../9_1_3_C_Cpp_Code/main.cpp | 182 +++++++++--------- .../tests/stm32_hal_mock.c | 8 + .../tests/stm32_hal_mock.h | 20 ++ 5 files changed, 193 insertions(+), 202 deletions(-) diff --git a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.cpp b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.cpp index 4f7a942..d9eb47d 100644 --- a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.cpp +++ b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.cpp @@ -163,8 +163,10 @@ void ADAR1000Manager::switchToTXMode() { DIAG("BF", "Step 3: PA bias ON"); setPABias(true); delayUs(50); - DIAG("BF", "Step 4: ADTR1107 -> TX"); - setADTR1107Control(true); + // Step 4 (former setADTR1107Control(true)) removed: TR pin is FPGA-owned. + // Chip follows adar_tr_x; TX path is asserted by the FPGA chirp FSM, not + // by SPI here. Write per-channel TX enables so the FPGA TR override has + // something to gate. for (uint8_t dev = 0; dev < devices_.size(); ++dev) { adarWrite(dev, REG_RX_ENABLES, 0x00, BROADCAST_OFF); @@ -185,8 +187,7 @@ void ADAR1000Manager::switchToRXMode() { DIAG("BF", "Step 2: Disable PA supplies"); disablePASupplies(); delayUs(10); - DIAG("BF", "Step 3: ADTR1107 -> RX"); - setADTR1107Control(false); + // Step 3 (former setADTR1107Control(false)) removed: FPGA owns TR pin. DIAG("BF", "Step 4: Enable LNA supplies"); enableLNASupplies(); delayUs(50); @@ -204,39 +205,11 @@ void ADAR1000Manager::switchToRXMode() { DIAG("BF", "switchToRXMode() complete"); } -void ADAR1000Manager::fastTXMode() { - DIAG("BF", "fastTXMode(): ADTR1107 -> TX (no bias sequencing)"); - setADTR1107Control(true); - for (uint8_t dev = 0; dev < devices_.size(); ++dev) { - adarWrite(dev, REG_RX_ENABLES, 0x00, BROADCAST_OFF); - adarWrite(dev, REG_TX_ENABLES, 0x0F, BROADCAST_OFF); - devices_[dev]->current_mode = BeamDirection::TX; - } - current_mode_ = BeamDirection::TX; -} - -void ADAR1000Manager::fastRXMode() { - DIAG("BF", "fastRXMode(): ADTR1107 -> RX (no bias sequencing)"); - setADTR1107Control(false); - for (uint8_t dev = 0; dev < devices_.size(); ++dev) { - adarWrite(dev, REG_TX_ENABLES, 0x00, BROADCAST_OFF); - adarWrite(dev, REG_RX_ENABLES, 0x0F, BROADCAST_OFF); - devices_[dev]->current_mode = BeamDirection::RX; - } - current_mode_ = BeamDirection::RX; -} - -void ADAR1000Manager::pulseTXMode() { - DIAG("BF", "pulseTXMode(): TR switch only"); - setADTR1107Control(true); - last_switch_time_us_ = HAL_GetTick() * 1000; -} - -void ADAR1000Manager::pulseRXMode() { - DIAG("BF", "pulseRXMode(): TR switch only"); - setADTR1107Control(false); - last_switch_time_us_ = HAL_GetTick() * 1000; -} +// fastTXMode, fastRXMode, pulseTXMode, pulseRXMode: REMOVED. +// The chirp hot path owns T/R switching via the FPGA adar_tr_x pins +// (see 9_Firmware/9_2_FPGA/plfm_chirp_controller.v). The old SPI-RMW per +// chirp was architecturally redundant, raced the FPGA, and toggled the +// wrong bit of REG_SW_CONTROL (TR_SOURCE instead of TR_SPI). // Beam Steering bool ADAR1000Manager::setBeamAngle(float angle_degrees, BeamDirection direction) { @@ -368,25 +341,10 @@ void ADAR1000Manager::writeRegister(uint8_t deviceIndex, uint32_t address, uint8 } // Configuration -void ADAR1000Manager::setSwitchSettlingTime(uint32_t us) { - switch_settling_time_us_ = us; -} - -void ADAR1000Manager::setFastSwitchMode(bool enable) { - DIAG("BF", "setFastSwitchMode(%s)", enable ? "ON" : "OFF"); - fast_switch_mode_ = enable; - if (enable) { - switch_settling_time_us_ = 10; - DIAG("BF", " settling time = 10 us, enabling PA+LNA supplies and bias simultaneously"); - enablePASupplies(); - enableLNASupplies(); - setPABias(true); - setLNABias(true); - } else { - switch_settling_time_us_ = 50; - DIAG("BF", " settling time = 50 us"); - } -} +// setSwitchSettlingTime, setFastSwitchMode: REMOVED. +// Their only reader was the deleted setADTR1107Control; setFastSwitchMode(true) +// also violated the ADTR1107 datasheet bias sequence (PA + LNA biased to +// operational simultaneously). Per-chirp T/R is FPGA-owned now. void ADAR1000Manager::setBeamDwellTime(uint32_t ms) { beam_dwell_time_ms_ = ms; @@ -428,6 +386,16 @@ bool ADAR1000Manager::initializeSingleDevice(uint8_t deviceIndex) { DIAG("BF", " dev[%u] set RAM bypass (bias+beam)", deviceIndex); adarSetRamBypass(deviceIndex, BROADCAST_OFF); + // Hand per-chirp T/R switching to the FPGA. + // Set TR_SOURCE (REG_SW_CONTROL bit 2) = 1 so the chip's internal + // RX_EN_OVERRIDE / TX_EN_OVERRIDE follow the external TR pin (driven by + // plfm_chirp_controller's adar_tr_x output). See ADAR1000 datasheet + // "Theory of Operation" -- SPI Control vs TR Pin Control. + // Without this write, the FPGA's TR pin is ignored and the chip stays + // in RX state (TR_SPI POR default). + DIAG("BF", " dev[%u] SW_CONTROL: TR_SOURCE=1 (FPGA owns TR pin)", deviceIndex); + adarWrite(deviceIndex, REG_SW_CONTROL, (1 << 2), BROADCAST_OFF); + // Initialize ADC DIAG("BF", " dev[%u] enable ADC (2MHz clk)", deviceIndex); adarWrite(deviceIndex, REG_ADC_CONTROL, ADAR1000_ADC_2MHZ_CLK | ADAR1000_ADC_EN, BROADCAST_OFF); @@ -469,9 +437,11 @@ bool ADAR1000Manager::initializeADTR1107Sequence() { HAL_GPIO_WritePin(EN_P_3V3_SW_GPIO_Port, EN_P_3V3_SW_Pin, GPIO_PIN_SET); HAL_Delay(1); - // Step 4: Set CTRL_SW to RX mode initially via GPIO - DIAG("BF", "Step 4: CTRL_SW -> RX (initial safe mode)"); - setADTR1107Control(false); // RX mode + // Step 4: CTRL_SW safe-default is RX. + // FPGA-owned path: with TR_SOURCE=1 (set in initializeSingleDevice) the + // chip follows adar_tr_x, which is 0 in the FPGA FSM's IDLE state = RX. + // No SPI write needed here. + DIAG("BF", "Step 4: CTRL_SW -> RX (FPGA adar_tr_x idle-low == RX)"); HAL_Delay(1); // Step 5: Set VGG_LNA to 0 @@ -573,7 +543,7 @@ bool ADAR1000Manager::setAllDevicesRXMode() { void ADAR1000Manager::setADTR1107Mode(BeamDirection direction) { if (direction == BeamDirection::TX) { DIAG_SECTION("ADTR1107 -> TX MODE"); - setADTR1107Control(true); // TX mode + // setADTR1107Control(true) removed: TR pin is FPGA-driven. // Step 1: Disable LNA power first DIAG("BF", " Disable LNA supplies"); @@ -603,10 +573,11 @@ void ADAR1000Manager::setADTR1107Mode(BeamDirection direction) { } HAL_Delay(5); - // Step 5: Set TR switch to TX mode - DIAG("BF", " TR switch -> TX (TR_SOURCE=1, BIAS_EN)"); + // Step 5: TR switch state is FPGA-driven. TR_SOURCE=1 is set once in + // initializeSingleDevice, so the chip already follows adar_tr_x. + // Only BIAS_EN needs to be asserted here. + DIAG("BF", " BIAS_EN (TR source still = FPGA adar_tr_x)"); for (uint8_t dev = 0; dev < devices_.size(); ++dev) { - adarSetBit(dev, REG_SW_CONTROL, 2, BROADCAST_OFF); // TR_SOURCE = 1 (TX) adarSetBit(dev, REG_MISC_ENABLES, 5, BROADCAST_OFF); // BIAS_EN } DIAG("BF", " ADTR1107 TX mode complete"); @@ -614,7 +585,7 @@ void ADAR1000Manager::setADTR1107Mode(BeamDirection direction) { } else { // RECEIVE MODE: Enable LNA, Disable PA DIAG_SECTION("ADTR1107 -> RX MODE"); - setADTR1107Control(false); // RX mode + // setADTR1107Control(false) removed: TR pin is FPGA-driven. // Step 1: Disable PA power first DIAG("BF", " Disable PA supplies"); @@ -645,34 +616,21 @@ void ADAR1000Manager::setADTR1107Mode(BeamDirection direction) { } HAL_Delay(5); - // Step 5: Set TR switch to RX mode - DIAG("BF", " TR switch -> RX (TR_SOURCE=0, LNA_BIAS_OUT_EN)"); + // Step 5: TR switch state is FPGA-driven (TR_SOURCE left at 1). + // Only LNA_BIAS_OUT_EN needs to be asserted here. + DIAG("BF", " LNA_BIAS_OUT_EN (TR source still = FPGA adar_tr_x)"); for (uint8_t dev = 0; dev < devices_.size(); ++dev) { - adarResetBit(dev, REG_SW_CONTROL, 2, BROADCAST_OFF); // TR_SOURCE = 0 (RX) adarSetBit(dev, REG_MISC_ENABLES, 4, BROADCAST_OFF); // LNA_BIAS_OUT_EN } DIAG("BF", " ADTR1107 RX mode complete"); } } -void ADAR1000Manager::setADTR1107Control(bool tx_mode) { - DIAG("BF", "setADTR1107Control(%s): setting TR switch on all %u devices, settling %lu us", - tx_mode ? "TX" : "RX", (unsigned)devices_.size(), (unsigned long)switch_settling_time_us_); - for (uint8_t dev = 0; dev < devices_.size(); ++dev) { - setTRSwitchPosition(dev, tx_mode); - } - delayUs(switch_settling_time_us_); -} - -void ADAR1000Manager::setTRSwitchPosition(uint8_t deviceIndex, bool tx_mode) { - if (tx_mode) { - // TX mode: Set TR_SOURCE = 1 - adarSetBit(deviceIndex, REG_SW_CONTROL, 2, BROADCAST_OFF); - } else { - // RX mode: Set TR_SOURCE = 0 - adarResetBit(deviceIndex, REG_SW_CONTROL, 2, BROADCAST_OFF); - } -} +// setADTR1107Control, setTRSwitchPosition: REMOVED. +// The per-device SPI RMW of REG_SW_CONTROL bit 2 (TR_SOURCE) was both wrong +// (it toggled the *control source*, not the TX/RX state -- TR_SPI is bit 1) +// and redundant with the FPGA's plfm_chirp_controller adar_tr_x output. +// TR_SOURCE is now set to 1 exactly once in initializeSingleDevice. // Add the new public method bool ADAR1000Manager::setCustomBeamPattern16(const uint8_t phase_pattern[16], BeamDirection direction) { diff --git a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.h b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.h index ae3d570..bc1e991 100644 --- a/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.h +++ b/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.h @@ -48,10 +48,11 @@ public: // Mode Switching void switchToTXMode(); void switchToRXMode(); - void fastTXMode(); - void fastRXMode(); - void pulseTXMode(); - void pulseRXMode(); + // fastTXMode/fastRXMode/pulseTXMode/pulseRXMode were removed: per-chirp T/R + // switching is owned by the FPGA (plfm_chirp_controller -> adar_tr_x pins, + // requires TR_SOURCE=1 in REG_SW_CONTROL, set in initializeSingleDevice). + // The old SPI RMW path was architecturally redundant and also toggled the + // wrong bit (TR_SOURCE instead of TR_SPI). See PR for details. // Beam Steering bool setBeamAngle(float angle_degrees, BeamDirection direction); @@ -69,7 +70,8 @@ public: bool setAllDevicesTXMode(); bool setAllDevicesRXMode(); void setADTR1107Mode(BeamDirection direction); - void setADTR1107Control(bool tx_mode); + // setADTR1107Control removed -- it only wrapped the now-deleted + // setTRSwitchPosition SPI path. FPGA drives the TR pin directly. // Monitoring and Diagnostics float readTemperature(uint8_t deviceIndex); @@ -78,8 +80,11 @@ public: void writeRegister(uint8_t deviceIndex, uint32_t address, uint8_t value); // Configuration - void setSwitchSettlingTime(uint32_t us); - void setFastSwitchMode(bool enable); + // setSwitchSettlingTime / setFastSwitchMode removed: their only reader was + // the deleted setADTR1107Control SPI path, and setFastSwitchMode(true) + // also bundled a datasheet-violating PA+LNA-biased-simultaneously side + // effect. Per-chirp settling is now FPGA-owned. Callers that need a + // warm-up bias state should use switchToTXMode / switchToRXMode instead. void setBeamDwellTime(uint32_t ms); // Getters @@ -100,8 +105,8 @@ public: }; // Configuration - bool fast_switch_mode_ = false; - uint32_t switch_settling_time_us_ = 50; + // fast_switch_mode_ / switch_settling_time_us_ removed: both had no + // readers after the FPGA-owned TR refactor. uint32_t beam_dwell_time_ms_ = 100; uint32_t last_switch_time_us_ = 0; @@ -121,22 +126,22 @@ public: // No VM_GAIN[] table exists: VM magnitude is bits [4:0] of the I/Q bytes // themselves; per-channel VGA gain uses a separate register. static const uint8_t VM_I[128]; - static const uint8_t VM_Q[128]; - - // Named defaults for the ADTR1107 and ADAR1000 power sequence. - static constexpr uint8_t kDefaultTxVgaGain = 0x7F; - static constexpr uint8_t kDefaultRxVgaGain = 30; - static constexpr uint8_t kLnaBiasOff = 0x00; - static constexpr uint8_t kLnaBiasOperational = 0x30; - static constexpr uint8_t kPaBiasTxSafe = 0x5D; - static constexpr uint8_t kPaBiasIdqCalibration = 0x0D; - static constexpr uint8_t kPaBiasOperational = 0x7F; - static constexpr uint8_t kPaBiasRxSafe = 0x20; - static constexpr uint8_t kTxBiasCurrent = 0x2D; - static constexpr uint8_t kTxDriverBiasCurrent = 0x06; - - // Private Methods - bool initializeSingleDevice(uint8_t deviceIndex); + static const uint8_t VM_Q[128]; + + // Named defaults for the ADTR1107 and ADAR1000 power sequence. + static constexpr uint8_t kDefaultTxVgaGain = 0x7F; + static constexpr uint8_t kDefaultRxVgaGain = 30; + static constexpr uint8_t kLnaBiasOff = 0x00; + static constexpr uint8_t kLnaBiasOperational = 0x30; + static constexpr uint8_t kPaBiasTxSafe = 0x5D; + static constexpr uint8_t kPaBiasIdqCalibration = 0x0D; + static constexpr uint8_t kPaBiasOperational = 0x7F; + static constexpr uint8_t kPaBiasRxSafe = 0x20; + static constexpr uint8_t kTxBiasCurrent = 0x2D; + static constexpr uint8_t kTxDriverBiasCurrent = 0x06; + + // Private Methods + bool initializeSingleDevice(uint8_t deviceIndex); bool initializeADTR1107Sequence(); void calculatePhaseSettings(float angle_degrees, uint8_t phase_settings[4]); void delayUs(uint32_t microseconds); @@ -167,7 +172,7 @@ public: void adarSetTxVgaGain(uint8_t deviceIndex, uint8_t channel, uint8_t gain, uint8_t broadcast); void adarSetTxBias(uint8_t deviceIndex, uint8_t broadcast); uint8_t adarAdcRead(uint8_t deviceIndex, uint8_t broadcast); - void setTRSwitchPosition(uint8_t deviceIndex, bool tx_mode); + // setTRSwitchPosition removed -- FPGA owns TR pin. See PR. private: diff --git a/9_Firmware/9_1_Microcontroller/9_1_3_C_Cpp_Code/main.cpp b/9_Firmware/9_1_Microcontroller/9_1_3_C_Cpp_Code/main.cpp index b7ef46f..6b194ee 100644 --- a/9_Firmware/9_1_Microcontroller/9_1_3_C_Cpp_Code/main.cpp +++ b/9_Firmware/9_1_Microcontroller/9_1_3_C_Cpp_Code/main.cpp @@ -483,11 +483,14 @@ void executeChirpSequence(int num_chirps, float T1, float PRI1, float T2, float DIAG("SYS", "executeChirpSequence: num_chirps=%d T1=%.2f PRI1=%.2f T2=%.2f PRI2=%.2f", num_chirps, T1, PRI1, T2, PRI2); // First chirp sequence (microsecond timing) + // T/R switching is owned by the FPGA plfm_chirp_controller: its chirp + // FSM drives adar_tr_x high during LONG_CHIRP/SHORT_CHIRP and low during + // listen/guard. new_chirp (GPIOD_8) triggers the FSM out of IDLE. + // The MCU's old pulseTXMode/pulseRXMode SPI path was redundant and raced + // the FPGA -- removed. for(int i = 0; i < num_chirps; i++) { HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_8); // New chirp signal to FPGA - adarManager.pulseTXMode(); delay_us((uint32_t)T1); - adarManager.pulseRXMode(); delay_us((uint32_t)(PRI1 - T1)); } @@ -496,11 +499,8 @@ void executeChirpSequence(int num_chirps, float T1, float PRI1, float T2, float // Second chirp sequence (nanosecond timing) for(int i = 0; i < num_chirps; i++) { HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_8); // New chirp signal to FPGA - adarManager.pulseTXMode(); delay_ns((uint32_t)(T2 * 1000)); - adarManager.pulseRXMode(); delay_ns((uint32_t)((PRI2 - T2) * 1000)); - } } @@ -513,9 +513,9 @@ void runRadarPulseSequence() { DIAG("SYS", "runRadarPulseSequence #%d: m_max=%d n_max=%d y_max=%d", sequence_count, m_max, n_max, y_max); - // Configure for fast switching - DIAG("BF", "Enabling fast-switch mode for beam sweep"); - adarManager.setFastSwitchMode(true); + // Fast per-chirp switching is now FPGA-owned (plfm_chirp_controller + // adar_tr_x), not MCU-driven. setFastSwitchMode(true) call removed. + DIAG("BF", "Beam sweep start (FPGA owns per-chirp T/R switching)"); int m = 1; // Chirp counter int n = 1; // Beam Elevation position counter @@ -656,18 +656,18 @@ SystemError_t checkSystemHealth(void) { // 1. Check AD9523 Clock Generator static uint32_t last_clock_check = 0; - if (HAL_GetTick() - last_clock_check > 5000) { - GPIO_PinState s0 = HAL_GPIO_ReadPin(AD9523_STATUS0_GPIO_Port, AD9523_STATUS0_Pin); - GPIO_PinState s1 = HAL_GPIO_ReadPin(AD9523_STATUS1_GPIO_Port, AD9523_STATUS1_Pin); - DIAG_GPIO("CLK", "AD9523 STATUS0", s0); - DIAG_GPIO("CLK", "AD9523 STATUS1", s1); - if (s0 == GPIO_PIN_RESET || s1 == GPIO_PIN_RESET) { - current_error = ERROR_AD9523_CLOCK; - DIAG_ERR("CLK", "AD9523 clock health check FAILED (STATUS0=%d STATUS1=%d)", s0, s1); - return current_error; - } - last_clock_check = HAL_GetTick(); - } + if (HAL_GetTick() - last_clock_check > 5000) { + GPIO_PinState s0 = HAL_GPIO_ReadPin(AD9523_STATUS0_GPIO_Port, AD9523_STATUS0_Pin); + GPIO_PinState s1 = HAL_GPIO_ReadPin(AD9523_STATUS1_GPIO_Port, AD9523_STATUS1_Pin); + DIAG_GPIO("CLK", "AD9523 STATUS0", s0); + DIAG_GPIO("CLK", "AD9523 STATUS1", s1); + if (s0 == GPIO_PIN_RESET || s1 == GPIO_PIN_RESET) { + current_error = ERROR_AD9523_CLOCK; + DIAG_ERR("CLK", "AD9523 clock health check FAILED (STATUS0=%d STATUS1=%d)", s0, s1); + return current_error; + } + last_clock_check = HAL_GetTick(); + } // 2. Check ADF4382 Lock Status bool tx_locked, rx_locked; @@ -702,34 +702,34 @@ SystemError_t checkSystemHealth(void) { // 4. Check IMU Communication static uint32_t last_imu_check = 0; - if (HAL_GetTick() - last_imu_check > 10000) { - if (!GY85_Update(&imu)) { - current_error = ERROR_IMU_COMM; - DIAG_ERR("IMU", "Health check: GY85_Update() FAILED"); - return current_error; - } - last_imu_check = HAL_GetTick(); - } + if (HAL_GetTick() - last_imu_check > 10000) { + if (!GY85_Update(&imu)) { + current_error = ERROR_IMU_COMM; + DIAG_ERR("IMU", "Health check: GY85_Update() FAILED"); + return current_error; + } + last_imu_check = HAL_GetTick(); + } // 5. Check BMP180 Communication static uint32_t last_bmp_check = 0; - if (HAL_GetTick() - last_bmp_check > 15000) { - double pressure = myBMP.getPressure(); - if (pressure < 30000.0 || pressure > 110000.0 || isnan(pressure)) { - current_error = ERROR_BMP180_COMM; - DIAG_ERR("SYS", "Health check: BMP180 pressure out of range: %.0f", pressure); - return current_error; - } - last_bmp_check = HAL_GetTick(); - } + if (HAL_GetTick() - last_bmp_check > 15000) { + double pressure = myBMP.getPressure(); + if (pressure < 30000.0 || pressure > 110000.0 || isnan(pressure)) { + current_error = ERROR_BMP180_COMM; + DIAG_ERR("SYS", "Health check: BMP180 pressure out of range: %.0f", pressure); + return current_error; + } + last_bmp_check = HAL_GetTick(); + } - // 6. Check GPS Communication (30s grace period from boot / last valid fix) - uint32_t gps_fix_age = um982_position_age(&um982); - if (gps_fix_age > 30000) { - current_error = ERROR_GPS_COMM; - DIAG_WARN("SYS", "Health check: GPS no fix for >30s (age=%lu ms)", (unsigned long)gps_fix_age); - return current_error; - } + // 6. Check GPS Communication (30s grace period from boot / last valid fix) + uint32_t gps_fix_age = um982_position_age(&um982); + if (gps_fix_age > 30000) { + current_error = ERROR_GPS_COMM; + DIAG_WARN("SYS", "Health check: GPS no fix for >30s (age=%lu ms)", (unsigned long)gps_fix_age); + return current_error; + } // 7. Check RF Power Amplifier Current if (PowerAmplifier) { @@ -760,7 +760,7 @@ SystemError_t checkSystemHealth(void) { DIAG_ERR("SYS", "checkSystemHealth returning error code %d", current_error); } return current_error; -} +} // Error recovery function void attemptErrorRecovery(SystemError_t error) { @@ -905,22 +905,22 @@ void handleSystemError(SystemError_t error) { HAL_Delay(200); } - // Critical errors trigger emergency shutdown. - // - // Safety-critical range: any fault that can damage the PAs or leave the - // system in an undefined state must cut the RF rails via Emergency_Stop(). - // This covers: - // ERROR_RF_PA_OVERCURRENT .. ERROR_POWER_SUPPLY (9..13) -- PA/supply faults - // ERROR_TEMPERATURE_HIGH (14) -- >75 C on the PA thermal sensors; - // without cutting bias + 5V/5V5/RFPA rails - // the GaN QPA2962 stage can thermal-runaway. - // ERROR_WATCHDOG_TIMEOUT (16) -- health-check loop has stalled (>60 s); - // transmitter state is unknown, safest to - // latch Emergency_Stop rather than rely on - // IWDG reset (which re-energises the rails). - if ((error >= ERROR_RF_PA_OVERCURRENT && error <= ERROR_POWER_SUPPLY) || - error == ERROR_TEMPERATURE_HIGH || - error == ERROR_WATCHDOG_TIMEOUT) { + // Critical errors trigger emergency shutdown. + // + // Safety-critical range: any fault that can damage the PAs or leave the + // system in an undefined state must cut the RF rails via Emergency_Stop(). + // This covers: + // ERROR_RF_PA_OVERCURRENT .. ERROR_POWER_SUPPLY (9..13) -- PA/supply faults + // ERROR_TEMPERATURE_HIGH (14) -- >75 C on the PA thermal sensors; + // without cutting bias + 5V/5V5/RFPA rails + // the GaN QPA2962 stage can thermal-runaway. + // ERROR_WATCHDOG_TIMEOUT (16) -- health-check loop has stalled (>60 s); + // transmitter state is unknown, safest to + // latch Emergency_Stop rather than rely on + // IWDG reset (which re-energises the rails). + if ((error >= ERROR_RF_PA_OVERCURRENT && error <= ERROR_POWER_SUPPLY) || + error == ERROR_TEMPERATURE_HIGH || + error == ERROR_WATCHDOG_TIMEOUT) { DIAG_ERR("SYS", "CRITICAL ERROR (code %d: %s) -- initiating Emergency_Stop()", error, err_name); snprintf(error_msg, sizeof(error_msg), "CRITICAL ERROR! Initiating emergency shutdown.\r\n"); @@ -1483,8 +1483,8 @@ int main(void) HAL_GPIO_WritePin(EN_P_3V3_FPGA_GPIO_Port,EN_P_3V3_FPGA_Pin,GPIO_PIN_SET); HAL_Delay(100); DIAG("PWR", "FPGA power sequencing complete -- 1.0V -> 1.8V -> 3.3V"); - - + + // Initialize module IMU DIAG_SECTION("IMU INIT (GY-85)"); DIAG("IMU", "Initializing GY-85 IMU..."); @@ -1493,12 +1493,12 @@ int main(void) Error_Handler(); } DIAG("IMU", "GY-85 initialized OK, running 10 calibration samples"); - for(int i=0; i<10;i++){ - if (!GY85_Update(&imu)) { - Error_Handler(); - } - - ax = imu.ax; + for(int i=0; i<10;i++){ + if (!GY85_Update(&imu)) { + Error_Handler(); + } + + ax = imu.ax; ay = imu.ay; az = imu.az; gx = -imu.gx; @@ -1793,20 +1793,20 @@ int main(void) HAL_Delay(10); } } - RADAR_Longitude = um982_get_longitude(&um982); - RADAR_Latitude = um982_get_latitude(&um982); - DIAG("GPS", "Initial position: lat=%.6f lon=%.6f fix=%d sats=%d", - RADAR_Latitude, RADAR_Longitude, - um982_get_fix_quality(&um982), um982_get_num_sats(&um982)); - - // Re-apply heading after GPS init so the north-alignment stepper move uses - // UM982 dual-antenna heading when available. - if (um982_is_heading_valid(&um982)) { - Yaw_Sensor = um982_get_heading(&um982); - } - - //move Stepper to position 1 = 0° - HAL_GPIO_WritePin(STEPPER_CW_P_GPIO_Port, STEPPER_CW_P_Pin, GPIO_PIN_RESET);//Set stepper motor spinning direction to CCW + RADAR_Longitude = um982_get_longitude(&um982); + RADAR_Latitude = um982_get_latitude(&um982); + DIAG("GPS", "Initial position: lat=%.6f lon=%.6f fix=%d sats=%d", + RADAR_Latitude, RADAR_Longitude, + um982_get_fix_quality(&um982), um982_get_num_sats(&um982)); + + // Re-apply heading after GPS init so the north-alignment stepper move uses + // UM982 dual-antenna heading when available. + if (um982_is_heading_valid(&um982)) { + Yaw_Sensor = um982_get_heading(&um982); + } + + //move Stepper to position 1 = 0° + HAL_GPIO_WritePin(STEPPER_CW_P_GPIO_Port, STEPPER_CW_P_Pin, GPIO_PIN_RESET);//Set stepper motor spinning direction to CCW //Point Stepper to North for(int i= 0;i<(int)(Yaw_Sensor*Stepper_steps/360);i++){ HAL_GPIO_WritePin(STEPPER_CLK_P_GPIO_Port, STEPPER_CLK_P_Pin, GPIO_PIN_SET); @@ -1819,14 +1819,14 @@ int main(void) /**********wait for GUI start flag and Send Lat/Long/alt********/ /***************************************************************/ - GPS_Data_t gps_data; - // Binary packet structure: - // [Header 4 bytes][Latitude 8 bytes][Longitude 8 bytes][Altitude 4 bytes][Pitch 4 bytes][CRC 2 bytes] - gps_data = {RADAR_Latitude, RADAR_Longitude, RADAR_Altitude, Pitch_Sensor, HAL_GetTick()}; - if (!GPS_SendBinaryToGUI(&gps_data)) { - const uint8_t gps_send_error[] = "GPS binary send failed\r\n"; - HAL_UART_Transmit(&huart3, (uint8_t*)gps_send_error, sizeof(gps_send_error) - 1, 1000); - } + GPS_Data_t gps_data; + // Binary packet structure: + // [Header 4 bytes][Latitude 8 bytes][Longitude 8 bytes][Altitude 4 bytes][Pitch 4 bytes][CRC 2 bytes] + gps_data = {RADAR_Latitude, RADAR_Longitude, RADAR_Altitude, Pitch_Sensor, HAL_GetTick()}; + if (!GPS_SendBinaryToGUI(&gps_data)) { + const uint8_t gps_send_error[] = "GPS binary send failed\r\n"; + HAL_UART_Transmit(&huart3, (uint8_t*)gps_send_error, sizeof(gps_send_error) - 1, 1000); + } /* [STM32-006 FIXED] Removed blocking do-while loop that waited for * usbHandler.isStartFlagReceived(). The production V7 PyQt GUI does not diff --git a/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c index 230efb9..597682f 100644 --- a/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c +++ b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.c @@ -406,3 +406,11 @@ static int mock_spi_init_stub(void) { return 0; } const struct no_os_spi_platform_ops stm32_spi_ops = { .init = mock_spi_init_stub, }; + +/* ========================= CMSIS-Core stub storage ======================= */ +/* See stm32_hal_mock.h for rationale. SystemCoreClock = 0 forces delayUs() to + * return immediately under host test builds. DWT->CTRL pre-enabled so the + * one-time-init branch is skipped deterministically. */ +struct _DWT_Mock_Type _dwt_mock = { .CTRL = DWT_CTRL_CYCCNTENA_Msk, .CYCCNT = 0 }; +struct _CoreDebug_Mock_Type _coredebug_mock = { .DEMCR = 0 }; +uint32_t SystemCoreClock = 0U; diff --git a/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.h b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.h index 9153011..7170677 100644 --- a/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.h +++ b/9_Firmware/9_1_Microcontroller/tests/stm32_hal_mock.h @@ -242,6 +242,26 @@ uint8_t ADS7830_Measure_SingleEnded(ADC_HandleTypeDef *hadc, uint8_t channel); * if desired via a global flag. */ extern int mock_printf_enabled; +/* ========================= CMSIS-Core stubs ======================= */ +/* Minimum surface to let F-4.7's DWT-based delayUs() in ADAR1000_Manager.cpp + * compile under the host mock build. SystemCoreClock is intentionally 0 so + * target = microseconds * (SystemCoreClock / 1000000) is also 0, making the + * busy-wait loop exit immediately regardless of argument. Pre-setting + * DWT->CTRL with CYCCNTENA also skips the one-time init branch. */ + +#define DWT_CTRL_CYCCNTENA_Msk (1UL << 0) +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << 24) + +struct _DWT_Mock_Type { uint32_t CTRL; uint32_t CYCCNT; }; +struct _CoreDebug_Mock_Type { uint32_t DEMCR; }; + +extern struct _DWT_Mock_Type _dwt_mock; +extern struct _CoreDebug_Mock_Type _coredebug_mock; +extern uint32_t SystemCoreClock; + +#define DWT (&_dwt_mock) +#define CoreDebug (&_coredebug_mock) + #ifdef __cplusplus } #endif