2019-01-06 15:08:23 +01:00
|
|
|
/*
|
|
|
|
* 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
|
2020-01-04 18:16:27 +01:00
|
|
|
* need to feature at least one UART and one optional GPIO that is able to raise
|
2019-01-06 15:08:23 +01:00
|
|
|
* 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
|
2021-05-27 22:20:53 +02:00
|
|
|
* 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.
|
2020-01-04 18:16:27 +01:00
|
|
|
* In this case, the bus allocation can be detected more precisely and
|
|
|
|
* collisions are less likely.
|
2019-01-06 15:08:23 +01:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2022-01-19 16:01:46 +01:00
|
|
|
* 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.
|
|
|
|
*
|
2019-01-06 15:08:23 +01:00
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file
|
|
|
|
* @brief Driver for the Differentially Operated Serial Ethernet module
|
|
|
|
*
|
|
|
|
* @author Juergen Fitschen <me@jue.yt>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef DOSE_H
|
|
|
|
#define DOSE_H
|
|
|
|
|
2021-11-14 20:03:22 +01:00
|
|
|
#include "chunked_ringbuffer.h"
|
2019-01-06 15:08:23 +01:00
|
|
|
#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"
|
2019-01-06 15:08:23 +01:00
|
|
|
|
|
|
|
#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 */
|
2019-01-06 15:08:23 +01:00
|
|
|
/** @} */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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 */
|
2021-08-17 18:59:38 +02:00
|
|
|
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 */
|
2019-01-06 15:08:23 +01:00
|
|
|
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 */
|
2019-01-06 15:08:23 +01:00
|
|
|
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 */
|
2021-11-18 18:55:37 +01:00
|
|
|
#define DOSE_FLAG_SEND_PENDING (BIT3) /**< A send operation is pending */
|
2019-01-06 15:08:23 +01:00
|
|
|
/** @} */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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 */
|
|
|
|
/** @} */
|
|
|
|
|
2020-03-31 19:19:37 +02:00
|
|
|
/**
|
|
|
|
* @defgroup drivers_dose_config Differentially Operated Serial Ethernet (DOSE) driver compile configuration
|
2020-04-10 12:14:15 +02:00
|
|
|
* @ingroup config_drivers_netdev
|
2020-03-31 19:19:37 +02:00
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
/**
|
2021-08-23 13:26:34 +02:00
|
|
|
* @brief Timeout that brings the driver back into idle state expressed as bytes.
|
2020-03-31 19:19:37 +02:00
|
|
|
*
|
|
|
|
* Fallback to idle if the remote side died within a transaction.
|
|
|
|
*/
|
2021-08-23 13:26:34 +02:00
|
|
|
#ifndef CONFIG_DOSE_TIMEOUT_BYTES
|
2022-01-19 16:01:46 +01:00
|
|
|
#define CONFIG_DOSE_TIMEOUT_BYTES (50)
|
2019-01-06 15:08:23 +01:00
|
|
|
#endif
|
2020-03-31 19:19:37 +02:00
|
|
|
/** @} */
|
2019-01-06 15:08:23 +01:00
|
|
|
|
|
|
|
#define DOSE_FRAME_CRC_LEN (2) /**< CRC16 is used */
|
2022-05-14 18:29:16 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @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
|
2019-01-06 15:08:23 +01:00
|
|
|
|
2022-01-19 16:01:46 +01:00
|
|
|
/**
|
|
|
|
* @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
|
2022-01-19 16:01:46 +01:00
|
|
|
* the RX path when high data rates / less CPU overhead is required.
|
|
|
|
*/
|
|
|
|
#if DOXYGEN
|
|
|
|
#define DOSE_TIMER_DEV TIMER_DEV(…)
|
|
|
|
#endif
|
|
|
|
|
2019-01-06 15:08:23 +01:00
|
|
|
/**
|
|
|
|
* @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 */
|
2022-05-14 18:29:16 +02:00
|
|
|
uint8_t recv_buf[CONFIG_DOSE_RX_BUF_LEN]; /**< Receive buffer for incoming frames */
|
2021-11-14 20:03:22 +01:00
|
|
|
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
|
2021-11-14 20:03:22 +01:00
|
|
|
void *recv_buf_ptr_last; /**< Last value of recv_buf_ptr when the watchdog visited */
|
2021-11-09 21:59:27 +01:00
|
|
|
#endif
|
2021-05-27 22:20:53 +02:00
|
|
|
#if !defined(MODULE_PERIPH_UART_RXSTART_IRQ) || DOXYGEN
|
2019-01-06 15:08:23 +01:00
|
|
|
gpio_t sense_pin; /**< GPIO to sense for start bits on the UART's rx line */
|
2021-05-27 22:20:53 +02:00
|
|
|
#endif
|
2021-08-17 18:59:38 +02:00
|
|
|
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 */
|
2019-01-06 15:08:23 +01:00
|
|
|
uint32_t timeout_base; /**< Base timeout in us */
|
2021-11-12 16:08:30 +01:00
|
|
|
uart_t uart; /**< UART device to use */
|
|
|
|
uint8_t uart_octet; /**< Last received octet */
|
|
|
|
uint8_t flags; /**< Several flags */
|
2019-01-06 15:08:23 +01:00
|
|
|
} dose_t;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Struct containing the required configuration
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
uart_t uart; /**< UART device to use */
|
2021-05-27 22:20:53 +02:00
|
|
|
#if !defined(MODULE_PERIPH_UART_RXSTART_IRQ) || DOXYGEN
|
2019-01-06 15:08:23 +01:00
|
|
|
gpio_t sense_pin; /**< GPIO to sense for start bits on the UART's rx line */
|
2021-05-27 22:20:53 +02:00
|
|
|
#endif
|
2021-08-17 18:59:38 +02:00
|
|
|
gpio_t standby_pin; /**< GPIO to put the CAN transceiver in standby mode */
|
2019-01-06 15:08:23 +01:00
|
|
|
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
|
2020-07-07 20:51:43 +02:00
|
|
|
* @param[in] index Index of @p params in a global parameter struct array.
|
|
|
|
* If initialized manually, pass a unique identifier instead.
|
2019-01-06 15:08:23 +01:00
|
|
|
*/
|
2020-07-07 20:51:43 +02:00
|
|
|
void dose_setup(dose_t *dev, const dose_params_t *params, uint8_t index);
|
2019-01-06 15:08:23 +01:00
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif /* DOSE_H */
|
|
|
|
/** @} */
|