aboutsummaryrefslogtreecommitdiff
path: root/Marlin/src/HAL/AVR/MarlinSerial.h
blob: 2834dbed35acbbf348a4aefb3c573923dbc50ea1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/**
 * Marlin 3D Printer Firmware
 * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
 *
 * Based on Sprinter and grbl.
 * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */
#pragma once

/**
 * MarlinSerial.h - Hardware serial library for Wiring
 * Copyright (c) 2006 Nicholas Zambetti.  All right reserved.
 *
 * Modified 28 September 2010 by Mark Sproul
 * Modified 14 February 2016 by Andreas Hardtung (added tx buffer)
 * Modified 01 October 2017 by Eduardo José Tagle (added XON/XOFF)
 * Templatized 01 October 2018 by Eduardo José Tagle to allow multiple instances
 */

#include <WString.h>

#include "../../inc/MarlinConfigPre.h"
#include "../../core/serial_hook.h"

#ifndef SERIAL_PORT
  #define SERIAL_PORT 0
#endif

#ifndef USBCON

  // The presence of the UBRRH register is used to detect a UART.
  #define UART_PRESENT(port) ((port == 0 && (defined(UBRRH) || defined(UBRR0H))) || \
                              (port == 1 && defined(UBRR1H)) || (port == 2 && defined(UBRR2H)) || \
                              (port == 3 && defined(UBRR3H)))

  // These are macros to build serial port register names for the selected SERIAL_PORT (C preprocessor
  // requires two levels of indirection to expand macro values properly)
  #define SERIAL_REGNAME(registerbase,number,suffix) _SERIAL_REGNAME(registerbase,number,suffix)
  #if SERIAL_PORT == 0 && (!defined(UBRR0H) || !defined(UDR0)) // use un-numbered registers if necessary
    #define _SERIAL_REGNAME(registerbase,number,suffix) registerbase##suffix
  #else
    #define _SERIAL_REGNAME(registerbase,number,suffix) registerbase##number##suffix
  #endif

  // Registers used by MarlinSerial class (expanded depending on selected serial port)

  // Templated 8bit register (generic)
  #define UART_REGISTER_DECL_BASE(registerbase, suffix) \
    template<int portNr> struct R_##registerbase##x##suffix {}

  // Templated 8bit register (specialization for each port)
  #define UART_REGISTER_DECL(port, registerbase, suffix) \
    template<> struct R_##registerbase##x##suffix<port> { \
      constexpr R_##registerbase##x##suffix(int) {} \
      FORCE_INLINE void operator=(uint8_t newVal) const { SERIAL_REGNAME(registerbase,port,suffix) = newVal; } \
      FORCE_INLINE operator uint8_t() const { return SERIAL_REGNAME(registerbase,port,suffix); } \
    }

  // Templated 1bit register (generic)
  #define UART_BIT_DECL_BASE(registerbase, suffix, bit) \
    template<int portNr>struct B_##bit##x {}

  // Templated 1bit register (specialization for each port)
  #define UART_BIT_DECL(port, registerbase, suffix, bit) \
    template<> struct B_##bit##x<port> { \
      constexpr B_##bit##x(int) {} \
      FORCE_INLINE void operator=(int newVal) const { \
        if (newVal) \
          SBI(SERIAL_REGNAME(registerbase,port,suffix),SERIAL_REGNAME(bit,port,)); \
        else \
          CBI(SERIAL_REGNAME(registerbase,port,suffix),SERIAL_REGNAME(bit,port,)); \
      } \
      FORCE_INLINE operator bool() const { return TEST(SERIAL_REGNAME(registerbase,port,suffix),SERIAL_REGNAME(bit,port,)); } \
    }

  #define UART_DECL_BASE() \
    UART_REGISTER_DECL_BASE(UCSR,A);\
    UART_REGISTER_DECL_BASE(UDR,);\
    UART_REGISTER_DECL_BASE(UBRR,H);\
    UART_REGISTER_DECL_BASE(UBRR,L);\
    UART_BIT_DECL_BASE(UCSR,B,RXEN);\
    UART_BIT_DECL_BASE(UCSR,B,TXEN);\
    UART_BIT_DECL_BASE(UCSR,A,TXC);\
    UART_BIT_DECL_BASE(UCSR,B,RXCIE);\
    UART_BIT_DECL_BASE(UCSR,A,UDRE);\
    UART_BIT_DECL_BASE(UCSR,A,FE);\
    UART_BIT_DECL_BASE(UCSR,A,DOR);\
    UART_BIT_DECL_BASE(UCSR,B,UDRIE);\
    UART_BIT_DECL_BASE(UCSR,A,RXC);\
    UART_BIT_DECL_BASE(UCSR,A,U2X)

  #define UART_DECL(port) \
    UART_REGISTER_DECL(port,UCSR,A);\
    UART_REGISTER_DECL(port,UDR,);\
    UART_REGISTER_DECL(port,UBRR,H);\
    UART_REGISTER_DECL(port,UBRR,L);\
    UART_BIT_DECL(port,UCSR,B,RXEN);\
    UART_BIT_DECL(port,UCSR,B,TXEN);\
    UART_BIT_DECL(port,UCSR,A,TXC);\
    UART_BIT_DECL(port,UCSR,B,RXCIE);\
    UART_BIT_DECL(port,UCSR,A,UDRE);\
    UART_BIT_DECL(port,UCSR,A,FE);\
    UART_BIT_DECL(port,UCSR,A,DOR);\
    UART_BIT_DECL(port,UCSR,B,UDRIE);\
    UART_BIT_DECL(port,UCSR,A,RXC);\
    UART_BIT_DECL(port,UCSR,A,U2X)

  // Declare empty templates
  UART_DECL_BASE();

  // And all the specializations for each possible serial port
  #if UART_PRESENT(0)
    UART_DECL(0);
  #endif
  #if UART_PRESENT(1)
    UART_DECL(1);
  #endif
  #if UART_PRESENT(2)
    UART_DECL(2);
  #endif
  #if UART_PRESENT(3)
    UART_DECL(3);
  #endif

  #define BYTE 0

  // Templated type selector
  template<bool b, typename T, typename F> struct TypeSelector { typedef T type;} ;
  template<typename T, typename F> struct TypeSelector<false, T, F> { typedef F type; };

  template<typename Cfg>
  class MarlinSerial {
  protected:
    // Registers
    static constexpr R_UCSRxA<Cfg::PORT> R_UCSRA = 0;
    static constexpr R_UDRx<Cfg::PORT>   R_UDR   = 0;
    static constexpr R_UBRRxH<Cfg::PORT> R_UBRRH = 0;
    static constexpr R_UBRRxL<Cfg::PORT> R_UBRRL = 0;

    // Bits
    static constexpr B_RXENx<Cfg::PORT>  B_RXEN  = 0;
    static constexpr B_TXENx<Cfg::PORT>  B_TXEN  = 0;
    static constexpr B_TXCx<Cfg::PORT>   B_TXC   = 0;
    static constexpr B_RXCIEx<Cfg::PORT> B_RXCIE = 0;
    static constexpr B_UDREx<Cfg::PORT>  B_UDRE  = 0;
    static constexpr B_FEx<Cfg::PORT>    B_FE    = 0;
    static constexpr B_DORx<Cfg::PORT>   B_DOR   = 0;
    static constexpr B_UDRIEx<Cfg::PORT> B_UDRIE = 0;
    static constexpr B_RXCx<Cfg::PORT>   B_RXC   = 0;
    static constexpr B_U2Xx<Cfg::PORT>   B_U2X   = 0;

    // Base size of type on buffer size
    typedef typename TypeSelector<(Cfg::RX_SIZE>256), uint16_t, uint8_t>::type ring_buffer_pos_t;

    struct ring_buffer_r {
      volatile ring_buffer_pos_t head, tail;
      unsigned char buffer[Cfg::RX_SIZE];
    };

    struct ring_buffer_t {
      volatile uint8_t head, tail;
      unsigned char buffer[Cfg::TX_SIZE];
    };

    static ring_buffer_r rx_buffer;
    static ring_buffer_t tx_buffer;
    static bool _written;

    static constexpr uint8_t XON_XOFF_CHAR_SENT = 0x80,  // XON / XOFF Character was sent
                             XON_XOFF_CHAR_MASK = 0x1F;  // XON / XOFF character to send

    // XON / XOFF character definitions
    static constexpr uint8_t XON_CHAR  = 17, XOFF_CHAR = 19;
    static uint8_t xon_xoff_state,
                   rx_dropped_bytes,
                   rx_buffer_overruns,
                   rx_framing_errors;
    static ring_buffer_pos_t rx_max_enqueued;

    static FORCE_INLINE ring_buffer_pos_t atomic_read_rx_head();

    static volatile bool rx_tail_value_not_stable;
    static volatile uint16_t rx_tail_value_backup;

    static FORCE_INLINE void atomic_set_rx_tail(ring_buffer_pos_t value);
    static FORCE_INLINE ring_buffer_pos_t atomic_read_rx_tail();

  public:
    FORCE_INLINE static void store_rxd_char();
    FORCE_INLINE static void _tx_udr_empty_irq();

  public:
    static void begin(const long);
    static void end();
    static int peek();
    static int read();
    static void flush();
    static ring_buffer_pos_t available();
    static size_t write(const uint8_t c);
    static void flushTX();
    #if HAS_DGUS_LCD
      static ring_buffer_pos_t get_tx_buffer_free();
    #endif

    enum { HasEmergencyParser = Cfg::EMERGENCYPARSER };
    static inline bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; }

    FORCE_INLINE static uint8_t dropped() { return Cfg::DROPPED_RX ? rx_dropped_bytes : 0; }
    FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; }
    FORCE_INLINE static uint8_t framing_errors() { return Cfg::RX_FRAMING_ERRORS ? rx_framing_errors : 0; }
    FORCE_INLINE static ring_buffer_pos_t rxMaxEnqueued() { return Cfg::MAX_RX_QUEUED ? rx_max_enqueued : 0; }
  };

  template <uint8_t serial>
  struct MarlinSerialCfg {
    static constexpr int PORT               = serial;
    static constexpr unsigned int RX_SIZE   = RX_BUFFER_SIZE;
    static constexpr unsigned int TX_SIZE   = TX_BUFFER_SIZE;
    static constexpr bool XONOFF            = ENABLED(SERIAL_XON_XOFF);
    static constexpr bool EMERGENCYPARSER   = ENABLED(EMERGENCY_PARSER);
    static constexpr bool DROPPED_RX        = ENABLED(SERIAL_STATS_DROPPED_RX);
    static constexpr bool RX_OVERRUNS       = ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS);
    static constexpr bool RX_FRAMING_ERRORS = ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS);
    static constexpr bool MAX_RX_QUEUED     = ENABLED(SERIAL_STATS_MAX_RX_QUEUED);
  };

  typedef Serial0Type< MarlinSerial< MarlinSerialCfg<SERIAL_PORT> > > MSerialT;
  extern MSerialT customizedSerial1;

  #ifdef SERIAL_PORT_2
    typedef Serial0Type< MarlinSerial< MarlinSerialCfg<SERIAL_PORT_2> > > MSerialT2;
    extern MSerialT2 customizedSerial2;
  #endif

#endif // !USBCON

#ifdef MMU2_SERIAL_PORT
  template <uint8_t serial>
  struct MMU2SerialCfg {
    static constexpr int PORT               = serial;
    static constexpr bool XONOFF            = false;
    static constexpr bool EMERGENCYPARSER   = false;
    static constexpr bool DROPPED_RX        = false;
    static constexpr bool RX_FRAMING_ERRORS = false;
    static constexpr bool MAX_RX_QUEUED     = false;
    static constexpr unsigned int RX_SIZE   = 32;
    static constexpr unsigned int TX_SIZE   = 32;
    static constexpr bool RX_OVERRUNS       = false;
  };

  typedef Serial0Type< MarlinSerial< MMU2SerialCfg<MMU2_SERIAL_PORT> > > MSerialT3;
  extern MSerial3 mmuSerial;
#endif

#ifdef LCD_SERIAL_PORT

  template <uint8_t serial>
  struct LCDSerialCfg {
    static constexpr int PORT                 = serial;
    static constexpr bool XONOFF              = false;
    static constexpr bool EMERGENCYPARSER     = ENABLED(EMERGENCY_PARSER);
    static constexpr bool DROPPED_RX          = false;
    static constexpr bool RX_FRAMING_ERRORS   = false;
    static constexpr bool MAX_RX_QUEUED       = false;
    #if HAS_DGUS_LCD
      static constexpr unsigned int RX_SIZE   = DGUS_RX_BUFFER_SIZE;
      static constexpr unsigned int TX_SIZE   = DGUS_TX_BUFFER_SIZE;
      static constexpr bool RX_OVERRUNS       = ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS);
    #elif EITHER(ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON)
      static constexpr unsigned int RX_SIZE   = 64;
      static constexpr unsigned int TX_SIZE   = 128;
      static constexpr bool RX_OVERRUNS       = false;
    #else
      static constexpr unsigned int RX_SIZE   = 64;
      static constexpr unsigned int TX_SIZE   = 128;
      static constexpr bool RX_OVERRUNS       = false
    #endif
  };


  typedef Serial0Type< MarlinSerial< LCDSerialCfg<LCD_SERIAL_PORT> > > MSerialT4;
  extern MSerialT4 lcdSerial;
#endif

// Use the UART for Bluetooth in AT90USB configurations
#if defined(USBCON) && ENABLED(BLUETOOTH)
  typedef Serial0Type<HardwareSerial> MSerialT5;
  extern MSerialT5 bluetoothSerial;
#endif