Drive Finite State Machine (drive_fsm)

Here’s what’s covered on this page:

  1. State Diagrams

  2. Sample Sequences/Paths through the FSM

State Diagrams

Here’s what the current state machine looks like:

Figure 1: Current drive_fsm state diagram

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)

Figure 2: Changes made during ebrake removal (you can probably ignore this)

 

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.