/** * @file apply_controller.c * @brief Apply/Hold/Release State Machine. * * @arch SWA-002 * @reqs SWE-001 SWE-002 SWE-003 SWE-004 * * ASIL: D. Diese Komponente ist die sicherheitskritische Kernlogik. * Aenderungen erfordern Technical Review mit 2 Approvals. */ #include #include "apply_controller.h" #include "actuator_driver.h" typedef struct { EpbState state; uint8_t step_in_state; /* 50ms-Ticks im aktuellen State */ EpbStatus last_error; uint32_t step_count; /* Watchdog-Alive-Counter */ } ApplyCtx; static ApplyCtx s_ctx; static void enter_state(EpbState new_state) { s_ctx.state = new_state; s_ctx.step_in_state = 0U; } static bool release_preconditions_ok(const ApplyInputs* in) { /* @reqs SWE-005 (Release-Voraussetzungen) — hier konsumiert */ return in->engine_running && in->brake_pedal_pressed && in->gear_engaged; } static bool apply_request_present(const ApplyInputs* in) { return (in->sw_state == SWITCH_APPLY) || in->safety_apply_request; } static bool release_request_present(const ApplyInputs* in) { return in->sw_state == SWITCH_RELEASE; } static uint16_t min_force(const ApplyInputs* in) { return (in->left_force_n < in->right_force_n) ? in->left_force_n : in->right_force_n; } EpbStatus apply_ctrl_init(void) { s_ctx.state = EPB_STATE_RELEASED; s_ctx.step_in_state = 0U; s_ctx.last_error = EPB_OK; s_ctx.step_count = 0U; return EPB_OK; } void apply_ctrl_step_50ms(const ApplyInputs* in) { if (in == NULL) { s_ctx.last_error = EPB_EINVAL; return; } /* SWE-002: Watchdog-Alive-Counter erhoehen */ ++s_ctx.step_count; if (s_ctx.step_in_state < UINT8_MAX) { ++s_ctx.step_in_state; } switch (s_ctx.state) { case EPB_STATE_RELEASED: if (apply_request_present(in) && in->standstill) { (void)actuator_apply(ACTUATOR_LEFT, 80U); (void)actuator_apply(ACTUATOR_RIGHT, 80U); enter_state(EPB_STATE_APPLYING); } break; case EPB_STATE_APPLYING: /* SWE-004: Klemmkraft-Erreichen pruefen */ if (min_force(in) >= APPLY_TARGET_FORCE_N) { (void)actuator_stop(ACTUATOR_LEFT); (void)actuator_stop(ACTUATOR_RIGHT); enter_state(EPB_STATE_APPLIED); } else if (s_ctx.step_in_state >= APPLY_TIMEOUT_50MS) { s_ctx.last_error = EPB_ETIMEOUT; (void)actuator_stop(ACTUATOR_LEFT); (void)actuator_stop(ACTUATOR_RIGHT); enter_state(EPB_STATE_ERROR); } break; case EPB_STATE_APPLIED: /* SWE-001: Klemmkraft halten — bei Unterschreitung nachregeln */ if (min_force(in) < (APPLY_TARGET_FORCE_N - HOLD_TOLERANCE_N)) { (void)actuator_apply(ACTUATOR_LEFT, 60U); (void)actuator_apply(ACTUATOR_RIGHT, 60U); enter_state(EPB_STATE_APPLYING); break; } if (release_request_present(in) && release_preconditions_ok(in)) { (void)actuator_release(ACTUATOR_LEFT, 80U); (void)actuator_release(ACTUATOR_RIGHT, 80U); enter_state(EPB_STATE_RELEASING); } break; case EPB_STATE_RELEASING: if (min_force(in) < HOLD_TOLERANCE_N) { (void)actuator_stop(ACTUATOR_LEFT); (void)actuator_stop(ACTUATOR_RIGHT); enter_state(EPB_STATE_RELEASED); } else if (s_ctx.step_in_state >= (APPLY_TIMEOUT_50MS - 6U)) { s_ctx.last_error = EPB_ETIMEOUT; (void)actuator_stop(ACTUATOR_LEFT); (void)actuator_stop(ACTUATOR_RIGHT); enter_state(EPB_STATE_ERROR); } break; case EPB_STATE_ERROR: default: /* Auf Reset warten; sicherer Zustand ist Apply, also kein Release */ if (!apply_request_present(in) && !release_request_present(in)) { s_ctx.last_error = EPB_OK; enter_state(EPB_STATE_RELEASED); } break; } } EpbState apply_ctrl_get_state(void) { return s_ctx.state; } EpbStatus apply_ctrl_last_error(void) { return s_ctx.last_error; } uint32_t apply_ctrl_get_step_count(void) { return s_ctx.step_count; }