fix: propagate FPGA AGC enable to MCU outer loop via DIG_6 GPIO
Resolve cross-layer AGC control mismatch where opcode 0x28 only controlled the FPGA inner-loop AGC but the STM32 outer-loop AGC (ADAR1000_AGC) ran independently with its own enable state. FPGA: Drive gpio_dig6 from host_agc_enable instead of tied low, making the FPGA register the single source of truth for AGC state. MCU: Change ADAR1000_AGC constructor default from enabled(true) to enabled(false) so boot state matches FPGA reset default (AGC off). Read DIG_6 GPIO every frame with 2-frame confirmation debounce to sync outerAgc.enabled — prevents single-sample glitch from causing spurious AGC state transitions. Tests: Update MCU unit tests for new default, add 6 cross-layer contract tests verifying the FPGA-MCU-GUI AGC invariant chain.
This commit is contained in:
@@ -18,7 +18,7 @@ ADAR1000_AGC::ADAR1000_AGC()
|
||||
, min_gain(0)
|
||||
, max_gain(127)
|
||||
, holdoff_frames(4)
|
||||
, enabled(true)
|
||||
, enabled(false)
|
||||
, holdoff_counter(0)
|
||||
, last_saturated(false)
|
||||
, saturation_event_count(0)
|
||||
|
||||
@@ -2180,9 +2180,24 @@ int main(void)
|
||||
|
||||
runRadarPulseSequence();
|
||||
|
||||
/* [AGC] Outer-loop AGC: read FPGA saturation flag (DIG_5 / PD13),
|
||||
* adjust ADAR1000 VGA common gain once per radar frame (~258 ms).
|
||||
* Only run when AGC is enabled — otherwise leave VGA gains untouched. */
|
||||
/* [AGC] Outer-loop AGC: sync enable from FPGA via DIG_6 (PD14),
|
||||
* then read saturation flag (DIG_5 / PD13) and adjust ADAR1000 VGA
|
||||
* common gain once per radar frame (~258 ms).
|
||||
* FPGA register host_agc_enable is the single source of truth —
|
||||
* DIG_6 propagates it to MCU every frame.
|
||||
* 2-frame confirmation debounce: only change outerAgc.enabled when
|
||||
* two consecutive frames read the same DIG_6 value. Prevents a
|
||||
* single-sample glitch from causing a spurious AGC state transition.
|
||||
* Added latency: 1 extra frame (~258 ms), acceptable for control plane. */
|
||||
{
|
||||
bool dig6_now = (HAL_GPIO_ReadPin(FPGA_DIG6_GPIO_Port,
|
||||
FPGA_DIG6_Pin) == GPIO_PIN_SET);
|
||||
static bool dig6_prev = false; // matches boot default (AGC off)
|
||||
if (dig6_now == dig6_prev) {
|
||||
outerAgc.enabled = dig6_now;
|
||||
}
|
||||
dig6_prev = dig6_now;
|
||||
}
|
||||
if (outerAgc.enabled) {
|
||||
bool sat = HAL_GPIO_ReadPin(FPGA_DIG5_SAT_GPIO_Port,
|
||||
FPGA_DIG5_SAT_Pin) == GPIO_PIN_SET;
|
||||
|
||||
@@ -50,7 +50,7 @@ static void test_defaults()
|
||||
assert(agc.min_gain == 0);
|
||||
assert(agc.max_gain == 127);
|
||||
assert(agc.holdoff_frames == 4);
|
||||
assert(agc.enabled == true);
|
||||
assert(agc.enabled == false); // disabled by default — FPGA DIG_6 is source of truth
|
||||
assert(agc.holdoff_counter == 0);
|
||||
assert(agc.last_saturated == false);
|
||||
assert(agc.saturation_event_count == 0);
|
||||
@@ -67,6 +67,7 @@ static void test_defaults()
|
||||
static void test_saturation_reduces_gain()
|
||||
{
|
||||
ADAR1000_AGC agc;
|
||||
agc.enabled = true; // default is OFF; enable for this test
|
||||
uint8_t initial = agc.agc_base_gain; // 30
|
||||
|
||||
agc.update(true); // saturation
|
||||
@@ -82,6 +83,7 @@ static void test_saturation_reduces_gain()
|
||||
static void test_holdoff_prevents_early_gain_up()
|
||||
{
|
||||
ADAR1000_AGC agc;
|
||||
agc.enabled = true; // default is OFF; enable for this test
|
||||
agc.update(true); // saturate once -> gain = 26
|
||||
uint8_t after_sat = agc.agc_base_gain;
|
||||
|
||||
@@ -101,6 +103,7 @@ static void test_holdoff_prevents_early_gain_up()
|
||||
static void test_recovery_after_holdoff()
|
||||
{
|
||||
ADAR1000_AGC agc;
|
||||
agc.enabled = true; // default is OFF; enable for this test
|
||||
agc.update(true); // saturate -> gain = 26
|
||||
uint8_t after_sat = agc.agc_base_gain;
|
||||
|
||||
@@ -119,6 +122,7 @@ static void test_recovery_after_holdoff()
|
||||
static void test_min_gain_clamp()
|
||||
{
|
||||
ADAR1000_AGC agc;
|
||||
agc.enabled = true; // default is OFF; enable for this test
|
||||
agc.min_gain = 10;
|
||||
agc.agc_base_gain = 12;
|
||||
agc.gain_step_down = 4;
|
||||
@@ -136,6 +140,7 @@ static void test_min_gain_clamp()
|
||||
static void test_max_gain_clamp()
|
||||
{
|
||||
ADAR1000_AGC agc;
|
||||
agc.enabled = true; // default is OFF; enable for this test
|
||||
agc.max_gain = 32;
|
||||
agc.agc_base_gain = 31;
|
||||
agc.gain_step_up = 2;
|
||||
@@ -226,6 +231,7 @@ static void test_apply_gain_spi()
|
||||
static void test_reset_preserves_config()
|
||||
{
|
||||
ADAR1000_AGC agc;
|
||||
agc.enabled = true; // default is OFF; enable for this test
|
||||
agc.agc_base_gain = 42;
|
||||
agc.gain_step_down = 8;
|
||||
agc.cal_offset[3] = -5;
|
||||
@@ -255,6 +261,7 @@ static void test_reset_preserves_config()
|
||||
static void test_saturation_counter()
|
||||
{
|
||||
ADAR1000_AGC agc;
|
||||
agc.enabled = true; // default is OFF; enable for this test
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
agc.update(true);
|
||||
@@ -274,6 +281,7 @@ static void test_saturation_counter()
|
||||
static void test_mixed_sequence()
|
||||
{
|
||||
ADAR1000_AGC agc;
|
||||
agc.enabled = true; // default is OFF; enable for this test
|
||||
agc.agc_base_gain = 30;
|
||||
agc.gain_step_down = 4;
|
||||
agc.gain_step_up = 1;
|
||||
|
||||
Reference in New Issue
Block a user