From b35e60e72058343ae18755af6abbda8615964b58 Mon Sep 17 00:00:00 2001 From: Firas Hamdi Date: Tue, 14 May 2024 09:56:03 +0200 Subject: [PATCH 1/3] cpu/samd5x: add CAN error codes --- cpu/samd5x/include/candev_samd5x.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cpu/samd5x/include/candev_samd5x.h b/cpu/samd5x/include/candev_samd5x.h index 3e065f93d8..6e6583e6bd 100644 --- a/cpu/samd5x/include/candev_samd5x.h +++ b/cpu/samd5x/include/candev_samd5x.h @@ -75,6 +75,16 @@ extern "C" { #define CANDEV_SAMD5X_MAX_TX_BUFFER 32 #define CANDEV_SAMD5X_MSG_RAM_MAX_SIZE 448 +/* SAMD5x CAN controller error codes (values from datasheet section 39.8.14) */ +#define CANDEV_SAMD5X_NO_ERROR 0 +#define CANDEV_SAMD5X_STUFF_ERROR 1 +#define CANDEV_SAMD5X_FORM_ERROR 2 +#define CANDEV_SAMD5X_ACK_ERROR 3 +#define CANDEV_SAMD5X_BIT1_ERROR 4 +#define CANDEV_SAMD5X_BIT0_ERROR 5 +#define CANDEV_SAMD5X_CRC_ERROR 6 +#define CANDEV_SAMD5X_NO_CHANGE_ERROR 7 + /** * @brief CAN device configuration descriptor */ From 59c9f68307f708c62974b6b729c01055158f4343 Mon Sep 17 00:00:00 2001 From: Firas Hamdi Date: Tue, 14 May 2024 10:08:41 +0200 Subject: [PATCH 2/3] cpu/samd5x: Enable and handle errors interrupts --- cpu/samd5x/periph/can.c | 144 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/cpu/samd5x/periph/can.c b/cpu/samd5x/periph/can.c index 21acb0ac79..d549e1e135 100644 --- a/cpu/samd5x/periph/can.c +++ b/cpu/samd5x/periph/can.c @@ -386,6 +386,8 @@ static int _init(candev_t *candev) dev->conf->can->IE.reg |= CAN_IE_RF0NE | CAN_IE_RF1NE; /* Enable transmission events interrupts */ dev->conf->can->IE.reg |= CAN_IE_TEFNE; + /* Enable errors interrupts */ + dev->conf->can->IE.reg |= CAN_IE_PEDE | CAN_IE_PEAE | CAN_IE_BOE | CAN_IE_EWE | CAN_IE_EPE; /* Enable the interrupt lines */ dev->conf->can->ILE.reg = CAN_ILE_EINT0 | CAN_ILE_EINT1; @@ -779,6 +781,148 @@ static void _isr(candev_t *candev) } } + static uint8_t last_error_code = 0; + /* Interrupt triggered due to protocol error in data phase */ + if (irq_reg & CAN_IR_PED) { + DEBUG_PUTS("protocol error in data phase"); + dev->conf->can->IR.reg |= CAN_IR_PED; + /* Extract the Tx and Rx error counters */ + uint8_t tx_err_cnt = (uint8_t)dev->conf->can->ECR.bit.TEC; + DEBUG("tx error counter = %u\n", tx_err_cnt); + uint8_t rx_err_cnt = (uint8_t)dev->conf->can->ECR.bit.REC; + DEBUG("rx error counter = %u\n", rx_err_cnt); + /* Check the CAN error type */ + uint8_t error_code = (uint8_t)dev->conf->can->PSR.bit.LEC; + DEBUG("error code = %u\n", error_code); + if (error_code == CANDEV_SAMD5X_NO_CHANGE_ERROR) { + error_code = last_error_code; + } + + switch (error_code) { + case CANDEV_SAMD5X_STUFF_ERROR: + DEBUG_PUTS("STUFF error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_RX_ERROR, NULL); + } + break; + case CANDEV_SAMD5X_FORM_ERROR: + DEBUG_PUTS("FORM error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_RX_ERROR, NULL); + } + break; + case CANDEV_SAMD5X_ACK_ERROR: + DEBUG_PUTS("ACK error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_TX_ERROR, NULL); + } + break; + case CANDEV_SAMD5X_BIT1_ERROR: + DEBUG_PUTS("BIT1 error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_TX_ERROR, NULL); + } + break; + case CANDEV_SAMD5X_BIT0_ERROR: + DEBUG_PUTS("BIT0 error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_TX_ERROR, NULL); + } + break; + case CANDEV_SAMD5X_CRC_ERROR: + DEBUG_PUTS("CRC error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_RX_ERROR, NULL); + } + break; + default: + break; + } + + last_error_code = error_code; + } + + /* Interrupt triggered due to protocol error in arbitration phase */ + if (irq_reg & CAN_IR_PEA) { + DEBUG_PUTS("protocol error in arbitration phase"); + dev->conf->can->IR.reg |= CAN_IR_PEA; + /* Extract the Tx and Rx error counters */ + uint8_t tx_err_cnt = (uint8_t)dev->conf->can->ECR.bit.TEC; + DEBUG("tx error counter = %u\n", tx_err_cnt); + uint8_t rx_err_cnt = (uint8_t)dev->conf->can->ECR.bit.REC; + DEBUG("rx error counter = %u\n", rx_err_cnt); + /* Check the CAN error type */ + uint8_t error_code = (uint8_t)dev->conf->can->PSR.bit.LEC; + DEBUG("error code = %u\n", error_code); + if (error_code == CANDEV_SAMD5X_NO_CHANGE_ERROR) { + error_code = last_error_code; + } + + switch (error_code) { + case CANDEV_SAMD5X_STUFF_ERROR: + DEBUG_PUTS("STUFF error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_RX_ERROR, NULL); + } + break; + case CANDEV_SAMD5X_FORM_ERROR: + DEBUG_PUTS("FORM error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_RX_ERROR, NULL); + } + break; + case CANDEV_SAMD5X_ACK_ERROR: + DEBUG_PUTS("ACK error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_TX_ERROR, NULL); + } + break; + case CANDEV_SAMD5X_BIT0_ERROR: + DEBUG_PUTS("BIT0 error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_TX_ERROR, NULL); + } + break; + case CANDEV_SAMD5X_CRC_ERROR: + DEBUG_PUTS("CRC error"); + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_RX_ERROR, NULL); + } + break; + default: + break; + } + + last_error_code = error_code; + } + + /* Interrupt triggered due to Bus_Off status */ + if (irq_reg & CAN_IR_BO) { + DEBUG_PUTS("Bus off"); + dev->conf->can->IR.reg |= CAN_IR_BO; + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_BUS_OFF, NULL); + } + } + + /* Interrupt triggered due to Error warning status */ + if (irq_reg & CAN_IR_EW) { + DEBUG_PUTS("Error warning"); + dev->conf->can->IR.reg |= CAN_IR_EW; + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_ERROR_WARNING, NULL); + } + } + + /* Interrupt triggered due to Error passive status */ + if (irq_reg & CAN_IR_EP) { + DEBUG_PUTS("Error Passive"); + dev->conf->can->IR.reg |= CAN_IR_EP; + if (dev->candev.event_callback) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_ERROR_PASSIVE, NULL); + } + } + /* Enable the peripheral's interrupt */ if (dev->conf->can == CAN0) { NVIC_EnableIRQ(CAN0_IRQn); From 31003bf3236e1ebab5cc6ad6f2bfe1c93328be02 Mon Sep 17 00:00:00 2001 From: Firas Hamdi Date: Tue, 14 May 2024 10:13:41 +0200 Subject: [PATCH 3/3] tests/drivers/candev: add debug output concerning errors --- tests/drivers/candev/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/drivers/candev/main.c b/tests/drivers/candev/main.c index 48f778813a..6376d0a96d 100644 --- a/tests/drivers/candev/main.c +++ b/tests/drivers/candev/main.c @@ -256,12 +256,15 @@ static void _can_event_callback(candev_t *dev, candev_event_t event, void *arg) DEBUG("_can_event: CANDEV_EVENT_RX_ERROR\n"); break; case CANDEV_EVENT_BUS_OFF: + DEBUG("_can_event: CANDEV_EVENT_BUS_OFF\n"); dev->state = CAN_STATE_BUS_OFF; break; case CANDEV_EVENT_ERROR_PASSIVE: + DEBUG("_can_event: CANDEV_EVENT_ERROR_PASSIVE\n"); dev->state = CAN_STATE_ERROR_PASSIVE; break; case CANDEV_EVENT_ERROR_WARNING: + DEBUG("_can_event: CANDEV_EVENT_ERROR_WARNING\n"); dev->state = CAN_STATE_ERROR_WARNING; break; default: