diff --git a/board/boards/board_declarations.h b/board/boards/board_declarations.h index 9655fc35..ff5ca97a 100644 --- a/board/boards/board_declarations.h +++ b/board/boards/board_declarations.h @@ -29,10 +29,9 @@ struct board { const uint8_t led_pin[3]; const uint8_t led_pwm_channels[3]; // leave at 0 to disable PWM const bool has_spi; - const uint16_t fan_max_rpm; + const bool has_fan; const uint16_t avdd_mV; const uint8_t fan_enable_cooldown_time; - const uint8_t fan_max_pwm; board_init init; board_init_bootloader init_bootloader; board_enable_can_transceiver enable_can_transceiver; diff --git a/board/boards/cuatro.h b/board/boards/cuatro.h index 8944922e..cb532bb8 100644 --- a/board/boards/cuatro.h +++ b/board/boards/cuatro.h @@ -132,8 +132,7 @@ static harness_configuration cuatro_harness_config = { board board_cuatro = { .harness_config = &cuatro_harness_config, .has_spi = true, - .fan_max_rpm = 12500U, - .fan_max_pwm = 99U, // it can go up to 14k RPM, but 99% -> 100% is very non-linear + .has_fan = true, .avdd_mV = 1800U, .fan_enable_cooldown_time = 3U, .init = cuatro_init, diff --git a/board/boards/red.h b/board/boards/red.h index 608ccf64..761c2799 100644 --- a/board/boards/red.h +++ b/board/boards/red.h @@ -115,8 +115,7 @@ board board_red = { .set_bootkick = unused_set_bootkick, .harness_config = &red_harness_config, .has_spi = false, - .fan_max_rpm = 0U, - .fan_max_pwm = 100U, + .has_fan = false, .avdd_mV = 3300U, .fan_enable_cooldown_time = 0U, .init = red_init, diff --git a/board/boards/tres.h b/board/boards/tres.h index b9918dd7..73675a98 100644 --- a/board/boards/tres.h +++ b/board/boards/tres.h @@ -155,8 +155,7 @@ static harness_configuration tres_harness_config = { board board_tres = { .harness_config = &tres_harness_config, .has_spi = true, - .fan_max_rpm = 6600U, - .fan_max_pwm = 100U, + .has_fan = true, .avdd_mV = 1800U, .fan_enable_cooldown_time = 3U, .init = tres_init, diff --git a/board/drivers/fan.h b/board/drivers/fan.h index 1f1fb551..f1041ce8 100644 --- a/board/drivers/fan.h +++ b/board/drivers/fan.h @@ -5,10 +5,13 @@ struct fan_state_t fan_state; static const uint8_t FAN_TICK_FREQ = 8U; void fan_set_power(uint8_t percentage) { - fan_state.target_rpm = ((current_board->fan_max_rpm * CLAMP(percentage, 0U, 100U)) / 100U); + if (percentage > 0U) { + fan_state.power = CLAMP(percentage, 20U, 100U); + } else { + fan_state.power = 0U; + } } -void llfan_init(void); void fan_init(void) { fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; llfan_init(); @@ -16,9 +19,7 @@ void fan_init(void) { // Call this at FAN_TICK_FREQ void fan_tick(void) { - const float FAN_I = 6.5f; - - if (current_board->fan_max_rpm > 0U) { + if (current_board->has_fan) { // Measure fan RPM uint16_t fan_rpm_fast = fan_state.tach_counter * (60U * FAN_TICK_FREQ / 4U); // 4 interrupts per rotation fan_state.tach_counter = 0U; @@ -31,8 +32,8 @@ void fan_tick(void) { print("\n"); #endif - // Cooldown counter - if (fan_state.target_rpm > 0U) { + // Cooldown counter to prevent noise on tachometer line. + if (fan_state.power > 0U) { fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; } else { if (fan_state.cooldown_counter > 0U) { @@ -40,18 +41,8 @@ void fan_tick(void) { } } - // Update controller - if (fan_state.target_rpm == 0U) { - fan_state.error_integral = 0.0f; - } else { - float error = (fan_state.target_rpm - fan_rpm_fast) / ((float) current_board->fan_max_rpm); - fan_state.error_integral += FAN_I * error; - } - fan_state.error_integral = CLAMP(fan_state.error_integral, 0U, current_board->fan_max_pwm); - fan_state.power = fan_state.error_integral; - // Set PWM and enable line pwm_set(TIM3, 3, fan_state.power); - current_board->set_fan_enabled((fan_state.target_rpm > 0U) || (fan_state.cooldown_counter > 0U)); + current_board->set_fan_enabled((fan_state.power > 0U) || (fan_state.cooldown_counter > 0U)); } } diff --git a/board/drivers/fan_declarations.h b/board/drivers/fan_declarations.h index f130bc43..3dd3e7cf 100644 --- a/board/drivers/fan_declarations.h +++ b/board/drivers/fan_declarations.h @@ -3,7 +3,6 @@ struct fan_state_t { uint16_t tach_counter; uint16_t rpm; - uint16_t target_rpm; uint8_t power; float error_integral; uint8_t cooldown_counter; diff --git a/board/main.c b/board/main.c index 11764d20..dcc40922 100644 --- a/board/main.c +++ b/board/main.c @@ -294,7 +294,7 @@ int main(void) { microsecond_timer_init(); current_board->set_siren(false); - if (current_board->fan_max_rpm > 0U) { + if (current_board->has_fan) { fan_init(); } diff --git a/python/__init__.py b/python/__init__.py index c40c5c4e..aa5f49dd 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -131,7 +131,7 @@ class Panda: MAX_FAN_RPMs = { HW_TYPE_TRES: 6600, - HW_TYPE_CUATRO: 12500, + HW_TYPE_CUATRO: 5000, } HARNESS_STATUS_NC = 0 diff --git a/tests/hitl/7_internal.py b/tests/hitl/7_internal.py index 337f287a..9622977f 100644 --- a/tests/hitl/7_internal.py +++ b/tests/hitl/7_internal.py @@ -4,18 +4,21 @@ import pytest from panda import Panda pytestmark = [ - pytest.mark.test_panda_types(Panda.INTERNAL_DEVICES) + # TODO: re-enable this once we update the HITL devices + #pytest.mark.test_panda_types(Panda.INTERNAL_DEVICES) + pytest.mark.test_panda_types([Panda.HW_TYPE_TRES]) ] @pytest.mark.timeout(2*60) -def test_fan_controller(p): +def test_fan_curve(p): + # ensure fan curve is (roughly) linear + for power in (30, 50, 80, 100): p.set_fan_power(0) while p.get_fan_rpm() > 0: time.sleep(0.1) - # wait until fan spins up (and recovers if needed), - # then wait a bit more for the RPM to converge + # wait until fan spins up, then wait a bit more for the RPM to converge p.set_fan_power(power) for _ in range(20): time.sleep(1) @@ -24,7 +27,7 @@ def test_fan_controller(p): time.sleep(5) expected_rpm = Panda.MAX_FAN_RPMs[bytes(p.get_type())] * power / 100 - assert 0.9 * expected_rpm <= p.get_fan_rpm() <= 1.1 * expected_rpm + assert 0.75 * expected_rpm <= p.get_fan_rpm() <= 1.25 * expected_rpm def test_fan_cooldown(p): # if the fan cooldown doesn't work, we get high frequency noise on the tach line @@ -35,21 +38,3 @@ def test_fan_cooldown(p): for _ in range(5): assert p.get_fan_rpm() <= Panda.MAX_FAN_RPMs[bytes(p.get_type())] time.sleep(0.5) - -def test_fan_overshoot(p): - - # make sure it's stopped completely - p.set_fan_power(0) - while p.get_fan_rpm() > 0: - time.sleep(0.1) - - # set it to 30% power to mimic going onroad - p.set_fan_power(30) - max_rpm = 0 - for _ in range(50): - max_rpm = max(max_rpm, p.get_fan_rpm()) - time.sleep(0.1) - - # tolerate 10% overshoot - expected_rpm = Panda.MAX_FAN_RPMs[bytes(p.get_type())] * 30 / 100 - assert max_rpm <= 1.1 * expected_rpm, f"Fan overshoot: {(max_rpm / expected_rpm * 100) - 100:.1f}%"