FastArduino  v1.7
C++ library to build fast but small Arduino/AVR projects
uart.h
Go to the documentation of this file.
1 // Copyright 2016-2021 Jean-Francois Poilpret
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
16 
21 #ifndef UART_HH
22 #define UART_HH
23 
24 #include "boards/board_traits.h"
25 #include "interrupts.h"
26 #include "uart_commons.h"
27 #include "streams.h"
28 
29 // Only MCU with physical USART are supported (not ATtiny then)
30 #if defined(UCSR0A) || defined(UCSR1A)
31 
37 #define REGISTER_UATX_ISR(UART_NUM) \
38  ISR(CAT3(USART, UART_NUM, _UDRE_vect)) \
39  { \
40  serial::hard::isr_handler::uatx<UART_NUM>(); \
41  }
42 
48 #define REGISTER_UARX_ISR(UART_NUM) \
49  ISR(CAT3(USART, UART_NUM, _RX_vect)) \
50  { \
51  serial::hard::isr_handler::uarx<UART_NUM>(); \
52  }
53 
59 #define REGISTER_UART_ISR(UART_NUM) \
60  ISR(CAT3(USART, UART_NUM, _UDRE_vect)) \
61  { \
62  serial::hard::isr_handler::uart_tx<UART_NUM>(); \
63  } \
64  \
65  ISR(CAT3(USART, UART_NUM, _RX_vect)) \
66  { \
67  serial::hard::isr_handler::uart_rx<UART_NUM>(); \
68  }
69 
70 namespace serial
71 {
78  namespace hard
79  {
80  }
81 }
82 
83 namespace serial::hard
84 {
85  //TODO Handle generic errors coming from UART TX (which errors?) in addition to internal overflow
87  class AbstractUART
88  {
89  protected:
90  AbstractUART() = default;
91  AbstractUART(const AbstractUART&) = delete;
92  AbstractUART& operator=(const AbstractUART&) = delete;
93 
94  struct SpeedSetup
95  {
96  constexpr SpeedSetup(uint16_t ubrr_value, bool u2x) : ubrr_value_{ubrr_value}, u2x_{u2x} {}
97  const uint16_t ubrr_value_;
98  const bool u2x_;
99  };
100 
101  static constexpr SpeedSetup compute_speed(uint32_t rate)
102  {
103  const uint16_t double_rate = UBRR_double(rate);
104  if (double_rate < DOUBLE_SPEED_RATE_LIMIT)
105  return SpeedSetup(double_rate, true);
106  else
107  return SpeedSetup(UBRR_single(rate), false);
108  }
109 
110  template<board::USART USART>
111  static void begin_(uint32_t rate, Parity parity, StopBits stop_bits,
113  {
114  using TRAIT = board_traits::USART_trait<USART>;
115  constexpr uint8_t UCSRB_TX = TRAIT::TX_ENABLE_MASK | TRAIT::UDRIE_MASK;
116  constexpr uint8_t UCSRB_RX = TRAIT::RX_ENABLE_MASK | TRAIT::RXCIE_MASK;
117  const uint8_t UCSRB_MASK = ((out != nullptr) ? UCSRB_TX : 0U) | ((in != nullptr) ? UCSRB_RX : 0U);
118  SpeedSetup setup = compute_speed(rate);
119  const uint8_t UCSRA_MASK = (setup.u2x_ ? TRAIT::U2X_MASK : 0);
120  synchronized
121  {
122  TRAIT::UBRR = setup.ubrr_value_;
123  TRAIT::UCSRA = UCSRA_MASK;
124  TRAIT::UCSRB |= UCSRB_MASK;
125  TRAIT::UCSRC = TRAIT::UCSRC_value(parity, stop_bits);
126  }
127  if (out != nullptr) out->queue().unlock();
128  }
129 
130  template<board::USART USART>
131  static void end_(BufferHandling buffer_handling, streams::istreambuf* in , streams::ostreambuf* out)
132  {
133  using TRAIT = board_traits::USART_trait<USART>;
134  if (out != nullptr)
135  {
136  out->queue().lock();
137  if (buffer_handling == BufferHandling::CLEAR)
138  out->queue().clear();
139  else if (buffer_handling == BufferHandling::FLUSH)
140  out->pubsync();
141  }
142  constexpr uint8_t UCSRB_TX = TRAIT::TX_ENABLE_MASK | TRAIT::UDRIE_MASK;
143  constexpr uint8_t UCSRB_RX = TRAIT::RX_ENABLE_MASK | TRAIT::RXCIE_MASK;
144  const uint8_t UCSRB_MASK = ((out != nullptr) ? UCSRB_TX : 0U) | ((in != nullptr) ? UCSRB_RX : 0U);
145  synchronized TRAIT::UCSRB &= ~UCSRB_MASK;
146  if ((in != nullptr) && (buffer_handling == BufferHandling::CLEAR))
147  in->queue().clear();
148  }
149 
150  private:
151  static constexpr const uint16_t DOUBLE_SPEED_RATE_LIMIT = 4096;
152 
153  static constexpr uint16_t UBRR_double(uint32_t rate)
154  {
155  return ((F_CPU / 4 / rate) - 1) / 2;
156  }
157  static constexpr uint16_t UBRR_single(uint32_t rate)
158  {
159  return ((F_CPU / 8 / rate) - 1) / 2;
160  }
161  };
162 
163  class AbstractUATX : public AbstractUART
164  {
165  public:
170  streams::ostream out()
171  {
172  return streams::ostream(obuf_);
173  }
174 
175  protected:
176  using CALLBACK = streams::ostreambuf::CALLBACK;
177 
178  template<uint8_t SIZE_TX>
179  AbstractUATX(char (&output)[SIZE_TX], CALLBACK callback, void* arg)
180  : obuf_{output, callback, arg} {}
181 
182  streams::ostreambuf& out_()
183  {
184  return obuf_;
185  }
186 
187  template<board::USART USART>
188  void data_register_empty(Errors& errors)
189  {
190  using TRAIT = board_traits::USART_trait<USART>;
191  errors.has_errors = 0;
192  char value;
193  if (obuf_.queue().pull_(value))
194  TRAIT::UDR = value;
195  else
196  {
197  transmitting_ = false;
198  // Clear UDRIE to prevent UDR interrupt to go on forever
199  TRAIT::UCSRB &= bits::COMPL(TRAIT::UDRIE_MASK);
200  }
201  }
202 
203  template<board::USART USART>
204  void on_put(Errors& errors)
205  {
206  using TRAIT = board_traits::USART_trait<USART>;
207  errors.queue_overflow = obuf_.overflow();
208  synchronized
209  {
210  // Check if TX is not currently active, if so, activate it
211  if (!transmitting_)
212  {
213  // Yes, trigger TX
214  char value;
215  if (obuf_.queue().pull_(value))
216  {
217  // Set UDR interrupt to be notified when we can send the next character
218  TRAIT::UCSRB |= TRAIT::UDRIE_MASK;
219  TRAIT::UDR = value;
220  transmitting_ = true;
221  }
222  }
223  }
224  }
225 
226  private:
227  streams::ostreambuf obuf_;
228  bool transmitting_ = false;
229  };
231 
240  template<board::USART USART_> class UATX : public AbstractUATX, public UARTErrors
241  {
242  public:
244  static constexpr const board::USART USART = USART_;
245 
246  private:
247  using THIS = UATX<USART_>;
248  using TRAIT = board_traits::USART_trait<USART>;
249 
250  public:
259  template<uint8_t SIZE_TX> explicit UATX(char (&output)[SIZE_TX])
260  : AbstractUATX{output, THIS::on_put, this}
261  {
263  }
264 
275  void begin(uint32_t rate, Parity parity = Parity::NONE, StopBits stop_bits = StopBits::ONE)
276  {
277  AbstractUART::begin_<USART>(rate, parity, stop_bits, nullptr, &out_());
278  }
279 
288  void end(BufferHandling buffer_handling = BufferHandling::KEEP)
289  {
290  AbstractUART::end_<USART>(buffer_handling, nullptr, &out_());
291  }
292 
293  private:
294  // Listeners of events on the buffer
295  static void on_put(void* arg)
296  {
297  THIS& target = *((THIS *) arg);
298  target.AbstractUATX::on_put<USART>(target.errors());
299  }
300 
301  void data_register_empty()
302  {
303  AbstractUATX::data_register_empty<USART>(errors());
304  }
305 
306  friend struct isr_handler;
307  };
308 
310  class AbstractUARX : public AbstractUART
311  {
312  public:
317  streams::istream in()
318  {
319  return streams::istream(ibuf_);
320  }
321 
322  protected:
323  template<uint8_t SIZE_RX> explicit AbstractUARX(char (&input)[SIZE_RX]) : ibuf_{input} {}
324 
325  streams::istreambuf& in_()
326  {
327  return ibuf_;
328  }
329 
330  template<board::USART USART>
331  void data_receive_complete(Errors& errors)
332  {
333  using TRAIT = board_traits::USART_trait<USART>;
334  uint8_t status = TRAIT::UCSRA;
335  errors.data_overrun = status & TRAIT::DOR_MASK;
336  errors.frame_error = status & TRAIT::FE_MASK;
337  errors.parity_error = status & TRAIT::UPE_MASK;
338  uint8_t value = TRAIT::UDR;
339  errors.queue_overflow = !ibuf_.queue().push_(value);
340  }
341 
342  private:
343  streams::istreambuf ibuf_;
344  };
346 
355  template<board::USART USART_> class UARX : public AbstractUARX, public UARTErrors
356  {
357  public:
359  static constexpr const board::USART USART = USART_;
360 
361  private:
362  using TRAIT = board_traits::USART_trait<USART>;
363 
364  public:
374  template<uint8_t SIZE_RX> explicit UARX(char (&input)[SIZE_RX]) : AbstractUARX{input}
375  {
377  }
378 
389  void begin(uint32_t rate, Parity parity = Parity::NONE, StopBits stop_bits = StopBits::ONE)
390  {
391  AbstractUART::begin_<USART>(rate, parity, stop_bits, &in_(), nullptr);
392  }
393 
402  void end(BufferHandling buffer_handling = BufferHandling::KEEP)
403  {
404  AbstractUART::end_<USART>(buffer_handling, &in_(), nullptr);
405  }
406 
407  private:
408  void data_receive_complete()
409  {
410  AbstractUARX::data_receive_complete<USART>(errors());
411  }
412 
413  friend struct isr_handler;
414  };
415 
424  template<board::USART USART_> class UART : public AbstractUARX, public AbstractUATX, public UARTErrors
425  {
426  public:
428  static constexpr const board::USART USART = USART_;
429 
430  private:
431  using THIS = UART<USART_>;
432  using TRAIT = board_traits::USART_trait<USART>;
433 
434  public:
449  template<uint8_t SIZE_RX, uint8_t SIZE_TX>
450  UART(char (&input)[SIZE_RX], char (&output)[SIZE_TX])
451  : AbstractUARX{input}, AbstractUATX{output, THIS::on_put, this}
452  {
454  }
455 
466  void begin(uint32_t rate, Parity parity = Parity::NONE, StopBits stop_bits = StopBits::ONE)
467  {
468  AbstractUART::begin_<USART>(rate, parity, stop_bits, &in_(), &out_());
469  }
470 
479  void end(BufferHandling buffer_handling = BufferHandling::KEEP)
480  {
481  AbstractUART::end_<USART>(buffer_handling, &in_(), &out_());
482  }
483 
484  private:
485  // Listeners of events on the buffer
486  static void on_put(void* arg)
487  {
488  THIS& target = *((THIS*) arg);
489  target.AbstractUATX::on_put<USART>(target.errors());
490  }
491 
492  void data_register_empty()
493  {
494  AbstractUATX::data_register_empty<USART>(errors());
495  }
496 
497  void data_receive_complete()
498  {
499  AbstractUARX::data_receive_complete<USART>(errors());
500  }
501 
502  friend struct isr_handler;
503  };
504 
505  // All UART-related methods called by pre-defined ISR are defined here
506  //=====================================================================
508  struct isr_handler
509  {
510  template<uint8_t UART_NUM_> static constexpr board::USART check_uart()
511  {
512  constexpr board::USART USART = (board::USART) UART_NUM_;
513  static_assert(board_traits::USART_trait<USART>::U2X_MASK != 0,
514  "UART_NUM must be an actual USART in target MCU");
515  return USART;
516  }
517 
518  template<uint8_t UART_NUM_> static void uatx()
519  {
520  static constexpr board::USART USART = check_uart<UART_NUM_>();
521  interrupt::HandlerHolder<UATX<USART>>::handler()->data_register_empty();
522  }
523 
524  template<uint8_t UART_NUM_> static void uarx()
525  {
526  static constexpr board::USART USART = check_uart<UART_NUM_>();
527  interrupt::HandlerHolder<UARX<USART>>::handler()->data_receive_complete();
528  }
529 
530  template<uint8_t UART_NUM_> static void uart_tx()
531  {
532  static constexpr board::USART USART = check_uart<UART_NUM_>();
533  interrupt::HandlerHolder<UART<USART>>::handler()->data_register_empty();
534  }
535 
536  template<uint8_t UART_NUM_> static void uart_rx()
537  {
538  static constexpr board::USART USART = check_uart<UART_NUM_>();
539  interrupt::HandlerHolder<UART<USART>>::handler()->data_receive_complete();
540  }
541  };
543 }
544 
545 #endif /* UCSR0A */
546 #endif /* UART_HH */
547 
serial::hard::UARX::end
void end(BufferHandling buffer_handling=BufferHandling::KEEP)
Stop reception.
Definition: uart.h:402
serial::hard::UARX::USART
static constexpr const board::USART USART
The hardware board::USART used by this UARX.
Definition: uart.h:359
serial::Parity
Parity
Parity used for serial transmission.
Definition: uart_commons.h:38
serial
Defines all API for UART features.
Definition: soft_uart.h:84
streams::ostream
Output stream wrapper to provide formatted output API, a la C++.
Definition: streams.h:61
serial::UARTErrors
Holder of latest UART errors.
Definition: uart_commons.h:96
streams::istreambuf
Input API based on a ring buffer.
Definition: streambuf.h:209
streams.h
C++-like std::iostream facilities.
board::USART
USART
Defines all USART modules of ATmega644.
Definition: atmega_xx4.h:305
serial::hard::UATX::UATX
UATX(char(&output)[SIZE_TX])
Construct a new hardware serial transmitter and provide it with a buffer for interrupt-based transmis...
Definition: uart.h:259
serial::hard::UATX::USART
static constexpr const board::USART USART
The hardware board::USART used by this UATX.
Definition: uart.h:244
uart_commons.h
Common definitions for serial API.
serial::Parity::NONE
@ NONE
No parity bit.
serial::hard::UART::USART
static constexpr const board::USART USART
The hardware board::USART used by this UART.
Definition: uart.h:428
serial::hard::UARX
Hardware serial receiver API.
Definition: uart.h:356
serial::hard::UATX
Hardware serial transmitter API.
Definition: uart.h:241
streams::ostreambuf
Output API based on a ring buffer.
Definition: streambuf.h:46
bits::COMPL
static constexpr uint8_t COMPL(uint8_t value)
Return the uint8_t 2-complement of a byte.
Definition: bits.h:253
serial::hard
Defines API types used by hardware UART features.
Definition: uart.h:79
serial::StopBits
StopBits
Number of stop bits used for serial transmission.
Definition: uart_commons.h:51
serial::hard::UARX::UARX
UARX(char(&input)[SIZE_RX])
Construct a new hardware serial receiver and provide it with a buffer for interrupt-based reception.
Definition: uart.h:374
serial::BufferHandling
BufferHandling
How the TX/RX buffer should be handled when ending transmission (see end() methods) on UATX/UARX.
Definition: uart_commons.h:63
serial::hard::UART
Hardware serial receiver/transceiver API.
Definition: uart.h:425
errors
This namespace defines common errors that can be returned by some FastArduino API methods,...
Definition: errors.h:38
serial::hard::UART::begin
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the receiver/transceiver.
Definition: uart.h:466
serial::hard::UATX::end
void end(BufferHandling buffer_handling=BufferHandling::KEEP)
Stop all transmissions.
Definition: uart.h:288
serial::hard::UART::UART
UART(char(&input)[SIZE_RX], char(&output)[SIZE_TX])
Construct a new hardware serial receiver/transceiver and provide it with 2 buffers,...
Definition: uart.h:450
serial::hard::UARX::begin
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the receiver.
Definition: uart.h:389
streams::istream
Input stream wrapper to provide formatted input API, a la C++.
Definition: streams.h:357
streams::ostreambuf::queue
QUEUE & queue()
Return the underlying queue.
Definition: streambuf.h:154
serial::hard::UATX::begin
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the transmitter.
Definition: uart.h:275
serial::hard::UART::end
void end(BufferHandling buffer_handling=BufferHandling::KEEP)
Stop all transmissions and receptions.
Definition: uart.h:479
streams::ostreambuf::pubsync
void pubsync()
Wait until all buffer content has been pulled by a consumer.
Definition: streambuf.h:64
serial::BufferHandling::KEEP
@ KEEP
Stop transmission immediately, keep buffer as-is.
interrupts.h
General API for handling AVR interrupt vectors.
interrupt::register_handler
void register_handler(Handler &handler)
Register a class instance containing methods that shall be called back by an ISR.
Definition: interrupts.h:157
streams::istreambuf::queue
QUEUE & queue()
Return the underlying queue.
Definition: streambuf.h:258