From 635fcadb59fdba47d1628c3abff1b9bfc890a5d5 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 16 Sep 2020 11:59:01 -0500 Subject: [PATCH] _canio.CAN: add error handling & bus state --- ports/atmel-samd/common-hal/_canio/CAN.c | 71 ++++++++++++++++-- ports/atmel-samd/common-hal/_canio/CAN.h | 37 ++++++---- shared-bindings/_canio/CAN.c | 93 ++++++++++++++++++------ 3 files changed, 156 insertions(+), 45 deletions(-) diff --git a/ports/atmel-samd/common-hal/_canio/CAN.c b/ports/atmel-samd/common-hal/_canio/CAN.c index dcd269495e..d2e7d8d9db 100644 --- a/ports/atmel-samd/common-hal/_canio/CAN.c +++ b/ports/atmel-samd/common-hal/_canio/CAN.c @@ -218,6 +218,15 @@ void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *rx, mc hri_can_write_XIDFC_reg(self->hw, dfc.reg); } + { + CAN_IE_Type ie = { + .bit.EWE = 1, + .bit.EPE = 1, + .bit.BOE = 1, + }; + hri_can_write_IE_reg(self->hw, ie.reg); + } + hri_can_write_XIDAM_reg(self->hw, CAN_XIDAM_RESETVALUE); // silent: The CAN is set in Bus Monitoring Mode by programming CCCR.MON to '1'. (tx pin unused) @@ -261,35 +270,71 @@ int common_hal_canio_can_baudrate_get(canio_can_obj_t *self) int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self) { - return -1; + return self->hw->ECR.bit.TEC; } int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self) { - return -1; + return self->hw->ECR.bit.REC; } int common_hal_canio_can_error_warning_state_count_get(canio_can_obj_t *self) { - return -1; + return self->error_warning_state_count; } int common_hal_canio_can_error_passive_state_count_get(canio_can_obj_t *self) { - return -1; + return self->error_passive_state_count; } int common_hal_canio_can_bus_off_state_count_get(canio_can_obj_t *self) { - return -1; + return self->bus_off_state_count; } -int common_hal_canio_can_state_get(canio_can_obj_t *self) { - return -1; +canio_bus_state_t common_hal_canio_can_state_get(canio_can_obj_t *self) { + CAN_PSR_Type psr = self->hw->PSR; + if(psr.bit.BO) { + return BUS_STATE_OFF; + } + if(psr.bit.EP) { + return BUS_STATE_ERROR_PASSIVE; + } + if(psr.bit.EW) { + return BUS_STATE_ERROR_WARNING; + } + return BUS_STATE_ERROR_ACTIVE; +} + +void common_hal_canio_can_restart(canio_can_obj_t *self) { + if (!self->hw->PSR.bit.BO) { + return; + } + + hri_can_clear_CCCR_INIT_bit(self->hw); + while (hri_can_get_CCCR_INIT_bit(self->hw)) { + } +} + +bool common_hal_canio_can_auto_restart_get(canio_can_obj_t *self) { + return self->auto_restart; +} + +void common_hal_canio_can_auto_restart_set(canio_can_obj_t *self, bool value) { + self->auto_restart = value; +} + +static void maybe_auto_restart(canio_can_obj_t *self) { + if(self->auto_restart) { + common_hal_canio_can_restart(self); + } } void common_hal_canio_can_send(canio_can_obj_t *self, canio_message_obj_t *message) { + maybe_auto_restart(self); + // We have just one dedicated TX buffer, use it! canio_can_fifo_t *ent = &self->state->tx_fifo[0]; ent->txb0.bit.ESI = false; @@ -365,7 +410,17 @@ STATIC void can_handler(int i) { Can *hw = can_insts[i]; uint32_t ir = hri_can_read_IR_reg(hw); - /* Handle various interrupts */ + + /* Count up errors*/ + if (ir & CAN_IE_EWE) { + self->error_warning_state_count += 1; + } + if (ir & CAN_IE_EPE) { + self->error_passive_state_count += 1; + } + if (ir & CAN_IE_BOE) { + self->bus_off_state_count += 1; + } /* Acknowledge interrupt */ hri_can_write_IR_reg(hw, ir); diff --git a/ports/atmel-samd/common-hal/_canio/CAN.h b/ports/atmel-samd/common-hal/_canio/CAN.h index 0e637fc408..02bc2e41b5 100644 --- a/ports/atmel-samd/common-hal/_canio/CAN.h +++ b/ports/atmel-samd/common-hal/_canio/CAN.h @@ -27,6 +27,7 @@ #pragma once #include "py/obj.h" +#include "shared-bindings/_canio/__init__.h" #include "component/can.h" #include "common-hal/microcontroller/Pin.h" #include "common-hal/_canio/__init__.h" @@ -38,25 +39,33 @@ typedef struct { mp_obj_base_t base; Can *hw; - int baudrate; - uint8_t rx_pin_number, tx_pin_number; - bool loopback; - bool silent; canio_can_state_t *state; + volatile uint32_t error_warning_state_count; + volatile uint32_t error_passive_state_count; + volatile uint32_t bus_off_state_count; + int baudrate; + uint8_t rx_pin_number:8; + uint8_t tx_pin_number:8; + bool loopback:1; + bool silent:1; + bool auto_restart:1; bool fifo0_in_use:1; bool fifo1_in_use:1; } canio_can_obj_t; void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *rx, mcu_pin_obj_t *tx, int baudrate, bool loopback, bool silent); -int common_hal_canio_can_state_get(canio_can_obj_t *self); -int common_hal_canio_can_baudrate_get(canio_can_obj_t *self); -int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self); -int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self); -int common_hal_canio_can_error_warning_state_count_get(canio_can_obj_t *self); -int common_hal_canio_can_error_passive_state_count_get(canio_can_obj_t *self); -int common_hal_canio_can_bus_off_state_count_get(canio_can_obj_t *self); -void common_hal_canio_can_send(canio_can_obj_t *self, canio_message_obj_t *message); -void common_hal_canio_can_deinit(canio_can_obj_t *self); -void common_hal_canio_can_check_for_deinit(canio_can_obj_t *self); +bool common_hal_canio_can_auto_restart_get(canio_can_obj_t *self); bool common_hal_canio_can_deinited(canio_can_obj_t *self); +int common_hal_canio_can_baudrate_get(canio_can_obj_t *self); +int common_hal_canio_can_bus_off_state_count_get(canio_can_obj_t *self); +int common_hal_canio_can_error_passive_state_count_get(canio_can_obj_t *self); +int common_hal_canio_can_error_warning_state_count_get(canio_can_obj_t *self); +int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self); +canio_bus_state_t common_hal_canio_can_state_get(canio_can_obj_t *self); +int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self); +void common_hal_canio_can_auto_restart_set(canio_can_obj_t *self, bool auto_restart); +void common_hal_canio_can_check_for_deinit(canio_can_obj_t *self); +void common_hal_canio_can_deinit(canio_can_obj_t *self); +void common_hal_canio_can_restart(canio_can_obj_t *self); +void common_hal_canio_can_send(canio_can_obj_t *self, canio_message_obj_t *message); void common_hal_canio_reset(void); diff --git a/shared-bindings/_canio/CAN.c b/shared-bindings/_canio/CAN.c index 4c7ae3650a..45640d41e4 100644 --- a/shared-bindings/_canio/CAN.c +++ b/shared-bindings/_canio/CAN.c @@ -24,8 +24,10 @@ * THE SOFTWARE. */ +#include "py/enum.h" #include "common-hal/_canio/CAN.h" #include "common-hal/_canio/Listener.h" +#include "shared-bindings/_canio/__init__.h" #include "shared-bindings/_canio/CAN.h" #include "shared-bindings/_canio/Listener.h" #include "shared-bindings/_canio/Match.h" @@ -45,6 +47,7 @@ //| *, //| baudrate: int = 250000, //| loopback: bool = False, +//| auto_restart: bool = False, //| ): //| """A common shared-bus protocol. The rx and tx pins are generally //| connected to a transceiver which controls the H and L pins on a shared @@ -54,19 +57,19 @@ //| :param ~microcontrller.Pin tx: the pin to transmit with, or None if the peripheral should operate in "silent" mode. //| :param int baudrate: The bit rate of the bus in Hz. All devices on the bus must agree on this value. //| :param bool loopback: True if the peripheral will be operated in loopback mode. +//| :param bool auto_restart: If True, will restart communications after entering bus-off state //| """ //| ... //| -//## auto_restart: bool = False, # Whether to restart communications after entering bus-off state -//## sample_point: float = .875, # When to sample within bit time (0.0-1.0) STATIC mp_obj_t canio_can_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_rx, ARG_tx, ARG_baudrate, ARG_loopback, ARG_silent, NUM_ARGS }; + enum { ARG_rx, ARG_tx, ARG_baudrate, ARG_loopback, ARG_silent, ARG_auto_restart, NUM_ARGS }; static const mp_arg_t allowed_args[] = { { MP_QSTR_rx, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = 0} }, { MP_QSTR_tx, MP_ARG_OBJ, {.u_obj = 0} }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 250000} }, { MP_QSTR_loopback, MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_silent, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_auto_restart, MP_ARG_BOOL, {.u_bool = false} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS ); @@ -85,12 +88,40 @@ mp_printf(&mp_plat_print, "tx_pin=%p\n", tx_pin); self->base.type = &canio_can_type; common_hal_canio_can_construct(self, rx_pin, tx_pin, args[ARG_baudrate].u_int, args[ARG_loopback].u_bool, args[ARG_silent].u_bool); + common_hal_canio_can_auto_restart_set(self, args[ARG_auto_restart].u_bool); + return MP_OBJ_FROM_PTR(self); } +//| auto_restart: int +//| """If True, will restart communications after entering bus-off state""" +//| +STATIC mp_obj_t canio_can_auto_restart_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return mp_obj_new_bool(common_hal_canio_can_auto_restart_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_auto_restart_get_obj, canio_can_auto_restart_get); + +STATIC mp_obj_t canio_can_auto_restart_set(mp_obj_t self_in, mp_obj_t flag_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + common_hal_canio_can_auto_restart_set(self, mp_obj_is_true(flag_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_can_auto_restart_set_obj, canio_can_auto_restart_set); + +STATIC const mp_obj_property_t canio_can_auto_restart_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_auto_restart_get_obj, + (mp_obj_t)&canio_can_auto_restart_set_obj, + (mp_obj_t)mp_const_none}, +}; + + //| baudrate: int -//| """The baud rate(read-only)""" +//| """The baud rate (read-only)""" //| STATIC mp_obj_t canio_can_baudrate_get(mp_obj_t self_in) { canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -106,23 +137,6 @@ STATIC const mp_obj_property_t canio_can_baudrate_obj = { (mp_obj_t)mp_const_none}, }; -//| state: State -//| """The status of the hardware (read-only)""" -//| -STATIC mp_obj_t canio_can_state_get(mp_obj_t self_in) { - canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_canio_can_check_for_deinit(self); - return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_state_get(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(canio_can_state_get_obj, canio_can_state_get); - -STATIC const mp_obj_property_t canio_can_state_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&canio_can_state_get_obj, - (mp_obj_t)mp_const_none, - (mp_obj_t)mp_const_none}, -}; - //| transmit_error_count: int //| """The number of transmit errors (read-only). Increased for a detected transmission error, decreased for successful transmission. Limited to the range from 0 to 255 inclusive. Also called TEC.""" //| @@ -208,12 +222,43 @@ STATIC const mp_obj_property_t canio_can_bus_off_state_count_obj = { (mp_obj_t)mp_const_none}, }; +//| state: State +//| """The current state of the bus.""" +STATIC mp_obj_t canio_can_state_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return cp_enum_find(&canio_bus_state_type, common_hal_canio_can_state_get(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_state_get_obj, canio_can_state_get); + +STATIC const mp_obj_property_t canio_can_state_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_state_get_obj, + (mp_obj_t)mp_const_none, + (mp_obj_t)mp_const_none}, +}; + + #if 0 //| # pending_tx_count: int //| # """The number of messages waiting to be transmitted. (read-only)""" +//| #endif -//| def listen(filters: Optional[Sequence[Filter]]=None, *, timeout: float=10) -> Listener: +//| def restart(self) -> None: +//| """If the device is in the bus off state, restart it.""" +//| ... +//| +STATIC mp_obj_t canio_can_restart(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + common_hal_canio_can_restart(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_can_restart_obj, canio_can_restart); + +//| def listen(self, filters: Optional[Sequence[Filter]]=None, *, timeout: float=10) -> Listener: //| """Start receiving messages that match any one of the filters. //| Creating a listener is an expensive operation and can interfere with reception of messages by other listeners. //| There is an implementation-defined maximum number of listeners and limit to the complexity of the filters. @@ -317,15 +362,17 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canio_can_exit_obj, 4, 4, canio_can_e STATIC const mp_rom_map_elem_t canio_can_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&canio_can_enter_obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&canio_can_exit_obj) }, + { MP_ROM_QSTR(MP_QSTR_auto_restart), MP_ROM_PTR(&canio_can_auto_restart_obj) }, + { MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&canio_can_baudrate_obj) }, { MP_ROM_QSTR(MP_QSTR_bus_off_state_count), MP_ROM_PTR(&canio_can_bus_off_state_count_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&canio_can_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_error_passive_state_count), MP_ROM_PTR(&canio_can_error_passive_state_count_obj) }, { MP_ROM_QSTR(MP_QSTR_error_warning_state_count), MP_ROM_PTR(&canio_can_error_warning_state_count_obj) }, { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&canio_can_listen_obj) }, { MP_ROM_QSTR(MP_QSTR_receive_error_count), MP_ROM_PTR(&canio_can_receive_error_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_restart), MP_ROM_PTR(&canio_can_restart_obj) }, { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&canio_can_send_obj) }, { MP_ROM_QSTR(MP_QSTR_state), MP_ROM_PTR(&canio_can_state_obj) }, - { MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&canio_can_baudrate_obj) }, { MP_ROM_QSTR(MP_QSTR_transmit_error_count), MP_ROM_PTR(&canio_can_transmit_error_count_obj) }, }; STATIC MP_DEFINE_CONST_DICT(canio_can_locals_dict, canio_can_locals_dict_table);