Drive Finite State Machine (drive_fsm)
Here’s what’s covered on this page:
State Diagrams
Sample Sequences/Paths through the FSM
State Diagrams
Here’s what the current state machine looks like:
Note that the state “Set MCO” refers to the state “set motor control output”. A general use pattern for this state machine will start at neutral precharged and transition to set precharge → neutral precharged → set MCO → drive/reverse. The parking state may be removed soon and the fsm can transition to the fault state if it detects that something unexpected has occurred.
As of June 6, 2021, the fault state always recovers to neutral precharge regardless of brake state but the foundation for dividing the recover brake pressed transition and recover brake released transitions remains intact for future implementation when the brake sensor is complete. (More on this later.)
For Reference: Here are the changes made during [SOFT-362] ebrake removal (June 16, 2021)
Sample Sequences/Paths through the FSM
We will be ignoring the majority of the CAN messages TX/RXed during the operation of drive_fsm and will instead focus on how the fsm transitions.
Here’s an example of the car transitioning from neutral → parking → drive:
After calling drive_fsm_init(), the fsm begins in the neutral discharged state with current state = DRIVE_STATE_NEUTRAL
.
Next, an external DRIVE_FSM_INPUT_EVENT_PARKING
event is raised and is passed into drive_fsm_process_event()
. This causes the destination state to be set to parking.
The fsm then transitions into the set precharge state. The corresponding callback function for set prechargeprv_drive_fsm_set_precharge_output
is called and will raise a DRIVE_FSM_INPUT_EVENT_DISCHARGE_COMPLETED
event because the car’s destination state is set to parking.
This newly raised discharge complete event is processed by the drive_fsm which transitions into the parking state.
Having reached the parking state, prv_drive_fsm_destination_output()
is called and the current state is updated to match the destination state. In this case, the destination is parking and thus the current state is updated to be parking. The fsm broadcasts an DRIVE_FSM_OUTPUT_EVENT_PARKING
event and we have officially transitioned from neutral → parking.
The driver now switches gear to the drive state.
DRIVE_FSM_INPUT_EVENT_DRIVE
is passed into drive_fsm_process_event()
. The destination state is set to drive and the fsm transitions from the parking state to set precharge. This time, since the destination is not parking, set precharge transitions to the neutral precharged state. Neutral precharge’s callback function checks if the destination is set to neutral. It is not and DRIVE_FSM_INPUT_EVENT_DRIVE
is re-raised as an event.
The fsm now transitions to the set MCO state and essentially raises a DRIVE_FSM_INPUT_EVENT_MCI_SET_OUTPUT_DESTINATION_DRIVE
causing the fsm to make its final transition into the drive state, and (similar to the parking state) the current state is updated to match the destination state and the fsm broadcasts a DRIVE_FSM_OUTPUT_EVENT_DRIVE
signalling that we have completed parking → drive.
Note that inputting another DRIVE_FSM_INPUT_EVENT_DRIVE
at this point would not have any effect on the fsm.
If the driver changes his gear to either neutral or reverse, inputting a DRIVE_FSM_INPUT_EVENT_NEUTRAL
or DRIVE_FSM_INPUT_EVENT_REVERSE
respectively, a similar process ensues, eventually culminating in the destination state to be reached and the current state updated to match the destination state.
That leaves one more unexplored state – the fault state.
Let’s examine an example of failing the set MCO state (also called MCI output).
If we start at the neutral precharged state and process a DRIVE_FSM_INPUT_EVENT_DRIVE
event, the fsm transitions to the set MCO state.
Now, if we run MS_TEST_HELPER_CAN_TX_RX_WITH_ACK(CENTRE_CONSOLE_EVENT_CAN_TX, CENTRE_CONSOLE_EVENT_CAN_RX);
for NUM_MCI_OUTPUT_TX_RETRIES
times, we can expect to find that we receive CAN_ACK_STATUS_INVALID
from MCI, resulting in a DRIVE_FSM_INPUT_EVENT_FAULT
to be raised.
This sends drive_fsm into a fault state and subsequently calls the following callback function:
static void prv_fault_output(Fsm *fsm, const Event *e, void *context) {
DriveFsmStorage *storage = (DriveFsmStorage *)context;
FaultReason reason = { .fields = { .area = EE_CONSOLE_FAULT_AREA_DRIVE_FSM, .reason = e->data } };
CAN_TRANSMIT_DISCHARGE_PRECHARGE();
CAN_TRANSMIT_STATE_TRANSITION_FAULT(reason.fields.area, reason.fields.reason);
EventId id = (TEST_PARKING_BRAKE_STATE ==
PARKING_BRAKE_STATE_PRESSED) // To be updated when parking sensor added
? DRIVE_FSM_INPUT_EVENT_FAULT_RECOVER_BRAKE_PRESSED
: DRIVE_FSM_INPUT_EVENT_FAULT_RECOVER_RELEASED;
storage->destination = NUM_DRIVE_STATES;
event_raise(id, 0);
}
NOTE: TEST_PARKING_BRAKE_STATE
is defined as PARKING_BRAKE_STATE_PRESSED
for now and should be updated when the parking sensor is implemented.
The relevant lines are lines 6-11.
If the parking brake state is pressed, DRIVE_FSM_INPUT_EVENT_FAULT_RECOVER_BRAKE_PRESSED
should be raised as an event. Otherwise, DRIVE_FSM_INPUT_EVENT_FAULT_RECOVER_RELEASED
should be raised.
Currently, both events cause the fsm to transition from the fault state into the neutral precharge state. It used to be that the fsm would recover to the parking state if DRIVE_FSM_INPUT_EVENT_FAULT_RECOVER_BRAKE_PRESSED
was processed in the fault state. drive_fsm.c has been intentionally left with this distinguishment between recovering with brakes pressed/released in case it is helpful in future iterations of drive_fsm.
For now, faulting will always recover back to neutral precharge, the initial state, completing the example process of entering and recovering from the fault state.