diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 71783893fa..c2a8dd7d67 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -203,3 +203,9 @@ ifneq (,$(filter xbee,$(USEMODULE))) USEMODULE += xtimer USEMODULE += netif endif + +ifneq (,$(filter uart_half_duplex,$(USEMODULE))) + FEATURES_REQUIRED += periph_gpio + FEATURES_REQUIRED += periph_uart + USEMODULE += xtimer +endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 971e91089b..849e6cbf3d 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -106,3 +106,6 @@ endif ifneq (,$(filter adxl345,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/adxl345/include endif +ifneq (,$(filter uart_half_duplex,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/uart_half_duplex/include +endif diff --git a/drivers/uart_half_duplex/Makefile b/drivers/uart_half_duplex/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/uart_half_duplex/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/uart_half_duplex/include/uart_half_duplex.h b/drivers/uart_half_duplex/include/uart_half_duplex.h new file mode 100644 index 0000000000..7e694d75f5 --- /dev/null +++ b/drivers/uart_half_duplex/include/uart_half_duplex.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @defgroup drivers_uart_half_duplex half-duplex UART Driver + * @ingroup drivers_actuators + * + * This module contains drivers for UART half-duplex communication bus. + * It needs to manage the communication direction by enabling or disabling TX. + * + * @{ + * + * @file + * @brief Interface definition for half-duplex UART driver + * + * @author Loïc Dauphin + */ + +#ifndef UART_HALF_DUPLEX_H +#define UART_HALF_DUPLEX_H + +#include + +#include "periph/uart.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef UART_HALF_DUPLEX_DEFAULT_TIMEOUT_US +#define UART_HALF_DUPLEX_DEFAULT_TIMEOUT_US (20000LU) /**< Default recv timeout (in microseconds) */ +#endif + +/** + * @brief half-duplex UART direction management method type + */ +typedef int uart_half_duplex_dir_t; + +#define UART_HALF_DUPLEX_DIR_NONE (0) /**< Don't manage direction */ + +#define UART_HALF_DUPLEX_DIR_PIN_SET(pin) (((pin + 1) << 1)) /**< pin set enables TX */ +#define UART_HALF_DUPLEX_DIR_PIN_CLEAR(pin) (((pin + 1) << 1) | 1) /**< pin clear enables TX */ + +/** + * @brief Configuration for half-duplex UART + */ +typedef struct { + uart_t uart; /**< the half-duplex UART bus to use */ + uint32_t baudrate; /**< the baudrate to use */ + uart_half_duplex_dir_t dir; /**< the direction management method */ +} uart_half_duplex_params_t; + +/** + * @brief Descriptor struct for half-duplex UART + */ +typedef struct { + uint8_t *buffer; /**< the buffer used for TX and RX */ + size_t size; /**< the number of available elements for TX/RX */ + size_t buffer_max_size; /**< the buffer size */ + uint32_t timeout_us; /**< the maximum duration (in microseconds) for waiting data */ + uart_half_duplex_params_t params; /**< the half-duplex UART configuration */ +} uart_half_duplex_t; + +/** + * @brief Possible UART_HALF_DUPLEX return values + */ +enum { + UART_HALF_DUPLEX_OK = UART_OK, /**< everything in order */ + UART_HALF_DUPLEX_NODEV = UART_NODEV, /**< invalid UART device given */ + UART_HALF_DUPLEX_NOBAUD = UART_NOBAUD, /**< given baudrate is not applicable */ + UART_HALF_DUPLEX_INTERR = UART_INTERR, /**< all other internal errors */ + UART_HALF_DUPLEX_NOMODE = UART_NOMODE, /**< given mode is not applicable */ + UART_HALF_DUPLEX_NOBUFF = -5 /**< invalid buffer given */ +}; + +/** + * @brief Initialize the half-duplex UART bus to communicate with devices + * + * @param[out] dev the device + * @param[in] buffer the buffer used for TX and RX + * @param[in] buffer_max_size the buffer size + * @param[in] params the initialization parameters + * + * @return UART_HALF_DUPLEX_OK if everything is in order + * @return UART_HALF_DUPLEX_NODEV if invalid UART device was given + * @return UART_HALF_DUPLEX_NOBAUD if given baudrate is not applicable + * @return UART_HALF_DUPLEX_INTERR if an other internal error occured + * @return UART_HALF_DUPLEX_NOMODE if the given mode is not applicable + * @return UART_HALF_DUPLEX_NOBUFF if an invalid buffer was given + */ +int uart_half_duplex_init(uart_half_duplex_t *dev, uint8_t *buffer, size_t buffer_max_size, const uart_half_duplex_params_t *params); + +/** + * @brief Set the half-duplex UART bus in TX mode + * + * @param[in] dev the device + */ +static inline void uart_half_duplex_set_tx(uart_half_duplex_t *dev) +{ + dev->size = dev->buffer_max_size; +} + +/** + * @brief Set the half-duplex UART bus in RX mode + * + * @param[in] dev the device + */ +static inline void uart_half_duplex_set_rx(uart_half_duplex_t *dev) +{ + dev->size = 0; +} + +/** + * @brief Send the data contained in the driver's buffer + * + * @param[in] dev the device + * @param[in] size the number of characters to send + * + * @return the number of characters actually sent + */ +size_t uart_half_duplex_send(uart_half_duplex_t *dev, size_t size); + +/** + * @brief Recv data an fill the driver's buffer + * + * @param[in] dev the device + * @param[in] size the number of characters to receive + * + * @return the number of characters actually received + */ +size_t uart_half_duplex_recv(uart_half_duplex_t *dev, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/drivers/uart_half_duplex/uart_half_duplex.c b/drivers/uart_half_duplex/uart_half_duplex.c new file mode 100644 index 0000000000..7147899b77 --- /dev/null +++ b/drivers/uart_half_duplex/uart_half_duplex.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_uart_half_duplex + * @{ + * + * @file + * @brief Driver implementation for half-duplex UART devices + * + * @author Loïc Dauphin + * + * @} + */ + +#include "uart_half_duplex.h" + +#include "periph/uart.h" +#include "periph/gpio.h" +#include "xtimer.h" + +#define IS_SET(dir) ((dir & 1) == 0) +#define IS_CLEAR(dir) ((dir & 1) == 1) + +#define IS_PIN(dir) (dir >= UART_HALF_DUPLEX_DIR_PIN_SET(0)) +#define GET_PIN(dir) ((dir >> 1) - 1) + +static inline void _enable_tx(uart_half_duplex_dir_t dir) +{ + if (IS_PIN(dir)) { + if (IS_SET(dir)) { + gpio_set(GET_PIN(dir)); + return; + } + if (IS_CLEAR(dir)) { + gpio_clear(GET_PIN(dir)); + return; + } + } +} + +static inline void _disable_tx(uart_half_duplex_dir_t dir) +{ + if (IS_PIN(dir)) { + if (IS_SET(dir)) { + gpio_clear(GET_PIN(dir)); + return; + } + if (IS_CLEAR(dir)) { + gpio_set(GET_PIN(dir)); + return; + } + } +} + +static void _rx_cb(void* data, uint8_t c) +{ + uart_half_duplex_t *dev = data; + if (dev->size < dev->buffer_max_size) { + dev->buffer[dev->size++] = c; + } +} + +int uart_half_duplex_init(uart_half_duplex_t *dev, uint8_t *buffer, size_t buffer_max_size, const uart_half_duplex_params_t *params) +{ + if (buffer == NULL || buffer_max_size <= 7) { + return UART_HALF_DUPLEX_NOBUFF; + } + + dev->buffer = buffer; + dev->buffer_max_size = buffer_max_size; + dev->params = *params; + dev->timeout_us = UART_HALF_DUPLEX_DEFAULT_TIMEOUT_US; + + if (IS_PIN(dev->params.dir)) { + gpio_init(GET_PIN(dev->params.dir), GPIO_OUT); + } + + int ret = uart_init(dev->params.uart, dev->params.baudrate, _rx_cb, dev); + + _disable_tx(dev->params.dir); + uart_half_duplex_set_rx(dev); + + return ret; +} + +size_t uart_half_duplex_send(uart_half_duplex_t *dev, size_t size) +{ + _enable_tx(dev->params.dir); + uart_write(dev->params.uart, dev->buffer, size); + _disable_tx(dev->params.dir); + return size; +} + +size_t uart_half_duplex_recv(uart_half_duplex_t *dev, size_t size) +{ + const uint32_t begin = xtimer_now_usec(); + while (xtimer_now_usec() - begin < dev->timeout_us) { + if (dev->size >= size) { + break; + } + } + return dev->size; +}