FastArduino v1.10
C++ library to build fast but small Arduino/AVR projects
Loading...
Searching...
No Matches
uart.h
Go to the documentation of this file.
1// Copyright 2016-2023 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
70namespace serial
71{
78 namespace hard
79 {
80 }
81}
82
83namespace 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 template<uint8_t SIZE_TX>
177 explicit AbstractUATX(char (&output)[SIZE_TX]) : obuf_{output} {}
178
179 streams::ostreambuf& out_()
180 {
181 return obuf_;
182 }
183
184 template<board::USART USART>
185 void data_register_empty(Errors& errors)
186 {
187 using TRAIT = board_traits::USART_trait<USART>;
188 errors.has_errors = 0;
189 char value;
190 if (obuf_.queue().pull_(value))
191 TRAIT::UDR = value;
192 else
193 {
194 transmitting_ = false;
195 // Clear UDRIE to prevent UDR interrupt to go on forever
196 TRAIT::UCSRB &= bits::COMPL(TRAIT::UDRIE_MASK);
197 }
198 }
199
200 template<board::USART USART>
201 void on_put(Errors& errors)
202 {
203 using TRAIT = board_traits::USART_trait<USART>;
204 errors.queue_overflow = obuf_.overflow();
205 synchronized
206 {
207 // Check if TX is not currently active, if so, activate it
208 if (!transmitting_)
209 {
210 // Yes, trigger TX
211 char value;
212 if (obuf_.queue().pull_(value))
213 {
214 // Set UDR interrupt to be notified when we can send the next character
215 TRAIT::UCSRB |= TRAIT::UDRIE_MASK;
216 TRAIT::UDR = value;
217 transmitting_ = true;
218 }
219 }
220 }
221 }
222
223 private:
225 bool transmitting_ = false;
226 };
228
240 template<board::USART USART_> class UATX : public AbstractUATX, public UARTErrors
241 {
242 public:
244 static constexpr const board::USART USART = USART_;
245
254 template<uint8_t SIZE_TX> explicit UATX(char (&output)[SIZE_TX]) : AbstractUATX{output}
255 {
257 }
258
269 void begin(uint32_t rate, Parity parity = Parity::NONE, StopBits stop_bits = StopBits::ONE)
270 {
271 AbstractUART::begin_<USART>(rate, parity, stop_bits, nullptr, &out_());
272 }
273
283 {
284 AbstractUART::end_<USART>(buffer_handling, nullptr, &out_());
285 }
286
287 private:
288 // Listeners of events on the buffer
289 bool on_put(streams::ostreambuf& obuf)
290 {
291 if (&obuf != &out_()) return false;
292 AbstractUATX::on_put<USART>(errors());
293 return true;
294 }
295
296 void data_register_empty()
297 {
298 AbstractUATX::data_register_empty<USART>(errors());
299 }
300
301 friend struct isr_handler;
303 };
304
306 class AbstractUARX : public AbstractUART
307 {
308 public:
314 {
315 return streams::istream(ibuf_);
316 }
317
318 protected:
319 template<uint8_t SIZE_RX> explicit AbstractUARX(char (&input)[SIZE_RX]) : ibuf_{input} {}
320
322 {
323 return ibuf_;
324 }
325
326 template<board::USART USART>
327 void data_receive_complete(Errors& errors)
328 {
329 using TRAIT = board_traits::USART_trait<USART>;
330 uint8_t status = TRAIT::UCSRA;
331 errors.data_overrun = status & TRAIT::DOR_MASK;
332 errors.frame_error = status & TRAIT::FE_MASK;
333 errors.parity_error = status & TRAIT::UPE_MASK;
334 uint8_t value = TRAIT::UDR;
335 errors.queue_overflow = !ibuf_.queue().push_(value);
336 }
337
338 private:
340 };
342
351 template<board::USART USART_> class UARX : public AbstractUARX, public UARTErrors
352 {
353 public:
355 static constexpr const board::USART USART = USART_;
356
366 template<uint8_t SIZE_RX> explicit UARX(char (&input)[SIZE_RX]) : AbstractUARX{input}
367 {
369 }
370
381 void begin(uint32_t rate, Parity parity = Parity::NONE, StopBits stop_bits = StopBits::ONE)
382 {
383 AbstractUART::begin_<USART>(rate, parity, stop_bits, &in_(), nullptr);
384 }
385
395 {
396 AbstractUART::end_<USART>(buffer_handling, &in_(), nullptr);
397 }
398
399 private:
400 void data_receive_complete()
401 {
402 AbstractUARX::data_receive_complete<USART>(errors());
403 }
404
405 friend struct isr_handler;
406 };
407
419 template<board::USART USART_> class UART : public AbstractUARX, public AbstractUATX, public UARTErrors
420 {
421 public:
423 static constexpr const board::USART USART = USART_;
424
439 template<uint8_t SIZE_RX, uint8_t SIZE_TX>
440 UART(char (&input)[SIZE_RX], char (&output)[SIZE_TX])
441 : AbstractUARX{input}, AbstractUATX{output}
442 {
444 }
445
456 void begin(uint32_t rate, Parity parity = Parity::NONE, StopBits stop_bits = StopBits::ONE)
457 {
458 AbstractUART::begin_<USART>(rate, parity, stop_bits, &in_(), &out_());
459 }
460
470 {
471 AbstractUART::end_<USART>(buffer_handling, &in_(), &out_());
472 }
473
474 private:
475 // Listeners of events on the buffer
476 bool on_put(streams::ostreambuf& obuf)
477 {
478 if (&obuf != &out_()) return false;
479 AbstractUATX::on_put<USART>(errors());
480 return true;
481 }
482
483 void data_register_empty()
484 {
485 AbstractUATX::data_register_empty<USART>(errors());
486 }
487
488 void data_receive_complete()
489 {
490 AbstractUARX::data_receive_complete<USART>(errors());
491 }
492
493 friend struct isr_handler;
495 };
496
497 // All UART-related methods called by pre-defined ISR are defined here
498 //=====================================================================
500 struct isr_handler
501 {
502 template<uint8_t UART_NUM_> static constexpr board::USART check_uart()
503 {
504 constexpr board::USART USART = (board::USART) UART_NUM_;
505 static_assert(board_traits::USART_trait<USART>::U2X_MASK != 0,
506 "UART_NUM must be an actual USART in target MCU");
507 return USART;
508 }
509
510 template<uint8_t UART_NUM_> static void uatx()
511 {
512 static constexpr board::USART USART = check_uart<UART_NUM_>();
513 interrupt::HandlerHolder<UATX<USART>>::handler()->data_register_empty();
514 }
515
516 template<uint8_t UART_NUM_> static void uarx()
517 {
518 static constexpr board::USART USART = check_uart<UART_NUM_>();
519 interrupt::HandlerHolder<UARX<USART>>::handler()->data_receive_complete();
520 }
521
522 template<uint8_t UART_NUM_> static void uart_tx()
523 {
524 static constexpr board::USART USART = check_uart<UART_NUM_>();
525 interrupt::HandlerHolder<UART<USART>>::handler()->data_register_empty();
526 }
527
528 template<uint8_t UART_NUM_> static void uart_rx()
529 {
530 static constexpr board::USART USART = check_uart<UART_NUM_>();
531 interrupt::HandlerHolder<UART<USART>>::handler()->data_receive_complete();
532 }
533 };
535}
536
537namespace serial
538{
540 // Specific traits of HW UART classes
541 template<board::USART USART> struct UART_trait<hard::UATX<USART>>
542 {
543 static constexpr bool IS_UART = true;
544 static constexpr bool IS_HW_UART = true;
545 static constexpr bool IS_SW_UART = false;
546 static constexpr bool HAS_TX = true;
547 static constexpr bool HAS_RX = false;
548 };
549 template<board::USART USART> struct UART_trait<hard::UARX<USART>>
550 {
551 static constexpr bool IS_UART = true;
552 static constexpr bool IS_HW_UART = true;
553 static constexpr bool IS_SW_UART = false;
554 static constexpr bool HAS_TX = false;
555 static constexpr bool HAS_RX = true;
556 };
557 template<board::USART USART> struct UART_trait<hard::UART<USART>>
558 {
559 static constexpr bool IS_UART = true;
560 static constexpr bool IS_HW_UART = true;
561 static constexpr bool IS_SW_UART = false;
562 static constexpr bool HAS_TX = true;
563 static constexpr bool HAS_RX = true;
564 };
566}
567
568#endif /* UCSR0A */
569#endif /* UART_HH */
Holder of latest UART errors.
Definition: uart_commons.h:96
Hardware serial receiver/transceiver API.
Definition: uart.h:420
void end(BufferHandling buffer_handling=BufferHandling::KEEP)
Stop all transmissions and receptions.
Definition: uart.h:469
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:440
static constexpr const board::USART USART
The hardware board::USART used by this UART.
Definition: uart.h:423
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the receiver/transceiver.
Definition: uart.h:456
Hardware serial receiver API.
Definition: uart.h:352
static constexpr const board::USART USART
The hardware board::USART used by this UARX.
Definition: uart.h:355
UARX(char(&input)[SIZE_RX])
Construct a new hardware serial receiver and provide it with a buffer for interrupt-based reception.
Definition: uart.h:366
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the receiver.
Definition: uart.h:381
void end(BufferHandling buffer_handling=BufferHandling::KEEP)
Stop reception.
Definition: uart.h:394
Hardware serial transmitter API.
Definition: uart.h:241
void end(BufferHandling buffer_handling=BufferHandling::KEEP)
Stop all transmissions.
Definition: uart.h:282
void begin(uint32_t rate, Parity parity=Parity::NONE, StopBits stop_bits=StopBits::ONE)
Enable the transmitter.
Definition: uart.h:269
UATX(char(&output)[SIZE_TX])
Construct a new hardware serial transmitter and provide it with a buffer for interrupt-based transmis...
Definition: uart.h:254
static constexpr const board::USART USART
The hardware board::USART used by this UATX.
Definition: uart.h:244
Input stream wrapper to provide formatted input API, a la C++.
Definition: streams.h:360
Input API based on a ring buffer.
Definition: streambuf.h:257
QUEUE & queue()
Return the underlying queue.
Definition: streambuf.h:308
Output stream wrapper to provide formatted output API, a la C++.
Definition: streams.h:61
Output API based on a ring buffer.
Definition: streambuf.h:96
void pubsync()
Wait until all buffer content has been pulled by a consumer.
Definition: streambuf.h:113
QUEUE & queue()
Return the underlying queue.
Definition: streambuf.h:203
General API for handling AVR interrupt vectors.
static constexpr uint8_t COMPL(uint8_t value)
Return the uint8_t 2-complement of a byte.
Definition: bits.h:253
USART
Defines all USART modules of target MCU.
Definition: empty.h:105
This namespace defines common errors that can be returned by some FastArduino API methods,...
Definition: errors.h:38
void register_handler(Handler &handler)
Register a class instance containing methods that shall be called back by an ISR.
Definition: interrupts.h:185
Defines API types used by hardware UART features.
Definition: uart.h:79
Defines all API for UART features.
Definition: soft_uart.h:84
BufferHandling
How the TX/RX buffer should be handled when ending transmission (see end() methods) on UATX/UARX.
Definition: uart_commons.h:63
@ KEEP
Stop transmission immediately, keep buffer as-is.
StopBits
Number of stop bits used for serial transmission.
Definition: uart_commons.h:51
@ ONE
One stop bit.
Parity
Parity used for serial transmission.
Definition: uart_commons.h:38
@ NONE
No parity bit.
#define DECL_OSTREAMBUF_LISTENERS_FRIEND
This macro shall be used in a class containing a private callback method bool on_put(streams::ostream...
Definition: streambuf.h:73
C++-like std::iostream facilities.
Common definitions for serial API.