CAN Message Handling
This page covers the board logic that sits on top of the CAN driver. It decides what the gateway publishes, which incoming frames should be acted on, and how those commands reach the rest of the firmware.
The core implementation is found in repos/CAN-Gateway/Core/Src/App/Communication/can_comm.c with header definitions in repos/CAN-Gateway/Core/Inc/App/Communication/can_comm.h.
FDCAN Messaging Protocol
The FDCAN interface operates on the vehicle bus using 500 KBps CAN 2.0. Messages use specific base identifiers depending on the board's compiled identity:
- Front Gateway Base ID:
0xA0 - Rear Gateway Base ID:
0xB0
Transmit Frames (TX)
Periodic frame serialization is defined in the set_can_frames function. The frame payload layouts differ depending on DEVICE_TYPE:
| CAN ID | Offset | Compiled Variant | Content / Signal Layout | Code Reference |
|---|---|---|---|---|
| Base + 0 | 0 | Front & Rear | Bytes 0-1: Wheel Speed 1 (RPM) Bytes 2-3: Wheel Speed 2 (RPM) Bytes 4-5: Suspension Travel 1 displacement (scaled by 1000) Bytes 6-7: Suspension Travel 2 displacement (scaled by 1000) | repos/CAN-Gateway/Core/Src/App/Communication/can_comm.c |
| Base + 1 | 1 | Front Gateway | Byte 0: APPS 1 displacement Byte 1: APPS 2 displacement Bytes 2-3: Steering Angle (scaled by 100) Byte 4: Ready-To-Drive (R2D) Button state (0/1) | repos/CAN-Gateway/Core/Src/App/Communication/can_comm.c |
| Base + 1 | 1 | Rear Gateway | Bytes 0-1: Brake Pressure (scaled by 100) Bytes 2-3: Junction Box Current 50A Bytes 4-5: Junction Box Current 200A Byte 6: Shutdown Circuit (SDC) state (0/1) | repos/CAN-Gateway/Core/Src/App/Communication/can_comm.c |
| Base + 2 | 2 | Rear Gateway | Bytes 0-1: Flow Rate (mL/s) Bytes 2-3: Temperature Sensor 1 Bytes 4-5: Temperature Sensor 2 | repos/CAN-Gateway/Core/Src/App/Communication/can_comm.c |
| Base + 3 | 3 | Front & Rear | Calibration Frame: Transmitted via send_calibration_values_frame containing current min/max values: APPS1 min/max (Bytes 0-3), APPS2 min/max (Bytes 4-7) | repos/CAN-Gateway/Core/Src/App/Communication/can_comm.c |
Receive Frames (RX Commands)
The receive pathway consumes frames from the driver's ring buffer in the process_can_frames function:
- ID
0x0A2: (Front Gateway only) Sets the R2D LED delay byte incan_input_data.front_nmos.R2D_LED_Delay. - ID
0x0B3: (Rear Gateway only) Controls the R2D buzzer state incan_input_data.rear_nmos.R2D_Buzzer_On_Off. - ID
0x0D0: Calibration command flags for APPS Recalibration (Bit 0), Steering Recalibration (Bit 1), and Suspension Recalibration (Bit 2). - ID
0x4F0: Bootloader Command. If byte 0 matches0x02(Front) or0x03(Rear), the FSM transitions toBOARD_FSM_MODE_BOOTLOADER.
RX Command Extraction
Below is the implementation detail showing how incoming CAN frames are parsed to update the shared can_input_data fields:
switch (current_frame.msg_id)
{
case 0x0A2:
can_input_data.front_nmos.R2D_LED_Delay = current_frame.payload[0];
break;
case 0x0B3:
can_input_data.rear_nmos.R2D_Buzzer_On_Off = current_frame.payload[0];
break;
case 0x4F0:
if (current_frame.payload[0] == (DEVICE_TYPE == DEVICE_FRONT_GATEWAY ? 0x002 : 0x003))
{
FSM_request_bootloader(&board_fsm_driver);
}
break;
case 0x0D0:
can_input_data.dash_buttons.APPS_Recal = GET_INTEL_BIT(current_frame.payload, 0);
can_input_data.dash_buttons.Steering_Recal = GET_INTEL_BIT(current_frame.payload, 1);
can_input_data.dash_buttons.Suspension_Recal = GET_INTEL_BIT(current_frame.payload, 2);
break;
default:
break;
}TIP
If a frame ID does not match any cases in this switch, the code discards it immediately. This bounds the RX processing cost and secures the board against unwanted bus interference.
Source Layout & Integration
- repos/CAN-Gateway/Core/Src/App/Communication/can_comm.c: Owns the packing and unpacking functions.
- repos/CAN-Gateway/Core/Inc/App/Communication/can_driver.h: Declares the base CAN identifier
DEVICE_CAN_BASE_ID(or ).
Every iteration of the main loop calls process_can_helper(), which delegates tasks in this order:
static void process_can_helper(void){
process_can_frames(&can_driver);
CAN_send_frames(&can_driver, HAL_GetTick());
if (can_driver.tx_queue_drain_requested) {
CAN_process_tx_queue(&can_driver, 0);
}
}