openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

274 lines
11 KiB

#include "fdcan_declarations.h"
FDCAN_GlobalTypeDef *cans[CANS_ARRAY_SIZE] = {FDCAN1, FDCAN2, FDCAN3};
static bool can_set_speed(uint8_t can_number) {
bool ret = true;
FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number);
uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number);
ret &= llcan_set_speed(
FDCANx,
bus_config[bus_number].can_speed,
bus_config[bus_number].can_data_speed,
bus_config[bus_number].canfd_non_iso,
can_loopback,
(unsigned int)(can_silent) & (1U << can_number)
);
return ret;
}
void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number) {
static uint32_t last_reset = 0U;
uint32_t time = microsecond_timer_get();
// Resetting CAN core is a slow blocking operation, limit frequency
if (get_ts_elapsed(time, last_reset) > 100000U) { // 10 Hz
can_health[can_number].can_core_reset_cnt += 1U;
can_health[can_number].total_tx_lost_cnt += (FDCAN_TX_FIFO_EL_CNT - (FDCANx->TXFQS & FDCAN_TXFQS_TFFL)); // TX FIFO msgs will be lost after reset
llcan_clear_send(FDCANx);
last_reset = time;
}
}
void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) {
uint8_t can_irq_number[3][2] = {
{ FDCAN1_IT0_IRQn, FDCAN1_IT1_IRQn },
{ FDCAN2_IT0_IRQn, FDCAN2_IT1_IRQn },
{ FDCAN3_IT0_IRQn, FDCAN3_IT1_IRQn },
};
FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number);
uint32_t psr_reg = FDCANx->PSR;
uint32_t ecr_reg = FDCANx->ECR;
can_health[can_number].bus_off = ((psr_reg & FDCAN_PSR_BO) >> FDCAN_PSR_BO_Pos);
can_health[can_number].bus_off_cnt += can_health[can_number].bus_off;
can_health[can_number].error_warning = ((psr_reg & FDCAN_PSR_EW) >> FDCAN_PSR_EW_Pos);
can_health[can_number].error_passive = ((psr_reg & FDCAN_PSR_EP) >> FDCAN_PSR_EP_Pos);
can_health[can_number].last_error = ((psr_reg & FDCAN_PSR_LEC) >> FDCAN_PSR_LEC_Pos);
if ((can_health[can_number].last_error != 0U) && (can_health[can_number].last_error != 7U)) {
can_health[can_number].last_stored_error = can_health[can_number].last_error;
}
can_health[can_number].last_data_error = ((psr_reg & FDCAN_PSR_DLEC) >> FDCAN_PSR_DLEC_Pos);
if ((can_health[can_number].last_data_error != 0U) && (can_health[can_number].last_data_error != 7U)) {
can_health[can_number].last_data_stored_error = can_health[can_number].last_data_error;
}
can_health[can_number].receive_error_cnt = ((ecr_reg & FDCAN_ECR_REC) >> FDCAN_ECR_REC_Pos);
can_health[can_number].transmit_error_cnt = ((ecr_reg & FDCAN_ECR_TEC) >> FDCAN_ECR_TEC_Pos);
can_health[can_number].irq0_call_rate = interrupts[can_irq_number[can_number][0]].call_rate;
can_health[can_number].irq1_call_rate = interrupts[can_irq_number[can_number][1]].call_rate;
if (ir_reg != 0U) {
// Clear error interrupts
FDCANx->IR |= (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L);
can_health[can_number].total_error_cnt += 1U;
// Check for RX FIFO overflow
if ((ir_reg & (FDCAN_IR_RF0L)) != 0U) {
can_health[can_number].total_rx_lost_cnt += 1U;
}
// Cases:
// 1. while multiplexing between buses 1 and 3 we are getting ACK errors that overwhelm CAN core, by resetting it recovers faster
// 2. H7 gets stuck in bus off recovery state indefinitely
if ((((can_health[can_number].last_error == CAN_ACK_ERROR) || (can_health[can_number].last_data_error == CAN_ACK_ERROR)) && (can_health[can_number].transmit_error_cnt > 127U)) ||
((ir_reg & FDCAN_IR_BO) != 0U)) {
can_clear_send(FDCANx, can_number);
}
}
}
// ***************************** CAN *****************************
// FDFDCANx_IT1 IRQ Handler (TX)
void process_can(uint8_t can_number) {
if (can_number != 0xffU) {
ENTER_CRITICAL();
FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number);
uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number);
FDCANx->IR |= FDCAN_IR_TFE; // Clear Tx FIFO Empty flag
if ((FDCANx->TXFQS & FDCAN_TXFQS_TFQF) == 0U) {
CANPacket_t to_send;
if (can_pop(can_queues[bus_number], &to_send)) {
if (can_check_checksum(&to_send)) {
can_health[can_number].total_tx_cnt += 1U;
uint32_t TxFIFOSA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET) + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE);
// get the index of the next TX FIFO element (0 to FDCAN_TX_FIFO_EL_CNT - 1)
uint32_t tx_index = (FDCANx->TXFQS >> FDCAN_TXFQS_TFQPI_Pos) & 0x1FU;
// only send if we have received a packet
canfd_fifo *fifo;
fifo = (canfd_fifo *)(TxFIFOSA + (tx_index * FDCAN_TX_FIFO_EL_SIZE));
fifo->header[0] = (to_send.extended << 30) | ((to_send.extended != 0U) ? (to_send.addr) : (to_send.addr << 18));
// If canfd_auto is set, outgoing packets will be automatically sent as CAN-FD if an incoming CAN-FD packet was seen
bool fd = bus_config[can_number].canfd_auto ? bus_config[can_number].canfd_enabled : (bool)(to_send.fd > 0U);
uint32_t canfd_enabled_header = fd ? (1UL << 21) : 0UL;
uint32_t brs_enabled_header = bus_config[can_number].brs_enabled ? (1UL << 20) : 0UL;
fifo->header[1] = (to_send.data_len_code << 16) | canfd_enabled_header | brs_enabled_header;
uint8_t data_len_w = (dlc_to_len[to_send.data_len_code] / 4U);
data_len_w += ((dlc_to_len[to_send.data_len_code] % 4U) > 0U) ? 1U : 0U;
for (unsigned int i = 0; i < data_len_w; i++) {
BYTE_ARRAY_TO_WORD(fifo->data_word[i], &to_send.data[i*4U]);
}
FDCANx->TXBAR = (1UL << tx_index);
// Send back to USB
CANPacket_t to_push;
to_push.fd = fd;
to_push.returned = 1U;
to_push.rejected = 0U;
to_push.extended = to_send.extended;
to_push.addr = to_send.addr;
to_push.bus = bus_number;
to_push.data_len_code = to_send.data_len_code;
(void)memcpy(to_push.data, to_send.data, dlc_to_len[to_push.data_len_code]);
can_set_checksum(&to_push);
rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U;
} else {
can_health[can_number].total_tx_checksum_error_cnt += 1U;
}
refresh_can_tx_slots_available();
}
}
EXIT_CRITICAL();
}
}
// FDFDCANx_IT0 IRQ Handler (RX and errors)
// blink blue when we are receiving CAN messages
void can_rx(uint8_t can_number) {
FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number);
uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number);
uint32_t ir_reg = FDCANx->IR;
// Clear all new messages from Rx FIFO 0
FDCANx->IR |= FDCAN_IR_RF0N;
while((FDCANx->RXF0S & FDCAN_RXF0S_F0FL) != 0U) {
can_health[can_number].total_rx_cnt += 1U;
// can is live
pending_can_live = 1;
// get the index of the next RX FIFO element (0 to FDCAN_RX_FIFO_0_EL_CNT - 1)
uint32_t rx_fifo_idx = (uint8_t)((FDCANx->RXF0S >> FDCAN_RXF0S_F0GI_Pos) & 0x3FU);
// Recommended to offset get index by at least +1 if RX FIFO is in overwrite mode and full (datasheet)
if((FDCANx->RXF0S & FDCAN_RXF0S_F0F) == FDCAN_RXF0S_F0F) {
rx_fifo_idx = ((rx_fifo_idx + 1U) >= FDCAN_RX_FIFO_0_EL_CNT) ? 0U : (rx_fifo_idx + 1U);
can_health[can_number].total_rx_lost_cnt += 1U; // At least one message was lost
}
uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET);
CANPacket_t to_push;
canfd_fifo *fifo;
// getting address
fifo = (canfd_fifo *)(RxFIFO0SA + (rx_fifo_idx * FDCAN_RX_FIFO_0_EL_SIZE));
bool canfd_frame = ((fifo->header[1] >> 21) & 0x1U);
bool brs_frame = ((fifo->header[1] >> 20) & 0x1U);
to_push.fd = canfd_frame;
to_push.returned = 0U;
to_push.rejected = 0U;
to_push.extended = (fifo->header[0] >> 30) & 0x1U;
to_push.addr = ((to_push.extended != 0U) ? (fifo->header[0] & 0x1FFFFFFFU) : ((fifo->header[0] >> 18) & 0x7FFU));
to_push.bus = bus_number;
to_push.data_len_code = ((fifo->header[1] >> 16) & 0xFU);
uint8_t data_len_w = (dlc_to_len[to_push.data_len_code] / 4U);
data_len_w += ((dlc_to_len[to_push.data_len_code] % 4U) > 0U) ? 1U : 0U;
for (unsigned int i = 0; i < data_len_w; i++) {
WORD_TO_BYTE_ARRAY(&to_push.data[i*4U], fifo->data_word[i]);
}
can_set_checksum(&to_push);
// forwarding (panda only)
int bus_fwd_num = safety_fwd_hook(bus_number, to_push.addr);
if (bus_fwd_num < 0) {
bus_fwd_num = bus_config[can_number].forwarding_bus;
}
if (bus_fwd_num != -1) {
CANPacket_t to_send;
to_send.fd = to_push.fd;
to_send.returned = 0U;
to_send.rejected = 0U;
to_send.extended = to_push.extended;
to_send.addr = to_push.addr;
to_send.bus = to_push.bus;
to_send.data_len_code = to_push.data_len_code;
(void)memcpy(to_send.data, to_push.data, dlc_to_len[to_push.data_len_code]);
can_set_checksum(&to_send);
can_send(&to_send, bus_fwd_num, true);
can_health[can_number].total_fwd_cnt += 1U;
}
safety_rx_invalid += safety_rx_hook(&to_push) ? 0U : 1U;
ignition_can_hook(&to_push);
led_set(LED_BLUE, true);
rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U;
// Enable CAN FD and BRS if CAN FD message was received
if (!(bus_config[can_number].canfd_enabled) && (canfd_frame)) {
bus_config[can_number].canfd_enabled = true;
}
if (!(bus_config[can_number].brs_enabled) && (brs_frame)) {
bus_config[can_number].brs_enabled = true;
}
// update read index
FDCANx->RXF0A = rx_fifo_idx;
}
// Error handling
if ((ir_reg & (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L)) != 0U) {
update_can_health_pkt(can_number, ir_reg);
}
}
static void FDCAN1_IT0_IRQ_Handler(void) { can_rx(0); }
static void FDCAN1_IT1_IRQ_Handler(void) { process_can(0); }
static void FDCAN2_IT0_IRQ_Handler(void) { can_rx(1); }
static void FDCAN2_IT1_IRQ_Handler(void) { process_can(1); }
static void FDCAN3_IT0_IRQ_Handler(void) { can_rx(2); }
static void FDCAN3_IT1_IRQ_Handler(void) { process_can(2); }
bool can_init(uint8_t can_number) {
bool ret = false;
REGISTER_INTERRUPT(FDCAN1_IT0_IRQn, FDCAN1_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1)
REGISTER_INTERRUPT(FDCAN1_IT1_IRQn, FDCAN1_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1)
REGISTER_INTERRUPT(FDCAN2_IT0_IRQn, FDCAN2_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2)
REGISTER_INTERRUPT(FDCAN2_IT1_IRQn, FDCAN2_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2)
REGISTER_INTERRUPT(FDCAN3_IT0_IRQn, FDCAN3_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3)
REGISTER_INTERRUPT(FDCAN3_IT1_IRQn, FDCAN3_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3)
if (can_number != 0xffU) {
FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number);
ret &= can_set_speed(can_number);
ret &= llcan_init(FDCANx);
// in case there are queued up messages
process_can(can_number);
}
return ret;
}