#include "state_machine.h" StateHandle state; static bool relay_closed = 0; static bool precharge_closed = 0; static int16_t RELAY_BAT_SIDE_VOLTAGE = 0; static int16_t RELAY_ESC_SIDE_VOLTAGE = 0; static int16_t CURRENT_MEASUREMENT_VOLTAGE = 0; static int16_t timestamp; void sm_init(){ state.current_state = STATE_INACTIVE; state.target_state = STATE_INACTIVE; state.error_source = 0; RELAY_BAT_SIDE_VOLTAGE = module.auxVoltages[0]; RELAY_ESC_SIDE_VOLTAGE = module.auxVoltages[1]; CURRENT_MEASUREMENT_VOLTAGE = module.auxVoltages[2]; } void sm_update(){ sm_handle_ams_in(); switch (state.current_state) { case STATE_INACTIVE: state.current_state = sm_update_inactive(); // monitor only break; case STATE_PRECHARGE: state.current_state = sm_update_precharge(); // set PRECHARGE and turn on cooling at 50% or such break; case STATE_READY: state.current_state = sm_update_ready(); // keep cooling at 50%, get ready to turn on powerground break; case STATE_ACTIVE: state.current_state = sm_update_active(); // set PRECHARGE and turn on cooling at 50% or such break; case STATE_DISCHARGE: state.current_state = sm_update_discharge(); // open the main relay, keep PRECHARGE closed break; case STATE_CHARGING_PRECHARGE: state.current_state = sm_update_charging_precharge(); break; case STATE_CHARGING: state.current_state = sm_update_charging(); // monitor and turn on cooling if needed. break; case STATE_ERROR: state.current_state = sm_update_error(); // enter the correct ERROR state break; } sm_set_relay_positions(state.current_state); } State sm_update_inactive(){ switch (state.target_state) { case STATE_PRECHARGE: return STATE_PRECHARGE; case STATE_CHARGING_PRECHARGE: return STATE_CHARGING_PRECHARGE; default: return STATE_INACTIVE; } } State sm_update_precharge(){ switch (state.target_state) { case STATE_INACTIVE: // if CAN Signal 0000 0000 then immidiete shutdown return STATE_DISCHARGE; case STATE_READY: if (RELAY_BAT_SIDE_VOLTAGE == RELAY_ESC_SIDE_VOLTAGE) return STATE_READY; default: return STATE_PRECHARGE; } } State sm_update_ready(){ switch (state.target_state) { case STATE_ACTIVE: // if CAN Signal 1100 0000 then turn on powerground return STATE_ACTIVE; case STATE_DISCHARGE: // if CAN Signal 0000 0000 then shutdown return STATE_DISCHARGE; default: return STATE_READY; } } State sm_update_active(){ switch (state.target_state) { case STATE_READY: // if CAN Signal 1000 0000 then turn oof powerground but stay ready return STATE_READY; case STATE_DISCHARGE: // if CAN Signal 0000 0000 then shutdown return STATE_DISCHARGE; default: return STATE_ACTIVE; } } State sm_update_discharge(){ switch (state.target_state) { case STATE_INACTIVE: if (RELAY_ESC_SIDE_VOLTAGE == 0) return STATE_INACTIVE; case STATE_PRECHARGE: // if CAN Signal 1000 0000 then get ready return STATE_PRECHARGE; default: return STATE_DISCHARGE; } } State sm_update_charging_precharge(){ switch (state.target_state) { case STATE_CHARGING: return STATE_CHARGING; case STATE_DISCHARGE: return STATE_DISCHARGE; default: return STATE_CHARGING_PRECHARGE; } } State sm_update_charging(){ switch (state.target_state) { case STATE_DISCHARGE: return STATE_DISCHARGE; default: return STATE_CHARGING; } } State sm_update_error(){ switch (state.target_state) { case STATE_DISCHARGE: return STATE_DISCHARGE; default: return STATE_ERROR; } } void sm_set_relay_positions(State current_state){ switch (state.target_state) { case STATE_INACTIVE: sm_set_relay(RELAY_MAIN, 0); sm_set_relay(RELAY_PRECHARGE, 0); break; case STATE_PRECHARGE: sm_set_relay(RELAY_MAIN, 0); sm_set_relay(RELAY_PRECHARGE, 1); break; case STATE_READY: sm_set_relay(RELAY_MAIN, 1); sm_set_relay(RELAY_PRECHARGE, 0); break; case STATE_ACTIVE: sm_set_relay(RELAY_MAIN, 1); sm_set_relay(RELAY_PRECHARGE, 0); case STATE_DISCHARGE: sm_set_relay(RELAY_MAIN, 0); sm_set_relay(RELAY_PRECHARGE, 0); break; case STATE_CHARGING: sm_set_relay(RELAY_MAIN, 1); sm_set_relay(RELAY_PRECHARGE, 1); break; case STATE_ERROR: sm_set_relay(RELAY_MAIN, 0); sm_set_relay(RELAY_PRECHARGE, 0); break; } } void sm_set_relay(Relay relay, bool closed){ GPIO_PinState state = closed ? GPIO_PIN_SET : GPIO_PIN_RESET; switch (relay) { case RELAY_MAIN: HAL_GPIO_WritePin(RELAY_EN_GPIO_Port, RELAY_EN_Pin, state); relay_closed = closed; break; case RELAY_PRECHARGE: HAL_GPIO_WritePin(PRECHARGE_EN_GPIO_Port, PRECHARGE_EN_Pin, state); precharge_closed = closed; break; } } void sm_handle_ams_in(){ uint8_t data[2] = {}; can_handle_recieve_command(&data); switch (data[0]) { case 0b00000000: if (state.current_state != STATE_INACTIVE){ PWM_powerground_control(0); state.target_state = STATE_DISCHARGE; } break; case 0b10000000: if (state.target_state == STATE_INACTIVE || state.target_state == STATE_DISCHARGE){ PWM_powerground_control(0); state.target_state = STATE_PRECHARGE; } else if (state.target_state == STATE_ACTIVE){ PWM_powerground_control(0); state.target_state = STATE_READY; } break; case 0b11000000: PWM_powerground_control(data[1]); state.target_state = STATE_ACTIVE; // READY -> ACTIVE break; } } void sm_set_error(ErrorKind error_kind, bool is_errored){} void sm_check_errors(){ if (module.status.THSD == 1) { state.error_type.bms_overtemp = 1; } if (RELAY_BAT_SIDE_VOLTAGE < 40){ state.error_source = (1 << 10); } } void sm_charging_check(){ if (RELAY_BAT_SIDE_VOLTAGE < RELAY_ESC_SIDE_VOLTAGE && timestamp == 0) timestamp = HAL_GetTick() + 5000; if (timestamp < HAL_GetTick()) state.target_state = STATE_CHARGING_PRECHARGE; }