| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title UsartDriver.hpp | ||
| 3 | // \author tumbar | ||
| 4 | // \brief hpp file for UsartDriver component implementation class | ||
| 5 | // ====================================================================== | ||
| 6 | |||
| 7 | #ifndef Samd21_UsartDriver_HPP | ||
| 8 | #define Samd21_UsartDriver_HPP | ||
| 9 | |||
| 10 | #include "Fw/DataStructures/FifoQueue.hpp" | ||
| 11 | #include "fprime-samd/Drv/Types/SercomKindEnumAc.hpp" | ||
| 12 | #include "fprime-samd/Drv/Types/ThinBuffer.hpp" | ||
| 13 | #include "fprime-samd/Drv/UsartDriver/UsartDriverComponentAc.hpp" | ||
| 14 | #include "samd-config/UsartDriverConfig.hpp" | ||
| 15 | |||
| 16 | namespace Samd21 { | ||
| 17 | |||
| 18 | class UsartDriver final : public UsartDriverComponentBase { | ||
| 19 | public: | ||
| 20 | // ---------------------------------------------------------------------- | ||
| 21 | // Component construction and destruction | ||
| 22 | // ---------------------------------------------------------------------- | ||
| 23 | |||
| 24 | //! Construct UsartDriver object | ||
| 25 | UsartDriver(const char* const compName //!< The component name | ||
| 26 | ); | ||
| 27 | |||
| 28 | //! Destroy UsartDriver object | ||
| 29 | ~UsartDriver(); | ||
| 30 | |||
| 31 | // ---------------------------------------------------------------------- | ||
| 32 | // Component configuration | ||
| 33 | // ---------------------------------------------------------------------- | ||
| 34 | |||
| 35 | enum class ClockMode : U8 { | ||
| 36 | EXTERNAL = 0x0, //!< Drive baud on the external XCK pad | ||
| 37 | INTERNAL = 0x1, //!< Drive the baud off a prescaled GCLK0 | ||
| 38 | }; | ||
| 39 | |||
| 40 | enum class CommunicationMode : U8 { | ||
| 41 | ASYNC = 0x0, //!< Asynchronous UART with TX/RX | ||
| 42 | SYNC = 0x1, //!< Synchronous UART with TX/RX, RTS/CTS | ||
| 43 | }; | ||
| 44 | |||
| 45 | // These bits define the receive data (RxD) pin configuration. | ||
| 46 | enum class RxPinOut : U8 { | ||
| 47 | PAD0 = 0, //!< SERCOM PAD[0] is used for data reception | ||
| 48 | PAD1 = 1, //!< SERCOM PAD[1] is used for data reception | ||
| 49 | PAD2 = 2, //!< SERCOM PAD[2] is used for data reception | ||
| 50 | PAD3 = 3, //!< SERCOM PAD[3] is used for data reception | ||
| 51 | }; | ||
| 52 | |||
| 53 | enum class TxPinOut : U8 { | ||
| 54 | PAD0 = 0, //!< TX: SERCOM PAD[0], XCK: SERCOM PAD[1] | ||
| 55 | PAD2_XCK_PAD3 = 1, //!< TX: SERCOM PAD[2], XCK: SERCOM PAD[3] | ||
| 56 | PAD0_RTS_PAD2_CTS_PAD3 = 2, //!< TX: SERCOM PAD[0], RTS: SERCOM PAD[2], CTS: SERCOM PAD[3] | ||
| 57 | }; | ||
| 58 | |||
| 59 | enum class DataOrder : U8 { | ||
| 60 | MSB_FIRST = 0x0, //!< MSB is transmitted first | ||
| 61 | LSB_FIRST = 0x1, //!< LSB is transmitted first | ||
| 62 | }; | ||
| 63 | |||
| 64 | enum class Parity : U8 { | ||
| 65 | NONE, | ||
| 66 | EVEN, | ||
| 67 | ODD, | ||
| 68 | }; | ||
| 69 | |||
| 70 | enum class StopBits : U8 { | ||
| 71 | ONE = 0x0, | ||
| 72 | TWO = 0x1, | ||
| 73 | }; | ||
| 74 | |||
| 75 | enum class DataBits : U8 { | ||
| 76 | BITS_8 = 0x0, | ||
| 77 | BITS_9 = 0x1, | ||
| 78 | BITS_5 = 0x5, | ||
| 79 | BITS_6 = 0x6, | ||
| 80 | BITS_7 = 0x7, | ||
| 81 | }; | ||
| 82 | |||
| 83 | enum class BaudRate : U32 { | ||
| 84 | BAUD_9600 = 9600, | ||
| 85 | BAUD_19200 = 19200, | ||
| 86 | BAUD_38400 = 38400, | ||
| 87 | BAUD_57600 = 57600, | ||
| 88 | BAUD_115200 = 115200, //!< Modern default | ||
| 89 | BAUD_230400 = 230400, | ||
| 90 | BAUD_460800 = 460800, | ||
| 91 | BAUD_921600 = 921600, | ||
| 92 | }; | ||
| 93 | |||
| 94 | void configure(SercomKind sercom, | ||
| 95 | RxPinOut rx, | ||
| 96 | TxPinOut tx, | ||
| 97 | ClockMode clock, | ||
| 98 | CommunicationMode mode, | ||
| 99 | BaudRate baud_rate, | ||
| 100 | DataOrder data_order, | ||
| 101 | DataBits data_bits, | ||
| 102 | StopBits stop_bits, | ||
| 103 | Parity parity); | ||
| 104 | |||
| 105 | private: | ||
| 106 | // ---------------------------------------------------------------------- | ||
| 107 | // Handler implementations for typed input ports | ||
| 108 | // ---------------------------------------------------------------------- | ||
| 109 | |||
| 110 | //! Service the UART driver: | ||
| 111 | //! 1. Return Tx buffers that finished DMA transmission. | ||
| 112 | //! 2. Detect IDLE Rx. Pull the in-progress DMA Rx and send it downstream | ||
| 113 | void schedIn_handler(FwIndexType portNum, U32 context) override; | ||
| 114 | |||
| 115 | //! Handler implementation for dmaReplyIn | ||
| 116 | //! | ||
| 117 | //! A signal from the DMAC that a request has finished. | ||
| 118 | //! This signal comes inside an ISR! | ||
| 119 | void dmaReplyIn_handler(FwIndexType portNum, //!< The port number | ||
| 120 | const Samd21::Dma::Reply& reply) override; | ||
| 121 | |||
| 122 | //! Handler implementation for recvReturnIn | ||
| 123 | //! | ||
| 124 | //! Port receiving back ownership of data sent out on $recv port | ||
| 125 | void recvReturnIn_handler(FwIndexType portNum, //!< The port number | ||
| 126 | Fw::Buffer& fwBuffer //!< The buffer | ||
| 127 | ) override; | ||
| 128 | |||
| 129 | //! Handler implementation for send | ||
| 130 | //! | ||
| 131 | //! Invoke this port to send data out the driver (asynchronous) | ||
| 132 | //! Status and ownership of the buffer are returned through the sendReturnOut callback | ||
| 133 | //! send buffer to the DMAC synchronously | ||
| 134 | void send_handler(FwIndexType portNum, //!< The port number | ||
| 135 | Fw::Buffer& fwBuffer //!< The buffer | ||
| 136 | ) override; | ||
| 137 | |||
| 138 | //! Handler for input port activeIn | ||
| 139 | //! | ||
| 140 | //! Returns true iff at least one signal was dequeued and processed this | ||
| 141 | //! tick (so the cycler should re-invoke us); false if idle (not configured | ||
| 142 | //! or the queue was empty). | ||
| 143 | bool activeIn_handler(FwIndexType portNum, //!< The port number | ||
| 144 | U32 context //!< The call order | ||
| 145 | ) override; | ||
| 146 | |||
| 147 | //! Handler for input port sendSync | ||
| 148 | Drv::ByteStreamStatus sendSync_handler(FwIndexType portNum, //!< The port number | ||
| 149 | Fw::Buffer& sendBuffer //!< Data to send | ||
| 150 | ) override; | ||
| 151 | |||
| 152 | // ---------------------------------------------------------------------- | ||
| 153 | // Helper functions | ||
| 154 | // ---------------------------------------------------------------------- | ||
| 155 | |||
| 156 | //! An enum tracking the | ||
| 157 | enum class RxDmaBufferID : U8 { | ||
| 158 | A, | ||
| 159 | B, | ||
| 160 | INVALID, | ||
| 161 | }; | ||
| 162 | |||
| 163 | //! Handle a reply on the TX DMA channel | ||
| 164 | void dmaReplyTxIsr(const Samd21::Dma::Reply& reply); | ||
| 165 | |||
| 166 | //! Handle a reply on the RX DMA channel | ||
| 167 | void dmaReplyRxIsr(const Samd21::Dma::Reply& reply); | ||
| 168 | |||
| 169 | //! Send a buffer to the RX DMA channel | ||
| 170 | void dmaQueueRxSend(const ThinBuffer& buffer); | ||
| 171 | |||
| 172 | // ---------------------------------------------------------------------- | ||
| 173 | // Member variables | ||
| 174 | // ---------------------------------------------------------------------- | ||
| 175 | |||
| 176 | //! SERCOM peripheral this driver controls | ||
| 177 | SercomKind m_sercom; | ||
| 178 | |||
| 179 | //! Fifo tracking the queue of TX DMA transactions. | ||
| 180 | //! DMA replies should come back in the same order | ||
| 181 | Fw::FifoQueue<ThinBuffer, UsartDriverConfig::USART_TX_BUFFER_N> m_tx_queue; | ||
| 182 | |||
| 183 | struct RxBuffer { | ||
| 184 | U8 data[USART_RX_BUFFER_SIZE]; | ||
| 185 | }; | ||
| 186 | |||
| 187 | //! Rx buffers (A and B) for receiving data over the DMA | ||
| 188 | RxBuffer m_rx[2]; | ||
| 189 | RxDmaBufferID m_active_rx; | ||
| 190 | |||
| 191 | //! Tracks whether configure() as been called or not | ||
| 192 | bool m_configured; | ||
| 193 | |||
| 194 | U16 m_active_processed; | ||
| 195 | |||
| 196 | //! m_rxOverflows: SERCOM STATUS.BUFOVF events (silent hardware RX drops). | ||
| 197 | U32 m_rxOverflows; | ||
| 198 | |||
| 199 | //! m_schedSkips: partial-read passes skipped by the high-RX watchdog. | ||
| 200 | U32 m_schedSkips; | ||
| 201 | |||
| 202 | //! High-RX watchdog. dmaReplyRxIsr bumps m_rx_activity on every RX | ||
| 203 | //! completion (ISR context); schedIn compares it against the value seen on | ||
| 204 | //! the previous tick. If it advanced, RX is actively moving full buffers, so | ||
| 205 | //! schedIn skips the partial read -- which would suspend the RX DMA channel | ||
| 206 | //! (see DmaChannel::readWriteback) and risk a BUFOVF drop. Full buffers are | ||
| 207 | //! delivered losslessly via RX_BUFFER_DONE, so skipping loses nothing; the | ||
| 208 | //! partial read only matters during genuine idle to extract a sub-buffer tail. | ||
| 209 | volatile U32 m_rx_activity; | ||
| 210 | U32 m_last_sched_activity; | ||
| 211 | |||
| 212 | U32 m_rxBytes; | ||
| 213 | U32 m_txBytes; | ||
| 214 | |||
| 215 | //! Signals sent to the internal queue for processing during schedIn | ||
| 216 | enum class SignalKind : U8 { | ||
| 217 | TX_BUFFER_OK, //!< A buffer has been TXed over the DMA successfully | ||
| 218 | RX_BUFFER_PARTIAL, //!< An RX buffer has been partially filled and is ready for processing | ||
| 219 | RX_BUFFER_DONE, //!< An RX buffer has been filled and is ready for processing | ||
| 220 | }; | ||
| 221 | |||
| 222 | struct Signal { | ||
| 223 | SignalKind kind; | ||
| 224 | U16 rx_bytes; | ||
| 225 | |||
| 226 | ✗ | Signal() : kind(SignalKind::TX_BUFFER_OK), rx_bytes(0) {} | |
| 227 | ✗ | explicit Signal(SignalKind kind_, U16 rx_bytes_) : kind(kind_), rx_bytes(rx_bytes_) {} | |
| 228 | }; | ||
| 229 | |||
| 230 | Fw::FifoQueue<Signal, UsartDriverConfig::USART_QUEUE_DEPTH> m_queue; | ||
| 231 | }; | ||
| 232 | |||
| 233 | } // namespace Samd21 | ||
| 234 | |||
| 235 | #endif | ||
| 236 |