summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/stm32/can.c198
-rw-r--r--ports/stm32/can.h84
-rw-r--r--ports/stm32/fdcan.c243
-rw-r--r--ports/stm32/main.c6
-rw-r--r--ports/stm32/modpyb.c2
-rw-r--r--ports/stm32/pyb_can.c202
-rw-r--r--ports/stm32/pyb_can.h58
7 files changed, 431 insertions, 362 deletions
diff --git a/ports/stm32/can.c b/ports/stm32/can.c
index 490faeba7..47385e0fa 100644
--- a/ports/stm32/can.c
+++ b/ports/stm32/can.c
@@ -28,29 +28,15 @@
#include "py/mperrno.h"
#include "py/mphal.h"
#include "can.h"
+#include "pyb_can.h"
#include "irq.h"
#if MICROPY_HW_ENABLE_CAN
-void can_init0(void) {
- for (uint i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_can_obj_all)); i++) {
- MP_STATE_PORT(pyb_can_obj_all)[i] = NULL;
- }
-}
-
-void can_deinit_all(void) {
- for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_can_obj_all)); i++) {
- pyb_can_obj_t *can_obj = MP_STATE_PORT(pyb_can_obj_all)[i];
- if (can_obj != NULL) {
- can_deinit(can_obj);
- }
- }
-}
-
#if !MICROPY_HW_ENABLE_FDCAN
-bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) {
- CAN_InitTypeDef *init = &can_obj->can.Init;
+bool can_init(CAN_HandleTypeDef *can, int can_id, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) {
+ CAN_InitTypeDef *init = &can->Init;
init->Mode = mode << 4; // shift-left so modes fit in a small-int
init->Prescaler = prescaler;
init->SJW = ((sjw - 1) & 3) << 24;
@@ -67,7 +53,7 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_
uint32_t sce_irq = 0;
const machine_pin_obj_t *pins[2];
- switch (can_obj->can_id) {
+ switch (can_id) {
#if defined(MICROPY_HW_CAN1_TX)
case PYB_CAN_1:
CANx = CAN1;
@@ -107,21 +93,16 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_
uint32_t pin_mode = MP_HAL_PIN_MODE_ALT;
uint32_t pin_pull = MP_HAL_PIN_PULL_UP;
for (int i = 0; i < 2; i++) {
- if (!mp_hal_pin_config_alt(pins[i], pin_mode, pin_pull, AF_FN_CAN, can_obj->can_id)) {
+ if (!mp_hal_pin_config_alt(pins[i], pin_mode, pin_pull, AF_FN_CAN, can_id)) {
return false;
}
}
// init CANx
- can_obj->can.Instance = CANx;
- HAL_CAN_Init(&can_obj->can);
+ can->Instance = CANx;
+ HAL_CAN_Init(can);
- can_obj->is_enabled = true;
- can_obj->num_error_warning = 0;
- can_obj->num_error_passive = 0;
- can_obj->num_bus_off = 0;
-
- __HAL_CAN_ENABLE_IT(&can_obj->can, CAN_IT_ERR | CAN_IT_BOF | CAN_IT_EPV | CAN_IT_EWG);
+ __HAL_CAN_ENABLE_IT(can, CAN_IT_ERR | CAN_IT_BOF | CAN_IT_EPV | CAN_IT_EWG);
NVIC_SetPriority(sce_irq, IRQ_PRI_CAN);
HAL_NVIC_EnableIRQ(sce_irq);
@@ -129,10 +110,9 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_
return true;
}
-void can_deinit(pyb_can_obj_t *self) {
- self->is_enabled = false;
- HAL_CAN_DeInit(&self->can);
- if (self->can.Instance == CAN1) {
+void can_deinit(CAN_HandleTypeDef *can) {
+ HAL_CAN_DeInit(can);
+ if (can->Instance == CAN1) {
HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn);
HAL_NVIC_DisableIRQ(CAN1_RX1_IRQn);
HAL_NVIC_DisableIRQ(CAN1_SCE_IRQn);
@@ -140,7 +120,7 @@ void can_deinit(pyb_can_obj_t *self) {
__HAL_RCC_CAN1_RELEASE_RESET();
__HAL_RCC_CAN1_CLK_DISABLE();
#if defined(CAN2)
- } else if (self->can.Instance == CAN2) {
+ } else if (can->Instance == CAN2) {
HAL_NVIC_DisableIRQ(CAN2_RX0_IRQn);
HAL_NVIC_DisableIRQ(CAN2_RX1_IRQn);
HAL_NVIC_DisableIRQ(CAN2_SCE_IRQn);
@@ -149,7 +129,7 @@ void can_deinit(pyb_can_obj_t *self) {
__HAL_RCC_CAN2_CLK_DISABLE();
#endif
#if defined(CAN3)
- } else if (self->can.Instance == CAN3) {
+ } else if (can->Instance == CAN3) {
HAL_NVIC_DisableIRQ(CAN3_RX0_IRQn);
HAL_NVIC_DisableIRQ(CAN3_RX1_IRQn);
HAL_NVIC_DisableIRQ(CAN3_SCE_IRQn);
@@ -160,7 +140,19 @@ void can_deinit(pyb_can_obj_t *self) {
}
}
-void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t bank) {
+void can_disable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo) {
+ __HAL_CAN_DISABLE_IT(can, ((fifo == CAN_RX_FIFO0) ?
+ (CAN_IT_FMP0 | CAN_IT_FF0 | CAN_IT_FOV0) :
+ (CAN_IT_FMP1 | CAN_IT_FF1 | CAN_IT_FOV1)));
+}
+
+void can_enable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo, bool enable_msg_received) {
+ __HAL_CAN_ENABLE_IT(can, ((fifo == CAN_RX_FIFO0) ?
+ ((enable_msg_received ? CAN_IT_FMP0 : 0) | CAN_IT_FF0 | CAN_IT_FOV0) :
+ ((enable_msg_received ? CAN_IT_FMP1 : 0) | CAN_IT_FF1 | CAN_IT_FOV1)));
+}
+
+void can_clearfilter(CAN_HandleTypeDef *can, uint32_t filter_num, uint8_t bank) {
CAN_FilterConfTypeDef filter;
filter.FilterIdHigh = 0;
@@ -168,18 +160,18 @@ void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t bank) {
filter.FilterMaskIdHigh = 0;
filter.FilterMaskIdLow = 0;
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
- filter.FilterNumber = f;
+ filter.FilterNumber = filter_num;
filter.FilterMode = CAN_FILTERMODE_IDMASK;
filter.FilterScale = CAN_FILTERSCALE_16BIT;
filter.FilterActivation = DISABLE;
filter.BankNumber = bank;
- HAL_CAN_ConfigFilter(&self->can, &filter);
+ HAL_CAN_ConfigFilter(can, &filter);
}
-int can_receive(CAN_HandleTypeDef *can, int fifo, CanRxMsgTypeDef *msg, uint8_t *data, uint32_t timeout_ms) {
+int can_receive(CAN_HandleTypeDef *can, can_rx_fifo_t fifo, CanRxMsgTypeDef *msg, uint8_t *data, uint32_t timeout_ms) {
volatile uint32_t *rfr;
- if (fifo == CAN_FIFO0) {
+ if (fifo == CAN_RX_FIFO0) {
rfr = &can->Instance->RF0R;
} else {
rfr = &can->Instance->RF1R;
@@ -222,13 +214,16 @@ int can_receive(CAN_HandleTypeDef *can, int fifo, CanRxMsgTypeDef *msg, uint8_t
return 0; // success
}
-// We have our own version of CAN transmit so we can handle Timeout=0 correctly.
-HAL_StatusTypeDef CAN_Transmit(CAN_HandleTypeDef *hcan, uint32_t Timeout) {
+// Lightly modified version of HAL CAN_Transmit to handle Timeout=0 correctly
+HAL_StatusTypeDef can_transmit(CAN_HandleTypeDef *hcan, CanTxMsgTypeDef *txmsg, uint8_t *data, uint32_t Timeout) {
uint32_t transmitmailbox;
uint32_t tickstart;
uint32_t rqcpflag = 0;
uint32_t txokflag = 0;
+ hcan->pTxMsg = txmsg;
+ (void)data; // Not needed here, caller has set it up as &tx_msg->Data
+
// Check the parameters
assert_param(IS_CAN_IDTYPE(hcan->pTxMsg->IDE));
assert_param(IS_CAN_RTR(hcan->pTxMsg->RTR));
@@ -312,79 +307,90 @@ HAL_StatusTypeDef CAN_Transmit(CAN_HandleTypeDef *hcan, uint32_t Timeout) {
}
}
-static void can_rx_irq_handler(uint can_id, uint fifo_id) {
- mp_obj_t callback;
- pyb_can_obj_t *self;
- mp_obj_t irq_reason = MP_OBJ_NEW_SMALL_INT(0);
- byte *state;
-
- self = MP_STATE_PORT(pyb_can_obj_all)[can_id - 1];
-
- if (fifo_id == CAN_FIFO0) {
- callback = self->rxcallback0;
- state = &self->rx_state0;
+// Workaround for the __HAL_CAN macros expecting a CAN_HandleTypeDef which we
+// don't have in the ISR. Using this "fake" struct instead of CAN_HandleTypeDef
+// so it's not possible to accidentally call an API that uses one of the other
+// fields in the structure.
+typedef struct {
+ CAN_TypeDef *Instance;
+} fake_handle_t;
+
+static void can_rx_irq_handler(uint can_id, CAN_TypeDef *instance, can_rx_fifo_t fifo) {
+ uint32_t full_flag, full_int, overrun_flag, overrun_int, pending_int;
+
+ const fake_handle_t handle = {
+ .Instance = instance,
+ };
+
+ if (fifo == CAN_RX_FIFO0) {
+ full_flag = CAN_FLAG_FF0;
+ full_int = CAN_IT_FF0;
+ overrun_flag = CAN_FLAG_FOV0;
+ overrun_int = CAN_IT_FOV0;
+ pending_int = CAN_IT_FMP0;
} else {
- callback = self->rxcallback1;
- state = &self->rx_state1;
+ full_flag = CAN_FLAG_FF1;
+ full_int = CAN_IT_FF1;
+ overrun_flag = CAN_FLAG_FOV1;
+ overrun_int = CAN_IT_FOV1;
+ pending_int = CAN_IT_FMP1;
}
- switch (*state) {
- case RX_STATE_FIFO_EMPTY:
- __HAL_CAN_DISABLE_IT(&self->can, (fifo_id == CAN_FIFO0) ? CAN_IT_FMP0 : CAN_IT_FMP1);
- irq_reason = MP_OBJ_NEW_SMALL_INT(0);
- *state = RX_STATE_MESSAGE_PENDING;
- break;
- case RX_STATE_MESSAGE_PENDING:
- __HAL_CAN_DISABLE_IT(&self->can, (fifo_id == CAN_FIFO0) ? CAN_IT_FF0 : CAN_IT_FF1);
- __HAL_CAN_CLEAR_FLAG(&self->can, (fifo_id == CAN_FIFO0) ? CAN_FLAG_FF0 : CAN_FLAG_FF1);
- irq_reason = MP_OBJ_NEW_SMALL_INT(1);
- *state = RX_STATE_FIFO_FULL;
- break;
- case RX_STATE_FIFO_FULL:
- __HAL_CAN_DISABLE_IT(&self->can, (fifo_id == CAN_FIFO0) ? CAN_IT_FOV0 : CAN_IT_FOV1);
- __HAL_CAN_CLEAR_FLAG(&self->can, (fifo_id == CAN_FIFO0) ? CAN_FLAG_FOV0 : CAN_FLAG_FOV1);
- irq_reason = MP_OBJ_NEW_SMALL_INT(2);
- *state = RX_STATE_FIFO_OVERFLOW;
- break;
- case RX_STATE_FIFO_OVERFLOW:
- // This should never happen
- break;
+ bool full = __HAL_CAN_GET_FLAG(&handle, full_flag);
+ bool overrun = __HAL_CAN_GET_FLAG(&handle, overrun_flag);
+
+ // Note: receive interrupt bits are disabled below, and re-enabled by the
+ // higher layer after calling can_receive()
+
+ if (full) {
+ __HAL_CAN_DISABLE_IT(&handle, full_int);
+ __HAL_CAN_CLEAR_FLAG(&handle, full_flag);
+ if (!overrun) {
+ can_irq_handler(can_id, CAN_INT_FIFO_FULL, fifo);
+ }
+ }
+ if (overrun) {
+ __HAL_CAN_DISABLE_IT(&handle, overrun_int);
+ __HAL_CAN_CLEAR_FLAG(&handle, overrun_flag);
+ can_irq_handler(can_id, CAN_INT_FIFO_OVERFLOW, fifo);
}
- pyb_can_handle_callback(self, fifo_id, callback, irq_reason);
+ if (!(full || overrun)) {
+ // Process of elimination, if neither of the above
+ // FIFO status flags are set then message pending interrupt is what fired.
+ __HAL_CAN_DISABLE_IT(&handle, pending_int);
+ can_irq_handler(can_id, CAN_INT_MESSAGE_RECEIVED, fifo);
+ }
}
-static void can_sce_irq_handler(uint can_id) {
- pyb_can_obj_t *self = MP_STATE_PORT(pyb_can_obj_all)[can_id - 1];
- if (self) {
- self->can.Instance->MSR = CAN_MSR_ERRI;
- uint32_t esr = self->can.Instance->ESR;
- if (esr & CAN_ESR_BOFF) {
- ++self->num_bus_off;
- } else if (esr & CAN_ESR_EPVF) {
- ++self->num_error_passive;
- } else if (esr & CAN_ESR_EWGF) {
- ++self->num_error_warning;
- }
+static void can_sce_irq_handler(uint can_id, CAN_TypeDef *instance) {
+ instance->MSR = CAN_MSR_ERRI; // Write to clear ERRIE interrupt
+ uint32_t esr = instance->ESR;
+ if (esr & CAN_ESR_BOFF) {
+ can_irq_handler(can_id, CAN_INT_ERR_BUS_OFF, 0);
+ } else if (esr & CAN_ESR_EPVF) {
+ can_irq_handler(can_id, CAN_INT_ERR_PASSIVE, 0);
+ } else if (esr & CAN_ESR_EWGF) {
+ can_irq_handler(can_id, CAN_INT_ERR_WARNING, 0);
}
}
#if defined(MICROPY_HW_CAN1_TX)
void CAN1_RX0_IRQHandler(void) {
IRQ_ENTER(CAN1_RX0_IRQn);
- can_rx_irq_handler(PYB_CAN_1, CAN_FIFO0);
+ can_rx_irq_handler(PYB_CAN_1, CAN1, CAN_RX_FIFO0);
IRQ_EXIT(CAN1_RX0_IRQn);
}
void CAN1_RX1_IRQHandler(void) {
IRQ_ENTER(CAN1_RX1_IRQn);
- can_rx_irq_handler(PYB_CAN_1, CAN_FIFO1);
+ can_rx_irq_handler(PYB_CAN_1, CAN1, CAN_RX_FIFO1);
IRQ_EXIT(CAN1_RX1_IRQn);
}
void CAN1_SCE_IRQHandler(void) {
IRQ_ENTER(CAN1_SCE_IRQn);
- can_sce_irq_handler(PYB_CAN_1);
+ can_sce_irq_handler(PYB_CAN_1, CAN1);
IRQ_EXIT(CAN1_SCE_IRQn);
}
#endif
@@ -392,19 +398,19 @@ void CAN1_SCE_IRQHandler(void) {
#if defined(MICROPY_HW_CAN2_TX)
void CAN2_RX0_IRQHandler(void) {
IRQ_ENTER(CAN2_RX0_IRQn);
- can_rx_irq_handler(PYB_CAN_2, CAN_FIFO0);
+ can_rx_irq_handler(PYB_CAN_2, CAN2, CAN_RX_FIFO0);
IRQ_EXIT(CAN2_RX0_IRQn);
}
void CAN2_RX1_IRQHandler(void) {
IRQ_ENTER(CAN2_RX1_IRQn);
- can_rx_irq_handler(PYB_CAN_2, CAN_FIFO1);
+ can_rx_irq_handler(PYB_CAN_2, CAN2, CAN_RX_FIFO1);
IRQ_EXIT(CAN2_RX1_IRQn);
}
void CAN2_SCE_IRQHandler(void) {
IRQ_ENTER(CAN2_SCE_IRQn);
- can_sce_irq_handler(PYB_CAN_2);
+ can_sce_irq_handler(PYB_CAN_2, CAN2);
IRQ_EXIT(CAN2_SCE_IRQn);
}
#endif
@@ -412,19 +418,19 @@ void CAN2_SCE_IRQHandler(void) {
#if defined(MICROPY_HW_CAN3_TX)
void CAN3_RX0_IRQHandler(void) {
IRQ_ENTER(CAN3_RX0_IRQn);
- can_rx_irq_handler(PYB_CAN_3, CAN_FIFO0);
+ can_rx_irq_handler(PYB_CAN_3, CAN3, CAN_RX_FIFO0);
IRQ_EXIT(CAN3_RX0_IRQn);
}
void CAN3_RX1_IRQHandler(void) {
IRQ_ENTER(CAN3_RX1_IRQn);
- can_rx_irq_handler(PYB_CAN_3, CAN_FIFO1);
+ can_rx_irq_handler(PYB_CAN_3, CAN3, CAN_RX_FIFO1);
IRQ_EXIT(CAN3_RX1_IRQn);
}
void CAN3_SCE_IRQHandler(void) {
IRQ_ENTER(CAN3_SCE_IRQn);
- can_sce_irq_handler(PYB_CAN_3);
+ can_sce_irq_handler(PYB_CAN_3, CAN3);
IRQ_EXIT(CAN3_SCE_IRQn);
}
#endif
diff --git a/ports/stm32/can.h b/ports/stm32/can.h
index 45ac7187e..3422f4180 100644
--- a/ports/stm32/can.h
+++ b/ports/stm32/can.h
@@ -26,10 +26,17 @@
#ifndef MICROPY_INCLUDED_STM32_CAN_H
#define MICROPY_INCLUDED_STM32_CAN_H
-#include "py/obj.h"
+#include "py/mphal.h"
#if MICROPY_HW_ENABLE_CAN
+// This API provides a higher abstraction layer over the two STM32
+// CAN controller APIs provided by the ST HAL (one for their classic
+// CAN controller, one for their FD CAN controller.)
+
+// There are two implementations, can.c and fdcan.c - only one is included in
+// the build for a given board.
+
#define PYB_CAN_1 (1)
#define PYB_CAN_2 (2)
#define PYB_CAN_3 (3)
@@ -61,31 +68,56 @@ typedef enum _rx_state_t {
RX_STATE_FIFO_OVERFLOW,
} rx_state_t;
-typedef struct _pyb_can_obj_t {
- mp_obj_base_t base;
- mp_obj_t rxcallback0;
- mp_obj_t rxcallback1;
- mp_uint_t can_id : 8;
- bool is_enabled : 1;
- byte rx_state0;
- byte rx_state1;
- uint16_t num_error_warning;
- uint16_t num_error_passive;
- uint16_t num_bus_off;
- CAN_HandleTypeDef can;
-} pyb_can_obj_t;
-
-extern const mp_obj_type_t pyb_can_type;
-
-void can_init0(void);
-void can_deinit_all(void);
-bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart);
-void can_deinit(pyb_can_obj_t *self);
-
-void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t bank);
-int can_receive(CAN_HandleTypeDef *can, int fifo, CanRxMsgTypeDef *msg, uint8_t *data, uint32_t timeout_ms);
-HAL_StatusTypeDef CAN_Transmit(CAN_HandleTypeDef *hcan, uint32_t Timeout);
-void pyb_can_handle_callback(pyb_can_obj_t *self, uint fifo_id, mp_obj_t callback, mp_obj_t irq_reason);
+typedef enum {
+ CAN_INT_MESSAGE_RECEIVED,
+ CAN_INT_FIFO_FULL,
+ CAN_INT_FIFO_OVERFLOW, // These first 3 correspond to pyb.CAN.rxcallback args
+
+ CAN_INT_ERR_BUS_OFF,
+ CAN_INT_ERR_PASSIVE,
+ CAN_INT_ERR_WARNING,
+} can_int_t;
+
+// RX FIFO numbering
+//
+// Note: For traditional CAN peripheral, the values of CAN_FIFO0 and CAN_FIFO1 are the same
+// as this enum. FDCAN macros FDCAN_RX_FIFO0 and FDCAN_RX_FIFO1 are hardware offsets and not the same.
+typedef enum {
+ CAN_RX_FIFO0,
+ CAN_RX_FIFO1,
+} can_rx_fifo_t;
+
+bool can_init(CAN_HandleTypeDef *can, int can_id, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart);
+void can_deinit(CAN_HandleTypeDef *can);
+
+void can_clearfilter(CAN_HandleTypeDef *can, uint32_t filter_num, uint8_t bank);
+int can_receive(CAN_HandleTypeDef *can, can_rx_fifo_t fifo, CanRxMsgTypeDef *msg, uint8_t *data, uint32_t timeout_ms);
+HAL_StatusTypeDef can_transmit(CAN_HandleTypeDef *hcan, CanTxMsgTypeDef *txmsg, uint8_t *data, uint32_t Timeout);
+
+// Disable all CAN receive interrupts for a FIFO
+void can_disable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo);
+
+// Enable CAN receive interrupts for a FIFO.
+//
+// Interrupt for CAN_INT_MESSAGE_RECEIVED is only enabled if enable_msg_received is set.
+void can_enable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo, bool enable_msg_received);
+
+// Implemented in pyb_can.c, called from lower layer
+extern void can_irq_handler(uint can_id, can_int_t interrupt, can_rx_fifo_t fifo);
+
+#if MICROPY_HW_ENABLE_FDCAN
+
+static inline unsigned can_rx_pending(CAN_HandleTypeDef *can, can_rx_fifo_t fifo) {
+ return HAL_FDCAN_GetRxFifoFillLevel(can, fifo == CAN_RX_FIFO0 ? FDCAN_RX_FIFO0 : FDCAN_RX_FIFO1);
+}
+
+#else
+
+static inline unsigned can_rx_pending(CAN_HandleTypeDef *can, can_rx_fifo_t fifo) {
+ return __HAL_CAN_MSG_PENDING(can, fifo == CAN_RX_FIFO0 ? CAN_FIFO0 : CAN_FIFO1);
+}
+
+#endif // MICROPY_HW_ENABLE_FDCAN
#endif // MICROPY_HW_ENABLE_CAN
diff --git a/ports/stm32/fdcan.c b/ports/stm32/fdcan.c
index 04d8135c0..b87ab48f1 100644
--- a/ports/stm32/fdcan.c
+++ b/ports/stm32/fdcan.c
@@ -28,6 +28,7 @@
#include "py/mperrno.h"
#include "py/mphal.h"
#include "can.h"
+#include "pyb_can.h"
#include "irq.h"
#if MICROPY_HW_ENABLE_CAN && MICROPY_HW_ENABLE_FDCAN
@@ -44,9 +45,13 @@
#define FDCAN_ELEMENT_MASK_FIDX (0x7f000000) // Filter Index
#define FDCAN_ELEMENT_MASK_ANMF (0x80000000) // Accepted Non-matching Frame
-#define FDCAN_RX_FIFO0_MASK (FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST | FDCAN_FLAG_RX_FIFO0_FULL | FDCAN_FLAG_RX_FIFO0_NEW_MESSAGE)
-#define FDCAN_RX_FIFO1_MASK (FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST | FDCAN_FLAG_RX_FIFO1_FULL | FDCAN_FLAG_RX_FIFO1_NEW_MESSAGE)
-#define FDCAN_ERROR_STATUS_MASK (FDCAN_FLAG_ERROR_PASSIVE | FDCAN_FLAG_ERROR_WARNING | FDCAN_FLAG_BUS_OFF)
+#define FDCAN_IT_RX_FIFO0_MASK (FDCAN_IT_RX_FIFO0_MESSAGE_LOST | FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO0_NEW_MESSAGE)
+#define FDCAN_IT_RX_FIFO1_MASK (FDCAN_IT_RX_FIFO1_MESSAGE_LOST | FDCAN_IT_RX_FIFO1_FULL | FDCAN_IT_RX_FIFO1_NEW_MESSAGE)
+#define FDCAN_IT_ERROR_STATUS_MASK (FDCAN_IT_ERROR_PASSIVE | FDCAN_IT_ERROR_WARNING | FDCAN_IT_BUS_OFF)
+
+#define FDCAN_IT_RX_NEW_MESSAGE_MASK (FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO1_NEW_MESSAGE)
+#define FDCAN_IT_RX_FULL_MASK (FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO1_FULL)
+#define FDCAN_IT_RX_MESSAGE_LOST_MASK (FDCAN_IT_RX_FIFO0_MESSAGE_LOST | FDCAN_IT_RX_FIFO1_MESSAGE_LOST)
#if defined(STM32H7)
// adaptations for H7 to G4 naming convention in HAL
@@ -63,10 +68,10 @@
// also defined in <PROC>_hal_fdcan.c, but not able to declare extern and reach the variable
const uint8_t DLCtoBytes[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64};
-bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) {
+bool can_init(CAN_HandleTypeDef *can, int can_id, uint32_t mode, uint32_t prescaler, uint32_t sjw, uint32_t bs1, uint32_t bs2, bool auto_restart) {
(void)auto_restart;
- FDCAN_InitTypeDef *init = &can_obj->can.Init;
+ FDCAN_InitTypeDef *init = &can->Init;
// Configure FDCAN with FD frame and BRS support.
init->FrameFormat = FDCAN_FRAME_FD_BRS;
init->Mode = mode;
@@ -94,7 +99,7 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_
// To support 2 FDCAN instances simultaneously, the Message RAM is divided in half by
// setting the second FDCAN memory offset to half the RAM size. With this configuration,
// the maximum words per FDCAN instance is 1280 32-bit words.
- if (can_obj->can_id == PYB_CAN_1) {
+ if (can_id == PYB_CAN_1) {
init->MessageRAMOffset = 0;
} else {
init->MessageRAMOffset = FDCAN_MESSAGE_RAM_SIZE / 2;
@@ -139,7 +144,7 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_
FDCAN_GlobalTypeDef *CANx = NULL;
const machine_pin_obj_t *pins[2];
- switch (can_obj->can_id) {
+ switch (can_id) {
#if defined(MICROPY_HW_CAN1_TX)
case PYB_CAN_1:
CANx = FDCAN1;
@@ -167,39 +172,34 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_
uint32_t pin_mode = MP_HAL_PIN_MODE_ALT;
uint32_t pin_pull = MP_HAL_PIN_PULL_UP;
for (int i = 0; i < 2; ++i) {
- if (!mp_hal_pin_config_alt(pins[i], pin_mode, pin_pull, AF_FN_CAN, can_obj->can_id)) {
+ if (!mp_hal_pin_config_alt(pins[i], pin_mode, pin_pull, AF_FN_CAN, can_id)) {
return false;
}
}
// init CANx
- can_obj->can.Instance = CANx;
+ can->Instance = CANx;
// catch bad configuration errors.
- if (HAL_FDCAN_Init(&can_obj->can) != HAL_OK) {
+ if (HAL_FDCAN_Init(can) != HAL_OK) {
return false;
}
// Disable acceptance of non-matching frames (enabled by default)
- HAL_FDCAN_ConfigGlobalFilter(&can_obj->can, FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);
+ HAL_FDCAN_ConfigGlobalFilter(can, FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);
// The configuration registers are locked after CAN is started.
- HAL_FDCAN_Start(&can_obj->can);
+ HAL_FDCAN_Start(can);
// Reset all filters
for (int f = 0; f < init->StdFiltersNbr; ++f) {
- can_clearfilter(can_obj, f, false);
+ can_clearfilter(can, f, false);
}
for (int f = 0; f < init->ExtFiltersNbr; ++f) {
- can_clearfilter(can_obj, f, true);
+ can_clearfilter(can, f, true);
}
- can_obj->is_enabled = true;
- can_obj->num_error_warning = 0;
- can_obj->num_error_passive = 0;
- can_obj->num_bus_off = 0;
-
- switch (can_obj->can_id) {
+ switch (can_id) {
case PYB_CAN_1:
NVIC_SetPriority(FDCAN1_IT0_IRQn, IRQ_PRI_CAN);
HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn);
@@ -218,22 +218,18 @@ bool can_init(pyb_can_obj_t *can_obj, uint32_t mode, uint32_t prescaler, uint32_
return false;
}
// FDCAN IT 0
- HAL_FDCAN_ConfigInterruptLines(&can_obj->can, FDCAN_IT_GROUP_RX_FIFO0 | FDCAN_IT_GROUP_BIT_LINE_ERROR | FDCAN_IT_GROUP_PROTOCOL_ERROR, FDCAN_INTERRUPT_LINE0);
+ HAL_FDCAN_ConfigInterruptLines(can, FDCAN_IT_GROUP_RX_FIFO0 | FDCAN_IT_GROUP_BIT_LINE_ERROR | FDCAN_IT_GROUP_PROTOCOL_ERROR, FDCAN_INTERRUPT_LINE0);
// FDCAN IT 1
- HAL_FDCAN_ConfigInterruptLines(&can_obj->can, FDCAN_IT_GROUP_RX_FIFO1, FDCAN_INTERRUPT_LINE1);
+ HAL_FDCAN_ConfigInterruptLines(can, FDCAN_IT_GROUP_RX_FIFO1, FDCAN_INTERRUPT_LINE1);
- uint32_t ActiveITs = FDCAN_IT_BUS_OFF | FDCAN_IT_ERROR_WARNING | FDCAN_IT_ERROR_PASSIVE;
- ActiveITs |= FDCAN_IT_RX_FIFO0_NEW_MESSAGE | FDCAN_IT_RX_FIFO1_NEW_MESSAGE;
- ActiveITs |= FDCAN_IT_RX_FIFO0_MESSAGE_LOST | FDCAN_IT_RX_FIFO1_MESSAGE_LOST;
- ActiveITs |= FDCAN_IT_RX_FIFO0_FULL | FDCAN_IT_RX_FIFO1_FULL;
- HAL_FDCAN_ActivateNotification(&can_obj->can, ActiveITs, 0);
+ // Enable error interrupts. RX-related interrupts are enabled via can_enable_rx_interrupts()
+ HAL_FDCAN_ActivateNotification(can, FDCAN_IT_BUS_OFF | FDCAN_IT_ERROR_WARNING | FDCAN_IT_ERROR_PASSIVE, 0);
return true;
}
-void can_deinit(pyb_can_obj_t *self) {
- self->is_enabled = false;
- HAL_FDCAN_DeInit(&self->can);
- if (self->can.Instance == FDCAN1) {
+void can_deinit(FDCAN_HandleTypeDef *can) {
+ HAL_FDCAN_DeInit(can);
+ if (can->Instance == FDCAN1) {
HAL_NVIC_DisableIRQ(FDCAN1_IT0_IRQn);
HAL_NVIC_DisableIRQ(FDCAN1_IT1_IRQn);
// TODO check if FDCAN2 is used.
@@ -241,7 +237,7 @@ void can_deinit(pyb_can_obj_t *self) {
__HAL_RCC_FDCAN_RELEASE_RESET();
__HAL_RCC_FDCAN_CLK_DISABLE();
#if defined(MICROPY_HW_CAN2_TX)
- } else if (self->can.Instance == FDCAN2) {
+ } else if (can->Instance == FDCAN2) {
HAL_NVIC_DisableIRQ(FDCAN2_IT0_IRQn);
HAL_NVIC_DisableIRQ(FDCAN2_IT1_IRQn);
// TODO check if FDCAN2 is used.
@@ -252,25 +248,52 @@ void can_deinit(pyb_can_obj_t *self) {
}
}
-void can_clearfilter(pyb_can_obj_t *self, uint32_t f, uint8_t extid) {
- if (self && self->can.Instance) {
- FDCAN_FilterTypeDef filter = {0};
- if (extid == 1) {
- filter.IdType = FDCAN_EXTENDED_ID;
- } else {
- filter.IdType = FDCAN_STANDARD_ID;
+void can_clearfilter(FDCAN_HandleTypeDef *can, uint32_t f, uint8_t extid) {
+ FDCAN_FilterTypeDef filter = {0};
+ if (extid == 1) {
+ filter.IdType = FDCAN_EXTENDED_ID;
+ } else {
+ filter.IdType = FDCAN_STANDARD_ID;
+ }
+ filter.FilterIndex = f;
+ filter.FilterConfig = FDCAN_FILTER_DISABLE;
+ HAL_FDCAN_ConfigFilter(can, &filter);
+}
+
+void can_disable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo) {
+ HAL_FDCAN_DeactivateNotification(can, (fifo == CAN_RX_FIFO0) ? FDCAN_IT_RX_FIFO0_MASK : FDCAN_IT_RX_FIFO1_MASK);
+}
+
+void can_enable_rx_interrupts(CAN_HandleTypeDef *can, can_rx_fifo_t fifo, bool enable_msg_received) {
+ uint32_t ints = (fifo == CAN_RX_FIFO0) ? FDCAN_IT_RX_FIFO0_MASK : FDCAN_IT_RX_FIFO1_MASK;
+ if (!enable_msg_received) {
+ ints &= FDCAN_IT_RX_NEW_MESSAGE_MASK;
+ }
+ HAL_FDCAN_ActivateNotification(can, ints, 0);
+}
+
+HAL_StatusTypeDef can_transmit(CAN_HandleTypeDef *can, CanTxMsgTypeDef *txmsg, uint8_t *data, uint32_t timeout_ms) {
+ uint32_t start = HAL_GetTick();
+ while (HAL_FDCAN_GetTxFifoFreeLevel(can) == 0) {
+ if (timeout_ms == 0) {
+ mp_raise_OSError(MP_ETIMEDOUT);
+ }
+ // Check for the Timeout
+ if (timeout_ms != HAL_MAX_DELAY) {
+ if (HAL_GetTick() - start >= timeout_ms) {
+ mp_raise_OSError(MP_ETIMEDOUT);
+ }
}
- filter.FilterIndex = f;
- filter.FilterConfig = FDCAN_FILTER_DISABLE;
- HAL_FDCAN_ConfigFilter(&self->can, &filter);
+ MICROPY_EVENT_POLL_HOOK
}
+ return HAL_FDCAN_AddMessageToTxFifoQ(can, txmsg, data);
}
-int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr, uint8_t *data, uint32_t timeout_ms) {
+int can_receive(FDCAN_HandleTypeDef *can, can_rx_fifo_t fifo, FDCAN_RxHeaderTypeDef *hdr, uint8_t *data, uint32_t timeout_ms) {
volatile uint32_t *rxf, *rxa;
uint32_t fl;
- if (fifo == FDCAN_RX_FIFO0) {
+ if (fifo == CAN_RX_FIFO0) {
rxf = &can->Instance->RXF0S;
rxa = &can->Instance->RXF0A;
fl = FDCAN_RXF0S_F0FL;
@@ -293,7 +316,7 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr,
// Get pointer to incoming message
uint32_t index, *address;
- if (fifo == FDCAN_RX_FIFO0) {
+ if (fifo == CAN_RX_FIFO0) {
index = (*rxf & FDCAN_RXF0S_F0GI) >> FDCAN_RXF0S_F0GI_Pos;
#if defined(STM32G4)
address = (uint32_t *)(can->msgRam.RxFIFO0SA + (index * (18U * 4U))); // SRAMCAN_RF0_SIZE bytes, size not configurable
@@ -303,7 +326,6 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr,
} else {
index = (*rxf & FDCAN_RXF1S_F1GI) >> FDCAN_RXF1S_F1GI_Pos;
#if defined(STM32G4)
- // ToDo: test FIFO1, FIFO 0 is ok
address = (uint32_t *)(can->msgRam.RxFIFO1SA + (index * (18U * 4U))); // SRAMCAN_RF1_SIZE bytes, size not configurable
#else
address = (uint32_t *)(can->msgRam.RxFIFO1SA + (index * can->Init.RxFifo1ElmtSize * 4));
@@ -340,117 +362,66 @@ int can_receive(FDCAN_HandleTypeDef *can, int fifo, FDCAN_RxHeaderTypeDef *hdr,
return 0; // success
}
-static void can_rx_irq_handler(uint can_id, uint fifo_id) {
- mp_obj_t callback;
- pyb_can_obj_t *self;
- mp_obj_t irq_reason = MP_OBJ_NEW_SMALL_INT(0);
- byte *state;
-
- self = MP_STATE_PORT(pyb_can_obj_all)[can_id - 1];
-
- CAN_TypeDef *can = self->can.Instance;
-
- uint32_t RxFifo0ITs;
- uint32_t RxFifo1ITs;
- // uint32_t Errors;
- uint32_t ErrorStatusITs;
- uint32_t Psr;
-
- RxFifo0ITs = can->IR & FDCAN_RX_FIFO0_MASK;
- RxFifo0ITs &= can->IE;
- RxFifo1ITs = can->IR & FDCAN_RX_FIFO1_MASK;
- RxFifo1ITs &= can->IE;
- // Errors = (&self->can)->Instance->IR & FDCAN_ERROR_MASK;
- // Errors &= (&self->can)->Instance->IE;
- ErrorStatusITs = can->IR & FDCAN_ERROR_STATUS_MASK;
- ErrorStatusITs &= can->IE;
- Psr = can->PSR;
-
- if (fifo_id == FDCAN_RX_FIFO0) {
- callback = self->rxcallback0;
- state = &self->rx_state0;
- if (RxFifo0ITs & FDCAN_FLAG_RX_FIFO0_NEW_MESSAGE) {
- __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO0_NEW_MESSAGE);
- __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO0_NEW_MESSAGE);
- irq_reason = MP_OBJ_NEW_SMALL_INT(0);
- *state = RX_STATE_MESSAGE_PENDING;
-
- }
- if (RxFifo0ITs & FDCAN_FLAG_RX_FIFO0_FULL) {
- __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO0_FULL);
- __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO0_FULL);
- irq_reason = MP_OBJ_NEW_SMALL_INT(1);
- *state = RX_STATE_FIFO_FULL;
+static void can_rx_irq_handler(uint can_id, CAN_TypeDef *instance, can_rx_fifo_t fifo) {
+ uint32_t ints, rx_fifo_ints, error_ints;
- }
- if (RxFifo0ITs & FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST) {
- __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO0_MESSAGE_LOST);
- __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST);
- irq_reason = MP_OBJ_NEW_SMALL_INT(2);
- *state = RX_STATE_FIFO_OVERFLOW;
- }
+ ints = instance->IR & instance->IE;
+ if (fifo == CAN_RX_FIFO0) {
+ rx_fifo_ints = ints & FDCAN_IT_RX_FIFO0_MASK;
} else {
- callback = self->rxcallback1;
- state = &self->rx_state1;
- if (RxFifo1ITs & FDCAN_FLAG_RX_FIFO1_NEW_MESSAGE) {
- __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO1_NEW_MESSAGE);
- __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO1_NEW_MESSAGE);
- irq_reason = MP_OBJ_NEW_SMALL_INT(0);
- *state = RX_STATE_MESSAGE_PENDING;
+ rx_fifo_ints = ints & FDCAN_IT_RX_FIFO1_MASK;
+ }
+ error_ints = ints & FDCAN_IT_ERROR_STATUS_MASK;
- }
- if (RxFifo1ITs & FDCAN_FLAG_RX_FIFO1_FULL) {
- __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO1_FULL);
- __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO1_FULL);
- irq_reason = MP_OBJ_NEW_SMALL_INT(1);
- *state = RX_STATE_FIFO_FULL;
+ // Disable receive interrupts, re-enabled by higher layer after calling can_receive()
+ // (Note: can't use __HAL_CAN API as only have a CAN_TypeDef, not CAN_HandleTypeDef)
+ instance->IE &= ~rx_fifo_ints;
+ instance->IR = rx_fifo_ints | error_ints;
+ if (rx_fifo_ints) {
+ if (rx_fifo_ints & FDCAN_IT_RX_NEW_MESSAGE_MASK) {
+ can_irq_handler(can_id, CAN_INT_MESSAGE_RECEIVED, fifo);
+ }
+ if (rx_fifo_ints & FDCAN_IT_RX_FULL_MASK) {
+ can_irq_handler(can_id, CAN_INT_FIFO_FULL, fifo);
}
- if (RxFifo1ITs & FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST) {
- __HAL_FDCAN_DISABLE_IT(&self->can, FDCAN_IT_RX_FIFO1_MESSAGE_LOST);
- __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST);
- irq_reason = MP_OBJ_NEW_SMALL_INT(2);
- *state = RX_STATE_FIFO_OVERFLOW;
+ if (rx_fifo_ints & FDCAN_IT_RX_MESSAGE_LOST_MASK) {
+ can_irq_handler(can_id, CAN_INT_FIFO_OVERFLOW, fifo);
}
}
- if (ErrorStatusITs & FDCAN_FLAG_ERROR_WARNING) {
- __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_ERROR_WARNING);
- if (Psr & FDCAN_PSR_EW) {
- irq_reason = MP_OBJ_NEW_SMALL_INT(3);
- // mp_printf(MICROPY_ERROR_PRINTER, "clear warning %08x\n", (can->IR & FDCAN_ERROR_STATUS_MASK));
+ if (error_ints) {
+ uint32_t Psr = instance->PSR;
+
+ if (error_ints & FDCAN_IT_ERROR_WARNING) {
+ if (Psr & FDCAN_PSR_EW) {
+ can_irq_handler(can_id, CAN_INT_ERR_WARNING, 0);
+ }
}
- }
- if (ErrorStatusITs & FDCAN_FLAG_ERROR_PASSIVE) {
- __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_ERROR_PASSIVE);
- if (Psr & FDCAN_PSR_EP) {
- irq_reason = MP_OBJ_NEW_SMALL_INT(4);
- // mp_printf(MICROPY_ERROR_PRINTER, "clear passive %08x\n", (can->IR & FDCAN_ERROR_STATUS_MASK));
+ if (error_ints & FDCAN_IT_ERROR_PASSIVE) {
+ if (Psr & FDCAN_PSR_EP) {
+ can_irq_handler(can_id, CAN_INT_ERR_PASSIVE, 0);
+ }
}
- }
- if (ErrorStatusITs & FDCAN_FLAG_BUS_OFF) {
- __HAL_FDCAN_CLEAR_FLAG(&self->can, FDCAN_FLAG_BUS_OFF);
- if (Psr & FDCAN_PSR_BO) {
- irq_reason = MP_OBJ_NEW_SMALL_INT(5);
- // mp_printf(MICROPY_ERROR_PRINTER, "bus off %08x\n", (can->IR & FDCAN_ERROR_STATUS_MASK));
+ if (error_ints & FDCAN_IT_BUS_OFF) {
+ if (Psr & FDCAN_PSR_BO) {
+ can_irq_handler(can_id, CAN_INT_ERR_BUS_OFF, 0);
+ }
}
}
-
- pyb_can_handle_callback(self, fifo_id, callback, irq_reason);
- // mp_printf(MICROPY_ERROR_PRINTER, "Ints: %08x, %08x, %08x\n", RxFifo0ITs, RxFifo1ITs, ErrorStatusITs);
}
#if defined(MICROPY_HW_CAN1_TX)
void FDCAN1_IT0_IRQHandler(void) {
IRQ_ENTER(FDCAN1_IT0_IRQn);
- can_rx_irq_handler(PYB_CAN_1, FDCAN_RX_FIFO0);
+ can_rx_irq_handler(PYB_CAN_1, FDCAN1, CAN_RX_FIFO0);
IRQ_EXIT(FDCAN1_IT0_IRQn);
}
void FDCAN1_IT1_IRQHandler(void) {
IRQ_ENTER(FDCAN1_IT1_IRQn);
- can_rx_irq_handler(PYB_CAN_1, FDCAN_RX_FIFO1);
+ can_rx_irq_handler(PYB_CAN_1, FDCAN1, CAN_RX_FIFO1);
IRQ_EXIT(FDCAN1_IT1_IRQn);
}
#endif
@@ -458,13 +429,13 @@ void FDCAN1_IT1_IRQHandler(void) {
#if defined(MICROPY_HW_CAN2_TX)
void FDCAN2_IT0_IRQHandler(void) {
IRQ_ENTER(FDCAN2_IT0_IRQn);
- can_rx_irq_handler(PYB_CAN_2, FDCAN_RX_FIFO0);
+ can_rx_irq_handler(PYB_CAN_2, FDCAN2, CAN_RX_FIFO0);
IRQ_EXIT(FDCAN2_IT0_IRQn);
}
void FDCAN2_IT1_IRQHandler(void) {
IRQ_ENTER(FDCAN2_IT1_IRQn);
- can_rx_irq_handler(PYB_CAN_2, FDCAN_RX_FIFO1);
+ can_rx_irq_handler(PYB_CAN_2, FDCAN2, CAN_RX_FIFO1);
IRQ_EXIT(FDCAN2_IT1_IRQn);
}
#endif
diff --git a/ports/stm32/main.c b/ports/stm32/main.c
index ea870ef0d..55dbeed98 100644
--- a/ports/stm32/main.c
+++ b/ports/stm32/main.c
@@ -86,7 +86,7 @@
#include "accel.h"
#include "servo.h"
#include "dac.h"
-#include "can.h"
+#include "pyb_can.h"
#include "subghz.h"
#if MICROPY_PY_THREAD
@@ -536,7 +536,7 @@ soft_reset:
timer_init0();
#if MICROPY_HW_ENABLE_CAN
- can_init0();
+ pyb_can_init0();
#endif
#if MICROPY_HW_ENABLE_USB
@@ -683,7 +683,7 @@ soft_reset_exit:
pyb_i2c_deinit_all();
#endif
#if MICROPY_HW_ENABLE_CAN
- can_deinit_all();
+ pyb_can_deinit_all();
#endif
#if MICROPY_HW_ENABLE_DAC
dac_deinit_all();
diff --git a/ports/stm32/modpyb.c b/ports/stm32/modpyb.c
index 176010a7e..3d8696e3c 100644
--- a/ports/stm32/modpyb.c
+++ b/ports/stm32/modpyb.c
@@ -41,7 +41,7 @@
#include "i2c.h"
#include "spi.h"
#include "uart.h"
-#include "can.h"
+#include "pyb_can.h"
#include "adc.h"
#include "storage.h"
#include "sdcard.h"
diff --git a/ports/stm32/pyb_can.c b/ports/stm32/pyb_can.c
index 181b31738..0d004ecfc 100644
--- a/ports/stm32/pyb_can.c
+++ b/ports/stm32/pyb_can.c
@@ -27,6 +27,7 @@
#include <string.h>
#include <stdlib.h>
+#include "py/obj.h"
#include "py/objarray.h"
#include "py/runtime.h"
#include "py/gc.h"
@@ -35,6 +36,7 @@
#include "py/mperrno.h"
#include "py/mphal.h"
#include "bufhelper.h"
+#include "pyb_can.h"
#include "can.h"
#include "irq.h"
@@ -44,8 +46,6 @@
#define CAN_MAX_DATA_FRAME (64)
-#define CAN_FIFO0 FDCAN_RX_FIFO0
-#define CAN_FIFO1 FDCAN_RX_FIFO1
#define CAN_FILTER_FIFO0 (0)
// Default timings; 125Kbps
@@ -82,21 +82,6 @@
#define CAN2_RX1_IRQn FDCAN2_IT1_IRQn
#endif
-#define CAN_IT_FIFO0_FULL FDCAN_IT_RX_FIFO0_FULL
-#define CAN_IT_FIFO1_FULL FDCAN_IT_RX_FIFO1_FULL
-#define CAN_IT_FIFO0_OVRF FDCAN_IT_RX_FIFO0_MESSAGE_LOST
-#define CAN_IT_FIFO1_OVRF FDCAN_IT_RX_FIFO1_MESSAGE_LOST
-#define CAN_IT_FIFO0_PENDING FDCAN_IT_RX_FIFO0_NEW_MESSAGE
-#define CAN_IT_FIFO1_PENDING FDCAN_IT_RX_FIFO1_NEW_MESSAGE
-#define CAN_FLAG_FIFO0_FULL FDCAN_FLAG_RX_FIFO0_FULL
-#define CAN_FLAG_FIFO1_FULL FDCAN_FLAG_RX_FIFO1_FULL
-#define CAN_FLAG_FIFO0_OVRF FDCAN_FLAG_RX_FIFO0_MESSAGE_LOST
-#define CAN_FLAG_FIFO1_OVRF FDCAN_FLAG_RX_FIFO1_MESSAGE_LOST
-
-#define __HAL_CAN_ENABLE_IT __HAL_FDCAN_ENABLE_IT
-#define __HAL_CAN_DISABLE_IT __HAL_FDCAN_DISABLE_IT
-#define __HAL_CAN_CLEAR_FLAG __HAL_FDCAN_CLEAR_FLAG
-#define __HAL_CAN_MSG_PENDING HAL_FDCAN_GetRxFifoFillLevel
extern const uint8_t DLCtoBytes[16];
#else
@@ -113,21 +98,27 @@ extern const uint8_t DLCtoBytes[16];
#define CAN_MAXIMUM_NBS2 (8)
#define CAN_MINIMUM_TSEG (1)
-#define CAN_IT_FIFO0_FULL CAN_IT_FF0
-#define CAN_IT_FIFO1_FULL CAN_IT_FF1
-#define CAN_IT_FIFO0_OVRF CAN_IT_FOV0
-#define CAN_IT_FIFO1_OVRF CAN_IT_FOV1
-#define CAN_IT_FIFO0_PENDING CAN_IT_FMP0
-#define CAN_IT_FIFO1_PENDING CAN_IT_FMP1
-#define CAN_FLAG_FIFO0_FULL CAN_FLAG_FF0
-#define CAN_FLAG_FIFO1_FULL CAN_FLAG_FF1
-#define CAN_FLAG_FIFO0_OVRF CAN_FLAG_FOV0
-#define CAN_FLAG_FIFO1_OVRF CAN_FLAG_FOV1
-
static uint8_t can2_start_bank = 14;
#endif
+static mp_obj_t pyb_can_deinit(mp_obj_t self_in);
+
+void pyb_can_init0(void) {
+ for (uint i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_can_obj_all)); i++) {
+ MP_STATE_PORT(pyb_can_obj_all)[i] = NULL;
+ }
+}
+
+void pyb_can_deinit_all(void) {
+ for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_can_obj_all)); i++) {
+ pyb_can_obj_t *can_obj = MP_STATE_PORT(pyb_can_obj_all)[i];
+ if (can_obj != NULL) {
+ pyb_can_deinit(MP_OBJ_FROM_PTR(can_obj));
+ }
+ }
+}
+
static void pyb_can_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (!self->is_enabled) {
@@ -283,15 +274,20 @@ static mp_obj_t pyb_can_init_helper(pyb_can_obj_t *self, size_t n_args, const mp
// Init filter banks for classic CAN.
can2_start_bank = args[ARG_num_filter_banks].u_int;
for (int f = 0; f < CAN_MAX_FILTER; f++) {
- can_clearfilter(self, f, can2_start_bank);
+ can_clearfilter(&self->can, f, can2_start_bank);
}
#endif
- if (!can_init(self, args[ARG_mode].u_int, args[ARG_prescaler].u_int, args[ARG_sjw].u_int,
+ if (!can_init(&self->can, self->can_id, args[ARG_mode].u_int, args[ARG_prescaler].u_int, args[ARG_sjw].u_int,
args[ARG_bs1].u_int, args[ARG_bs2].u_int, args[ARG_auto_restart].u_bool)) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("CAN(%d) init failure"), self->can_id);
}
+ self->is_enabled = true;
+ self->num_error_warning = 0;
+ self->num_error_passive = 0;
+ self->num_bus_off = 0;
+
return mp_const_none;
}
@@ -346,7 +342,7 @@ static mp_obj_t pyb_can_make_new(const mp_obj_type_t *type, size_t n_args, size_
if (self->is_enabled) {
// The caller is requesting a reconfiguration of the hardware
// this can only be done if the hardware is in init mode
- can_deinit(self);
+ pyb_can_deinit(MP_OBJ_FROM_PTR(self));
}
self->rxcallback0 = mp_const_none;
@@ -373,7 +369,8 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_init_obj, 1, pyb_can_init);
// deinit()
static mp_obj_t pyb_can_deinit(mp_obj_t self_in) {
pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
- can_deinit(self);
+ can_deinit(&self->can);
+ self->is_enabled = false;
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(pyb_can_deinit_obj, pyb_can_deinit);
@@ -489,17 +486,8 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_can_info_obj, 1, 2, pyb_can_info)
// any(fifo) - return `True` if any message waiting on the FIFO, else `False`
static mp_obj_t pyb_can_any(mp_obj_t self_in, mp_obj_t fifo_in) {
pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
- mp_int_t fifo = mp_obj_get_int(fifo_in);
- if (fifo == 0) {
- if (__HAL_CAN_MSG_PENDING(&self->can, CAN_FIFO0) != 0) {
- return mp_const_true;
- }
- } else {
- if (__HAL_CAN_MSG_PENDING(&self->can, CAN_FIFO1) != 0) {
- return mp_const_true;
- }
- }
- return mp_const_false;
+ can_rx_fifo_t fifo = mp_obj_get_int(fifo_in);
+ return mp_obj_new_bool(can_rx_pending(&self->can, fifo) != 0);
}
static MP_DEFINE_CONST_FUN_OBJ_2(pyb_can_any_obj, pyb_can_any);
@@ -533,6 +521,7 @@ static mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
}
// send the data
+ HAL_StatusTypeDef status;
CanTxMsgTypeDef tx_msg;
#if MICROPY_HW_ENABLE_FDCAN
@@ -594,27 +583,7 @@ static mp_obj_t pyb_can_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
tx_data[i] = ((byte *)bufinfo.buf)[i];
}
- HAL_StatusTypeDef status;
- #if MICROPY_HW_ENABLE_FDCAN
- uint32_t timeout_ms = args[ARG_timeout].u_int;
- uint32_t start = HAL_GetTick();
- while (HAL_FDCAN_GetTxFifoFreeLevel(&self->can) == 0) {
- if (timeout_ms == 0) {
- mp_raise_OSError(MP_ETIMEDOUT);
- }
- // Check for the Timeout
- if (timeout_ms != HAL_MAX_DELAY) {
- if (HAL_GetTick() - start >= timeout_ms) {
- mp_raise_OSError(MP_ETIMEDOUT);
- }
- }
- MICROPY_EVENT_POLL_HOOK
- }
- status = HAL_FDCAN_AddMessageToTxFifoQ(&self->can, &tx_msg, tx_data);
- #else
- self->can.pTxMsg = &tx_msg;
- status = CAN_Transmit(&self->can, args[ARG_timeout].u_int);
- #endif
+ status = can_transmit(&self->can, &tx_msg, tx_data, args[ARG_timeout].u_int);
if (status != HAL_OK) {
mp_hal_raise(status);
@@ -646,12 +615,8 @@ static mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
uint8_t *rx_data = rx_msg.Data;
#endif
- mp_uint_t fifo = args[ARG_fifo].u_int;
- if (fifo == 0) {
- fifo = CAN_FIFO0;
- } else if (fifo == 1) {
- fifo = CAN_FIFO1;
- } else {
+ can_rx_fifo_t fifo = args[ARG_fifo].u_int;
+ if (fifo != CAN_RX_FIFO0 && fifo != CAN_RX_FIFO1) {
mp_raise_TypeError(NULL);
}
@@ -667,30 +632,28 @@ static mp_obj_t pyb_can_recv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
#endif
// Manage the rx state machine
- if ((fifo == CAN_FIFO0 && self->rxcallback0 != mp_const_none) ||
- (fifo == CAN_FIFO1 && self->rxcallback1 != mp_const_none)) {
- byte *state = (fifo == CAN_FIFO0) ? &self->rx_state0 : &self->rx_state1;
-
+ if ((fifo == CAN_RX_FIFO0 && self->rxcallback0 != mp_const_none) ||
+ (fifo == CAN_RX_FIFO1 && self->rxcallback1 != mp_const_none)) {
+ bool fifo_empty = can_rx_pending(&self->can, fifo) == 0;
+ byte *state = (fifo == CAN_RX_FIFO0) ? &self->rx_state0 : &self->rx_state1;
switch (*state) {
case RX_STATE_FIFO_EMPTY:
break;
case RX_STATE_MESSAGE_PENDING:
- if (__HAL_CAN_MSG_PENDING(&self->can, fifo) == 0) {
- // Fifo is empty
- __HAL_CAN_ENABLE_IT(&self->can, (fifo == CAN_FIFO0) ? CAN_IT_FIFO0_PENDING : CAN_IT_FIFO1_PENDING);
+ if (fifo_empty) {
*state = RX_STATE_FIFO_EMPTY;
}
break;
case RX_STATE_FIFO_FULL:
- __HAL_CAN_ENABLE_IT(&self->can, (fifo == CAN_FIFO0) ? CAN_IT_FIFO0_FULL : CAN_IT_FIFO1_FULL);
*state = RX_STATE_MESSAGE_PENDING;
break;
case RX_STATE_FIFO_OVERFLOW:
- __HAL_CAN_ENABLE_IT(&self->can, (fifo == CAN_FIFO0) ? CAN_IT_FIFO0_OVRF : CAN_IT_FIFO1_OVRF);
- __HAL_CAN_ENABLE_IT(&self->can, (fifo == CAN_FIFO0) ? CAN_IT_FIFO0_FULL : CAN_IT_FIFO1_FULL);
*state = RX_STATE_MESSAGE_PENDING;
break;
}
+
+ // Re-enable any interrupts that were disabled in RX IRQ handlers
+ can_enable_rx_interrupts(&self->can, fifo, fifo_empty);
}
// Create the tuple, or get the list, that will hold the return values
@@ -756,12 +719,12 @@ static mp_obj_t pyb_can_clearfilter(size_t n_args, const mp_obj_t *pos_args, mp_
mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
#if MICROPY_HW_ENABLE_FDCAN
- can_clearfilter(self, f, args[ARG_extframe].u_bool);
+ can_clearfilter(&self->can, f, args[ARG_extframe].u_bool);
#else
if (self->can_id == 2) {
f += can2_start_bank;
}
- can_clearfilter(self, f, can2_start_bank);
+ can_clearfilter(&self->can, f, can2_start_bank);
#endif
return mp_const_none;
}
@@ -945,16 +908,12 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(pyb_can_setfilter_obj, 1, pyb_can_setfilter);
static mp_obj_t pyb_can_rxcallback(mp_obj_t self_in, mp_obj_t fifo_in, mp_obj_t callback_in) {
pyb_can_obj_t *self = MP_OBJ_TO_PTR(self_in);
- mp_int_t fifo = mp_obj_get_int(fifo_in);
+ can_rx_fifo_t fifo = mp_obj_get_int(fifo_in);
mp_obj_t *callback;
- callback = (fifo == 0) ? &self->rxcallback0 : &self->rxcallback1;
+ callback = (fifo == CAN_RX_FIFO0) ? &self->rxcallback0 : &self->rxcallback1;
if (callback_in == mp_const_none) {
- __HAL_CAN_DISABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_PENDING : CAN_IT_FIFO1_PENDING);
- __HAL_CAN_DISABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_FULL : CAN_IT_FIFO1_FULL);
- __HAL_CAN_DISABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_OVRF : CAN_IT_FIFO1_OVRF);
- __HAL_CAN_CLEAR_FLAG(&self->can, (fifo == CAN_FIFO0) ? CAN_FLAG_FIFO0_FULL : CAN_FLAG_FIFO1_FULL);
- __HAL_CAN_CLEAR_FLAG(&self->can, (fifo == CAN_FIFO0) ? CAN_FLAG_FIFO0_OVRF : CAN_FLAG_FIFO1_OVRF);
+ can_disable_rx_interrupts(&self->can, fifo);
*callback = mp_const_none;
} else if (*callback != mp_const_none) {
// Rx call backs has already been initialized
@@ -964,21 +923,19 @@ static mp_obj_t pyb_can_rxcallback(mp_obj_t self_in, mp_obj_t fifo_in, mp_obj_t
*callback = callback_in;
uint32_t irq = 0;
if (self->can_id == PYB_CAN_1) {
- irq = (fifo == 0) ? CAN1_RX0_IRQn : CAN1_RX1_IRQn;
+ irq = (fifo == CAN_RX_FIFO0) ? CAN1_RX0_IRQn : CAN1_RX1_IRQn;
#if defined(CAN2)
} else if (self->can_id == PYB_CAN_2) {
- irq = (fifo == 0) ? CAN2_RX0_IRQn : CAN2_RX1_IRQn;
+ irq = (fifo == CAN_RX_FIFO0) ? CAN2_RX0_IRQn : CAN2_RX1_IRQn;
#endif
#if defined(CAN3)
} else {
- irq = (fifo == 0) ? CAN3_RX0_IRQn : CAN3_RX1_IRQn;
+ irq = (fifo == CAN_RX_FIFO0) ? CAN3_RX0_IRQn : CAN3_RX1_IRQn;
#endif
}
NVIC_SetPriority(irq, IRQ_PRI_CAN);
HAL_NVIC_EnableIRQ(irq);
- __HAL_CAN_ENABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_PENDING : CAN_IT_FIFO1_PENDING);
- __HAL_CAN_ENABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_FULL : CAN_IT_FIFO1_FULL);
- __HAL_CAN_ENABLE_IT(&self->can, (fifo == 0) ? CAN_IT_FIFO0_OVRF : CAN_IT_FIFO1_OVRF);
+ can_enable_rx_interrupts(&self->can, fifo, true);
}
return mp_const_none;
}
@@ -1038,8 +995,8 @@ static mp_uint_t can_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, i
uintptr_t flags = arg;
ret = 0;
if ((flags & MP_STREAM_POLL_RD)
- && ((__HAL_CAN_MSG_PENDING(&self->can, CAN_FIFO0) != 0)
- || (__HAL_CAN_MSG_PENDING(&self->can, CAN_FIFO1) != 0))) {
+ && ((can_rx_pending(&self->can, 0) != 0)
+ || (can_rx_pending(&self->can, 1) != 0))) {
ret |= MP_STREAM_POLL_RD;
}
#if MICROPY_HW_ENABLE_FDCAN
@@ -1057,17 +1014,62 @@ static mp_uint_t can_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, i
return ret;
}
-void pyb_can_handle_callback(pyb_can_obj_t *self, uint fifo_id, mp_obj_t callback, mp_obj_t irq_reason) {
+// IRQ handler, called from lower layer can.c or fdcan.c in ISR context
+
+void can_irq_handler(uint can_id, can_int_t interrupt, can_rx_fifo_t fifo) {
+ mp_obj_t callback;
+ pyb_can_obj_t *self;
+ byte *state;
+
+ self = MP_STATE_PORT(pyb_can_obj_all)[can_id - 1];
+
+ if (fifo == CAN_RX_FIFO0) {
+ callback = self->rxcallback0;
+ state = &self->rx_state0;
+ } else {
+ callback = self->rxcallback1;
+ state = &self->rx_state1;
+ }
+
+ switch (interrupt) {
+ // These interrupts go on to run a Python callback, interrupt arg is the
+ // 'interrupt' enum value as an int.
+ case CAN_INT_MESSAGE_RECEIVED:
+ *state = RX_STATE_MESSAGE_PENDING;
+ break;
+ case CAN_INT_FIFO_FULL:
+ *state = RX_STATE_FIFO_FULL;
+ break;
+ case CAN_INT_FIFO_OVERFLOW:
+ *state = RX_STATE_FIFO_OVERFLOW;
+ break;
+
+ // These interrupts do not run a Python callback
+ case CAN_INT_ERR_BUS_OFF:
+ self->num_bus_off++;
+ return;
+ case CAN_INT_ERR_PASSIVE:
+ self->num_error_passive++;
+ return;
+ case CAN_INT_ERR_WARNING:
+ self->num_error_warning++;
+ return;
+
+ default:
+ return; // Should be unreachable
+ }
+
+ // Run the callback
if (callback != mp_const_none) {
mp_sched_lock();
gc_lock();
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
- mp_call_function_2(callback, MP_OBJ_FROM_PTR(self), irq_reason);
+ mp_call_function_2(callback, MP_OBJ_FROM_PTR(self), MP_OBJ_NEW_SMALL_INT(interrupt));
nlr_pop();
} else {
// Uncaught exception; disable the callback so it doesn't run again.
- pyb_can_rxcallback(MP_OBJ_FROM_PTR(self), MP_OBJ_NEW_SMALL_INT(fifo_id), mp_const_none);
+ pyb_can_rxcallback(MP_OBJ_FROM_PTR(self), MP_OBJ_NEW_SMALL_INT(fifo), mp_const_none);
mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in CAN(%u) rx interrupt handler\n", self->can_id);
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
}
diff --git a/ports/stm32/pyb_can.h b/ports/stm32/pyb_can.h
new file mode 100644
index 000000000..a82043d78
--- /dev/null
+++ b/ports/stm32/pyb_can.h
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_STM32_PYB_CAN_H
+#define MICROPY_INCLUDED_STM32_PYB_CAN_H
+
+#include "py/mphal.h"
+
+#if MICROPY_HW_ENABLE_CAN
+
+#include "py/obj.h"
+#include "can.h"
+
+typedef struct _pyb_can_obj_t {
+ mp_obj_base_t base;
+ mp_obj_t rxcallback0;
+ mp_obj_t rxcallback1;
+ mp_uint_t can_id : 8;
+ bool is_enabled : 1;
+ byte rx_state0;
+ byte rx_state1;
+ uint16_t num_error_warning;
+ uint16_t num_error_passive;
+ uint16_t num_bus_off;
+ CAN_HandleTypeDef can;
+} pyb_can_obj_t;
+
+extern const mp_obj_type_t pyb_can_type;
+
+void pyb_can_deinit_all(void);
+void pyb_can_init0(void);
+
+void pyb_can_irq_handler(uint can_id, can_rx_fifo_t fifo, can_int_t interrupt);
+
+#endif
+#endif