FW15/16 CAN Implementation

Background

For MS16 we use a super loop architecture to drive all data transitions through each project. Each controller board has 3 cycle speeds it can transmit and receive at. Our receiving is ISR based and transmitting depends on the CAN TX Mailbox. We also autogenerate rx_structs and tx_structs to hold all messages using YAML files for each project.

Ex: BMS Carrier

image-20240822-045825.png

It begins with the initialization of all storage and modules. Next, the super loop is really simple to implement! TLDR: All we do is call run_can_rx_cycle() and run_can_tx_cycle() and our rx_structs are updated, and tx_structs are transmitted onto the bus.

run_can_rx_cycle() and run_can_tx_cycle() notify CAN_RX and CAN_TX tasks. CAN_RX will flush the rx_queue and update the rx_structs based on the message raw ID. CAN_TX will similarly go through the entire tx_struct and transmit the respective messages. It will then check all CAN watchdogs, which are autogenerated variables that are incremented each time a message is not received and reset when it is.

image-20240822-050436.png

 

CAN Receiving:

 

All incoming CAN messages trigger an interrupt USB_LP_CAN1_RX0_IRQHandler() or USB_LP_CAN1_RX1_IRQHandler() which calls on can_hw_receive, fetching all the data. Within the ISR, we compare the current CAN ID with the CAN filter IDs. If the message is not in the filter, we then push the CAN message to the global rx_queue. The data now waits in the queue to be popped an used, typically by the CAN_RX task.

Incoming CAN msgCAN Receive ISRFilter CAN IDsPush message to rx_queuePop message from rx_queue for access

CAN Transmit:

can_hw_transmituses the STM32’s mailbox to transmit messages. A mailbox is basically a hardware storage unit, and for CAN TX, the mailbox also schedules out transmissions. We attempt 3 times to transmit the given message, but if the mailbox is full, then it suggests that CAN is not connected or another underlying issue exists in the TX process.

Options considered

Proposal: Redesign CAN architecture to be main clock of system, since its the main mode of communication between different boards of the car.

Using the current system, we receive CAN messages using interrupts and just push onto the event queue as normal priority. This means that even critical messages are are some what delayed as they go through the event_processing.

There’s also no semaphores/guards for the global event queue. Would be fine single threaded but not if we move to SafeRTOS.

 

What would this look like?: Still can use interrupts to catch CAN messages, but separate CAN events from the main event queues. Instead, every incoming message will be logged on separate queue, then main program will only read off the queue every 10Hz or something. Same with transmitting. Transmit will just log on the tx queue, and only transmit at a fixed interval.

 

What this does?: It creates a synchronized system within the main program, only doing tasks at specific intervals of time. Other tasks can based themselves off the CAN tasks to only poll at certain and lifts CPU power off constant polling and other tasks.