diff options
| -rw-r--r-- | ports/stm32/can.c | 198 | ||||
| -rw-r--r-- | ports/stm32/can.h | 84 | ||||
| -rw-r--r-- | ports/stm32/fdcan.c | 243 | ||||
| -rw-r--r-- | ports/stm32/main.c | 6 | ||||
| -rw-r--r-- | ports/stm32/modpyb.c | 2 | ||||
| -rw-r--r-- | ports/stm32/pyb_can.c | 202 | ||||
| -rw-r--r-- | ports/stm32/pyb_can.h | 58 |
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 |
