1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/include/dose.h

238 lines
9.1 KiB
C
Raw Normal View History

/*
* Copyright (C) 2019 Juergen Fitschen <me@jue.yt>
*
* 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_dose Differentially Operated Serial Ethernet
* @ingroup drivers_netdev
* @brief Driver for connecting RIOT devices using a single bus wire
*
* About
* =====
*
* This driver enables RIOT nodes to communicate by Ethernet over a serial bus.
* This enables them to interact in an easy and cheap manner using a single
* bus wire with very low hardware requirements: The used microcontrollers just
* need to feature at least one UART and one optional GPIO that is able to raise
* interrupts.
*
* Wiring
* ======
*
* ![DOSE wiring](dose-wiring.svg)
*
* For bus access, you need a CAN transceiver, since the DOSE uses the PHY layer
* of CAN for the electrical connection of the nodes. Every transceiver IC
* operating with the right voltage levels should do. (If you are on a 3.3V MCU,
* you could use an IC such as the SN65HVD233.)
*
* Basically, UART TX and RX are connected to respective pins of the
* transceiver. In addition, the RX pin can also be connected to the sense GPIO
* if the UART does not implement the `periph_uart_rxstart_irq` feature.
* In this case, the bus allocation can be detected more precisely and
* collisions are less likely.
*
* How it works
* ============
*
* Some technical details for those interested: The Ethernet frames are sent
* onto the bus using `uart_write()` while observing the received echo from
* the bus. This way collisions are detected (received echo != transmitted
* octet) and retransmissions are scheduled. The frames are appended with a
* CRC16 to protect the system from transmission errors.
*
* A note on high data rates
* =========================
*
* When using high UART data rates (1 MBit/s and above) per-byte overhead
* becomes significant.
* A major factor here is setting (software) timers which are used to catch
* error conditions.
* To speed up the TX path it is therefore recommended to implement hardware
* collision detection (if available) to avoid the need for setting a timeout
* for each byte transmitted.
*
* To speed up the more critical RX path, enable the `dose_watchdog` module.
* This requires a dedicated hardware timer `DOSE_TIMER_DEV` to be configured
* in e.g. `board.h`.
* The timer is shared between all DOSE interfaces and will periodically check
* if any interface does not make progress with receiving a frame (payload
* marker did not advance between two timer periods) and abort the RX process.
*
* @{
*
* @file
* @brief Driver for the Differentially Operated Serial Ethernet module
*
* @author Juergen Fitschen <me@jue.yt>
*/
#ifndef DOSE_H
#define DOSE_H
#include "chunked_ringbuffer.h"
#include "periph/uart.h"
#include "periph/gpio.h"
#include "net/netdev.h"
#include "net/ethernet.h"
#include "net/eui48.h"
#include "bitarithm.h"
#include "mutex.h"
2022-01-24 17:43:32 +01:00
#include "ztimer.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Escape octet definitions
* @{
*/
2020-01-04 18:04:18 +01:00
#define DOSE_OCTET_END (0xFF) /**< Magic octet indicating the end of frame */
#define DOSE_OCTET_ESC (0xFE) /**< Magic octet escaping 0xFF in byte stream */
/** @} */
/**
* @name State definitions
* @brief The drivers internal state that is hold in dose_t.state
*/
typedef enum {
DOSE_STATE_INIT = 0x00, /**< Initial state that will never be reentered */
DOSE_STATE_BLOCKED = 0x01, /**< The driver just listens to incoming frames and blocks outgress frames */
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;
/**
* @name Signal definitions
* @brief A signal controls the state machine and may cause a state transition
*/
typedef enum {
DOSE_SIGNAL_NONE = 0x00, /**< No signal ... */
DOSE_SIGNAL_INIT = 0x10, /**< Init the state machine */
DOSE_SIGNAL_GPIO = 0x20, /**< Sense GPIO detected a falling edge */
DOSE_SIGNAL_UART = 0x30, /**< Octet has been received */
2022-01-24 17:43:32 +01:00
DOSE_SIGNAL_ZTIMER = 0x40, /**< Timer timed out */
DOSE_SIGNAL_SEND = 0x50, /**< Enter send state */
DOSE_SIGNAL_END = 0x60 /**< Leave send state */
} dose_signal_t;
/**
* @name Flag definitions
* @brief Hold in dose_t.flags
* @{
*/
#define DOSE_FLAG_RECV_BUF_DIRTY (BIT0) /**< Receive buffer contains a complete unhandled frame */
#define DOSE_FLAG_END_RECEIVED (BIT1) /**< END octet has been received */
#define DOSE_FLAG_ESC_RECEIVED (BIT2) /**< ESC octet has been received */
#define DOSE_FLAG_SEND_PENDING (BIT3) /**< A send operation is pending */
/** @} */
/**
* @name Opt definitions
* @brief Hold in dose_t.opts
* @{
*/
#define DOSE_OPT_PROMISCUOUS (BIT0) /**< Don't check the destination MAC - pass every frame to upper layers */
/** @} */
/**
* @defgroup drivers_dose_config Differentially Operated Serial Ethernet (DOSE) driver compile configuration
* @ingroup config_drivers_netdev
* @{
*/
/**
* @brief Timeout that brings the driver back into idle state expressed as bytes.
*
* Fallback to idle if the remote side died within a transaction.
*/
#ifndef CONFIG_DOSE_TIMEOUT_BYTES
#define CONFIG_DOSE_TIMEOUT_BYTES (50)
#endif
/** @} */
#define DOSE_FRAME_CRC_LEN (2) /**< CRC16 is used */
/**
* @brief DOSE RX buffer length
* Should be large enough to fit at least one Ethernet frame
*/
#ifndef CONFIG_DOSE_RX_BUF_LEN
#define CONFIG_DOSE_RX_BUF_LEN (ETHERNET_FRAME_LEN + DOSE_FRAME_CRC_LEN)
#endif
/**
* @brief Hardware timer to use with the `dose_watchdog` module.
*
2022-01-24 17:43:32 +01:00
* This will be used to detect RX timeout instead of ztimer to speed up
* the RX path when high data rates / less CPU overhead is required.
*/
#if DOXYGEN
#define DOSE_TIMER_DEV TIMER_DEV(…)
#endif
/**
* @brief DOSE netdev device
* @extends netdev_t
*/
typedef struct {
netdev_t netdev; /**< Extended netdev structure */
eui48_t mac_addr; /**< This device's MAC address */
uint8_t opts; /**< Driver options */
dose_state_t state; /**< Current state of the driver's state machine */
mutex_t state_mtx; /**< Is unlocked every time a state is (re)entered */
uint8_t recv_buf[CONFIG_DOSE_RX_BUF_LEN]; /**< Receive buffer for incoming frames */
chunk_ringbuf_t rb; /**< Ringbuffer to store received frames. */
/* Written to from interrupts (with irq_disable */
/* to prevent any simultaneous writes), */
/* consumed exclusively in the network stack's */
/* loop at _isr. */
2021-11-09 21:59:27 +01:00
#if defined(MODULE_DOSE_WATCHDOG) || DOXYGEN
void *recv_buf_ptr_last; /**< Last value of recv_buf_ptr when the watchdog visited */
2021-11-09 21:59:27 +01:00
#endif
#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 */
2022-01-24 17:43:32 +01:00
ztimer_t timeout; /**< Timeout timer ensuring always to get back to IDLE state */
uint32_t timeout_base; /**< Base timeout in us */
uart_t uart; /**< UART device to use */
uint8_t uart_octet; /**< Last received octet */
uint8_t flags; /**< Several flags */
} dose_t;
/**
* @brief Struct containing the required configuration
*/
typedef struct {
uart_t uart; /**< UART device to use */
#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;
/**
* @brief Setup a DOSE based device state
* @param[out] dev Handle of the device to initialize
* @param[in] params Parameters for device initialization
* @param[in] index Index of @p params in a global parameter struct array.
* If initialized manually, pass a unique identifier instead.
*/
void dose_setup(dose_t *dev, const dose_params_t *params, uint8_t index);
#ifdef __cplusplus
}
#endif
#endif /* DOSE_H */
/** @} */