refactor(mcu): remove redundant ADAR1000 T/R SPI paths (FPGA-owned)

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.
This commit is contained in:
Jason
2026-04-21 01:07:34 +05:45
parent 1a7bd7e971
commit 25a280c200
5 changed files with 193 additions and 202 deletions
@@ -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) {
@@ -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:
@@ -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
@@ -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;
@@ -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