canio: Make both tx and rx (but not both) optional

.. loopback and silent come from the (optional) constructor parameters
not guessing based on the pin specification

.. docstring & comment improvements
This commit is contained in:
Jeff Epler 2020-09-18 14:19:05 -05:00
parent 09f8a83a75
commit 7fd6cab2ac
3 changed files with 74 additions and 20 deletions

View File

@ -51,9 +51,10 @@ __attribute__((optimize("O0"), noinline))
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)
{
mcu_pin_function_t *tx_function = mcu_find_pin_function(can_tx, tx, -1, MP_QSTR_tx);
int instance = tx_function->instance;
int instance = tx_function ? tx_function->instance : -1;
mcu_pin_function_t *rx_function = mcu_find_pin_function(can_rx, rx, instance, MP_QSTR_rx);
instance = rx_function->instance;
const uint32_t can_frequency = CONF_CAN0_FREQUENCY;
@ -68,9 +69,11 @@ void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *rx, mc
mp_raise_OSError(MP_EINVAL); // baudrate cannot be attained (16kHz or something is lower bound, should never happen)
}
gpio_set_pin_direction(tx_function->pin, GPIO_DIRECTION_OUT);
gpio_set_pin_function(tx_function->pin, tx_function->function);
common_hal_never_reset_pin(tx_function->obj);
if (tx_function) {
gpio_set_pin_direction(tx_function->pin, GPIO_DIRECTION_OUT);
gpio_set_pin_function(tx_function->pin, tx_function->function);
common_hal_never_reset_pin(tx_function->obj);
}
if (rx_function) {
gpio_set_pin_direction(rx_function->pin, GPIO_DIRECTION_IN);
@ -78,12 +81,13 @@ void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *rx, mc
common_hal_never_reset_pin(rx_function->obj);
}
self->tx_pin_number = common_hal_mcu_pin_number(tx);
self->tx_pin_number = tx ? common_hal_mcu_pin_number(tx) : COMMON_HAL_MCU_NO_PIN;
self->rx_pin_number = rx ? common_hal_mcu_pin_number(rx) : COMMON_HAL_MCU_NO_PIN;
self->hw = can_insts[instance];
self->state = &can_state[instance];
self->loopback = loopback;
self->silent = silent;
// Allow configuration change
hri_can_set_CCCR_INIT_bit(self->hw);
@ -129,11 +133,9 @@ void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *rx, mc
hri_can_write_NBTP_reg(self->hw, btp.reg);
}
// we don't do the high bit rate yet, so do we need to set this register?
// hri_can_write_DBTP_reg(self->hw, ???);
// A "data bit" is a data bit :) with dul rate CAN FD, this is a higher
// rate. However, CAN FD is not implemented in CircuitPython, and this is the same rate as
// the "nominal rate".
// A "data bit" is a data bit :) with dula rate CAN FD, this is a higher
// rate. However, CAN FD is not implemented in CircuitPython, and this is
// the same rate as the "nominal rate".
{
CAN_DBTP_Type btp = {
.bit.DTSEG1 = DIV_ROUND(clocks_to_sample, divisor) - 1,
@ -259,7 +261,7 @@ void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *rx, mc
can_objs[instance] = self;
}
int common_hal_canio_can_loopback_get(canio_can_obj_t *self)
bool common_hal_canio_can_loopback_get(canio_can_obj_t *self)
{
return self->loopback;
}
@ -368,6 +370,10 @@ void common_hal_canio_can_send(canio_can_obj_t *self, canio_message_obj_t *messa
}
}
bool common_hal_canio_can_silent_get(canio_can_obj_t *self) {
return self->silent;
}
bool common_hal_canio_can_deinited(canio_can_obj_t *self) {
return !self->hw;
}

View File

@ -60,8 +60,10 @@ 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);
bool common_hal_canio_can_loopback_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);
bool common_hal_canio_can_silent_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);

View File

@ -42,7 +42,7 @@
//| """CAN bus protocol"""
//|
//| def __init__(self,
//| rx: microcontroller.Pin,
//| rx: Optional[microcontroller.Pin]=None,
//| tx: Optional[microcontroller.Pin]=None,
//| *,
//| baudrate: int = 250000,
@ -50,13 +50,18 @@
//| 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
//| bus.
//| connected to a transceiver which controls the H and L pins on a
//| shared bus.
//|
//| :param ~microcontroller.Pin rx: the pin to receive with.
//| :param ~microcontroller.Pin tx: the pin to transmit with, or None if the peripheral should operate in "silent" mode.
//| Normally, both ``tx`` and ``rx`` pins will be specified. However,
//| in silent and loopback modes, the other pin may not be required and
//| can be used for other purposes.
//|
//| :param ~microcontroller.Pin rx: the pin to receive with, or None.
//| :param ~microcontroller.Pin tx: the pin to transmit with, or None.
//| :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 loopback: True if the peripheral will be operated in loopback mode. In loopback mode, the ``rx`` pin's value is ignored, and the device receives the packets it sends.
//| :param bool silent: True if the peripheral will be operated in silent mode. In silent mode, the ``tx`` pin is always driven to the high logic level. This mode can be used to "sniff" a CAN bus without interfering.
//| :param bool auto_restart: If True, will restart communications after entering bus-off state
//| """
//| ...
@ -64,8 +69,8 @@
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, 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_rx, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_tx, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ 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} },
@ -76,8 +81,11 @@ STATIC mp_obj_t canio_can_make_new(const mp_obj_type_t *type, size_t n_args, con
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mcu_pin_obj_t *rx_pin = validate_obj_is_free_pin(args[ARG_rx].u_obj);
mcu_pin_obj_t *rx_pin = validate_obj_is_free_pin_or_none(args[ARG_rx].u_obj);
mcu_pin_obj_t *tx_pin = validate_obj_is_free_pin_or_none(args[ARG_tx].u_obj);
if (!rx_pin && !tx_pin) {
mp_raise_ValueError(translate("tx and rx cannot both be None"));
}
canio_can_obj_t *self = m_new_obj(canio_can_obj_t);
self->base.type = &canio_can_type;
@ -301,6 +309,24 @@ STATIC mp_obj_t canio_can_listen(size_t n_args, const mp_obj_t *pos_args, mp_map
}
MP_DEFINE_CONST_FUN_OBJ_KW(canio_can_listen_obj, 1, canio_can_listen);
//| loopback: bool
//| """True if the device was created in loopback mode, False otherwise"""
//|
STATIC mp_obj_t canio_can_loopback_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_loopback_get(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(canio_can_loopback_get_obj, canio_can_loopback_get);
STATIC const mp_obj_property_t canio_can_loopback_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&canio_can_loopback_get_obj,
(mp_obj_t)mp_const_none,
(mp_obj_t)mp_const_none},
};
//| def send(message: Message) -> None:
//| """Send a message on the bus with the given data and id.
//| If the message could not be sent due to a full fifo or a bus error condition, RuntimeError is raised.
@ -321,6 +347,24 @@ STATIC mp_obj_t canio_can_send(mp_obj_t self_in, mp_obj_t message_in) {
}
MP_DEFINE_CONST_FUN_OBJ_2(canio_can_send_obj, canio_can_send);
//| silent: bool
//| """True if the device was created in silent mode, False otherwise"""
//|
STATIC mp_obj_t canio_can_silent_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_silent_get(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(canio_can_silent_get_obj, canio_can_silent_get);
STATIC const mp_obj_property_t canio_can_silent_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&canio_can_silent_get_obj,
(mp_obj_t)mp_const_none,
(mp_obj_t)mp_const_none},
};
//| def deinit(self) -> None:
//| """Deinitialize this object, freeing its hardware resources"""
//| ...
@ -363,9 +407,11 @@ STATIC const mp_rom_map_elem_t canio_can_locals_dict_table[] = {
{ 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_loopback), MP_ROM_PTR(&canio_can_loopback_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_silent), MP_ROM_PTR(&canio_can_silent_obj) },
{ MP_ROM_QSTR(MP_QSTR_state), MP_ROM_PTR(&canio_can_state_obj) },
{ MP_ROM_QSTR(MP_QSTR_transmit_error_count), MP_ROM_PTR(&canio_can_transmit_error_count_obj) },
};