From 6c1481b6ee490d90cf6c51ea9a1e4b56adebae97 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 17 Aug 2021 18:59:38 +0200 Subject: [PATCH] drivers/dose: enable standby pin Some CAN transceivers have a standby pin that has to be pulled low in order to use it. If the interface is disabled we can set it to high again to save some power. --- drivers/dose/dose.c | 67 +++++++++++++++++++++++++++++- drivers/dose/include/dose_params.h | 23 ++++++---- drivers/include/dose.h | 4 ++ 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/drivers/dose/dose.c b/drivers/dose/dose.c index 6bf97a0ad7..dda81b31b6 100644 --- a/drivers/dose/dose.c +++ b/drivers/dose/dose.c @@ -48,6 +48,7 @@ static int send_octet(dose_t *ctx, uint8_t c); static int _send(netdev_t *dev, const iolist_t *iolist); static int _get(netdev_t *dev, netopt_t opt, void *value, size_t max_len); static int _set(netdev_t *dev, netopt_t opt, const void *value, size_t len); +static int _set_state(dose_t *ctx, netopt_state_t state); static int _init(netdev_t *dev); static uint16_t crc16_update(uint16_t crc, uint8_t octet) @@ -60,6 +61,15 @@ static uint16_t crc16_update(uint16_t crc, uint8_t octet) return crc; } +static void _init_standby(dose_t *ctx, const dose_params_t *params) +{ + ctx->standby_pin = params->standby_pin; + if (gpio_is_valid(ctx->standby_pin) && + gpio_init(ctx->standby_pin, GPIO_OUT)) { + gpio_clear(ctx->standby_pin); + } +} + static void _init_sense(dose_t *ctx, const dose_params_t *params) { #ifdef MODULE_PERIPH_UART_RXSTART_IRQ @@ -389,8 +399,8 @@ static int send_octet(dose_t *ctx, uint8_t c) uart_write(ctx->uart, (uint8_t *) &c, sizeof(c)); /* Wait for a state transition */ - uint8_t state = wait_for_state(ctx, DOSE_STATE_ANY); - if (state != DOSE_STATE_SEND) { + uint8_t new_state = wait_for_state(ctx, DOSE_STATE_ANY); + if (new_state != DOSE_STATE_SEND) { /* Timeout */ DEBUG("dose send_octet(): timeout\n"); return -2; @@ -429,6 +439,16 @@ static int _send(netdev_t *dev, const iolist_t *iolist) size_t pktlen; uint16_t crc; + /* discard data when interface is in SLEEP mode */ + if (ctx->state == DOSE_STATE_SLEEP) { + return -ENETDOWN; + } + + /* sending data wakes the interface from STANDBY */ + if (ctx->state == DOSE_STATE_STANDBY) { + _set_state(ctx, NETOPT_STATE_IDLE); + } + send: crc = 0xffff; pktlen = 0; @@ -517,6 +537,45 @@ static int _get(netdev_t *dev, netopt_t opt, void *value, size_t max_len) return 0; } +static void _gpio_try_set(gpio_t pin) +{ + if (gpio_is_valid(pin)) { + gpio_set(pin); + } +} + +static void _gpio_try_clear(gpio_t pin) +{ + if (gpio_is_valid(pin)) { + gpio_clear(pin); + } +} + +static int _set_state(dose_t *ctx, netopt_state_t state) +{ + switch (state) { + case NETOPT_STATE_STANDBY: + _gpio_try_set(ctx->standby_pin); + uart_poweroff(ctx->uart); + ctx->state = DOSE_STATE_STANDBY; + return sizeof(netopt_state_t); + case NETOPT_STATE_SLEEP: + _gpio_try_set(ctx->standby_pin); + uart_poweroff(ctx->uart); + ctx->state = DOSE_STATE_SLEEP; + return sizeof(netopt_state_t); + case NETOPT_STATE_IDLE: + uart_poweron(ctx->uart); + _gpio_try_clear(ctx->standby_pin); + ctx->state = DOSE_STATE_IDLE; + return sizeof(netopt_state_t); + default: + break; + } + + return -ENOTSUP; +} + static int _set(netdev_t *dev, netopt_t opt, const void *value, size_t len) { dose_t *ctx = container_of(dev, dose_t, netdev); @@ -539,6 +598,9 @@ static int _set(netdev_t *dev, netopt_t opt, const void *value, size_t len) CLRBIT(ctx->opts, DOSE_OPT_PROMISCUOUS); } return sizeof(netopt_enable_t); + case NETOPT_STATE: + assert(len <= sizeof(netopt_state_t)); + return _set_state(ctx, *((const netopt_state_t *)value)); default: return netdev_eth_set(dev, opt, value, len); } @@ -585,6 +647,7 @@ void dose_setup(dose_t *ctx, const dose_params_t *params, uint8_t index) uart_init(ctx->uart, params->baudrate, _isr_uart, (void *) ctx); _init_sense(ctx, params); + _init_standby(ctx, params); netdev_register(&ctx->netdev, NETDEV_DOSE, index); diff --git a/drivers/dose/include/dose_params.h b/drivers/dose/include/dose_params.h index f493f41365..0d930ebbee 100644 --- a/drivers/dose/include/dose_params.h +++ b/drivers/dose/include/dose_params.h @@ -38,16 +38,25 @@ extern "C" { #ifndef DOSE_PARAM_SENSE_PIN #define DOSE_PARAM_SENSE_PIN (GPIO_UNDEF) #endif +#ifndef DOSE_PARAM_STANDBY_PIN +#define DOSE_PARAM_STANDBY_PIN (GPIO_UNDEF) /**< Standby/Silent mode */ +#endif #ifndef DOSE_PARAMS #ifdef MODULE_PERIPH_UART_RXSTART_IRQ -#define DOSE_PARAMS { .uart = DOSE_PARAM_UART, \ - .baudrate = DOSE_PARAM_BAUDRATE } -#else -#define DOSE_PARAMS { .uart = DOSE_PARAM_UART, \ - .baudrate = DOSE_PARAM_BAUDRATE, \ - .sense_pin = DOSE_PARAM_SENSE_PIN } -#endif +#define DOSE_PARAMS { \ + .uart = DOSE_PARAM_UART, \ + .baudrate = DOSE_PARAM_BAUDRATE, \ + .standby_pin = DOSE_PARAM_STANDBY_PIN, \ + } +#else /* MODULE_PERIPH_UART_RXSTART_IRQ */ +#define DOSE_PARAMS { \ + .uart = DOSE_PARAM_UART, \ + .baudrate = DOSE_PARAM_BAUDRATE, \ + .standby_pin = DOSE_PARAM_STANDBY_PIN, \ + .sense_pin = DOSE_PARAM_SENSE_PIN, \ + } +#endif /* !MODULE_PERIPH_UART_RXSTART_IRQ */ #endif /**@}*/ diff --git a/drivers/include/dose.h b/drivers/include/dose.h index 50052fafdb..abaa27ad29 100644 --- a/drivers/include/dose.h +++ b/drivers/include/dose.h @@ -87,6 +87,8 @@ typedef enum { DOSE_STATE_IDLE = 0x02, /**< Frames will be received or sent */ DOSE_STATE_RECV = 0x03, /**< Currently receiving a frame */ DOSE_STATE_SEND = 0x04, /**< Currently sending a frame */ + DOSE_STATE_STANDBY = 0x05, /**< Receiver is turned off, but send will wake it up */ + DOSE_STATE_SLEEP = 0x06, /**< Receiver is turned off and send will be discarded */ DOSE_STATE_ANY = 0x0F /**< Special state filter used internally to observe any state transition */ } dose_state_t; @@ -158,6 +160,7 @@ typedef struct { #if !defined(MODULE_PERIPH_UART_RXSTART_IRQ) || DOXYGEN gpio_t sense_pin; /**< GPIO to sense for start bits on the UART's rx line */ #endif + gpio_t standby_pin; /**< GPIO to put the CAN transceiver in standby mode */ xtimer_t timeout; /**< Timeout timer ensuring always to get back to IDLE state */ uint32_t timeout_base; /**< Base timeout in us */ } dose_t; @@ -170,6 +173,7 @@ typedef struct { #if !defined(MODULE_PERIPH_UART_RXSTART_IRQ) || DOXYGEN gpio_t sense_pin; /**< GPIO to sense for start bits on the UART's rx line */ #endif + gpio_t standby_pin; /**< GPIO to put the CAN transceiver in standby mode */ uint32_t baudrate; /**< Baudrate to UART device */ } dose_params_t;