libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
esp_uart.cpp
1#include "esp_uart.hpp"
2
3#include <algorithm>
4#include <cstring>
5#include <new>
6
7#include "esp_attr.h"
8#include "esp_clk_tree.h"
9#include "esp_err.h"
10#include "esp_heap_caps.h"
11#include "esp_private/periph_ctrl.h"
12#include "esp_rom_gpio.h"
13#include "hal/uart_ll.h"
14#include "soc/gpio_sig_map.h"
15#include "soc/uart_periph.h"
16
17namespace
18{
19constexpr uint32_t kUartRxIntrMask =
20 UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_OVF;
21constexpr uint32_t kUartTxIntrMask = UART_INTR_TXFIFO_EMPTY;
22
23constexpr uint8_t kRxToutThreshold = 2;
24constexpr uint16_t kTxEmptyThreshold = 24;
25
26bool IsConsoleUartInUse(uart_port_t uart_num)
27{
28#if defined(CONFIG_ESP_CONSOLE_UART) && CONFIG_ESP_CONSOLE_UART
29 return static_cast<int>(uart_num) == CONFIG_ESP_CONSOLE_UART_NUM;
30#else
31 (void)uart_num;
32 return false;
33#endif
34}
35} // namespace
36
37namespace LibXR
38{
39
40uint8_t* ESP32UART::AllocateTxStorage(size_t size)
41{
42 void* aligned = heap_caps_aligned_alloc(
43 4, size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
44 if (aligned != nullptr)
45 {
46 return static_cast<uint8_t*>(aligned);
47 }
48
49 return static_cast<uint8_t*>(
50 heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT));
51}
52
53ErrorCode ESP32UART::ResolveUartPeriph(uart_port_t uart_num, periph_module_t& out)
54{
55 switch (uart_num)
56 {
57 case UART_NUM_0:
58 out = PERIPH_UART0_MODULE;
59 return ErrorCode::OK;
60 case UART_NUM_1:
61 out = PERIPH_UART1_MODULE;
62 return ErrorCode::OK;
63#if SOC_UART_HP_NUM > 2
64 case UART_NUM_2:
65 out = PERIPH_UART2_MODULE;
66 return ErrorCode::OK;
67#endif
68 default:
70 }
71}
72
73ESP32UART::ESP32UART(uart_port_t uart_num, int tx_pin, int rx_pin, int rts_pin,
74 int cts_pin, size_t rx_buffer_size, size_t tx_buffer_size,
75 uint32_t tx_queue_size, UART::Configuration config, bool enable_dma)
76 : UART(&_read_port, &_write_port),
77 uart_num_(uart_num),
78 tx_pin_(tx_pin),
79 rx_pin_(rx_pin),
80 rts_pin_(rts_pin),
81 cts_pin_(cts_pin),
82 config_(config),
83 rx_isr_buffer_(new (std::nothrow) uint8_t[rx_buffer_size]),
84 rx_isr_buffer_size_(rx_buffer_size),
85 tx_storage_(AllocateTxStorage(tx_buffer_size * 2)),
86 tx_storage_size_(tx_buffer_size * 2),
87 tx_buffer_size_(tx_buffer_size),
88 dma_requested_(enable_dma),
89 _read_port(rx_buffer_size),
90 _write_port(tx_queue_size, tx_buffer_size)
91{
92 ASSERT(!IsConsoleUartInUse(uart_num_));
93 ASSERT(uart_num_ < UART_NUM_MAX);
94 ASSERT(uart_num_ < SOC_UART_HP_NUM);
95 ASSERT(rx_isr_buffer_size_ > 0);
96 ASSERT(tx_buffer_size_ > 0);
97 ASSERT(rx_isr_buffer_ != nullptr);
98 ASSERT(tx_storage_ != nullptr);
99 ASSERT(tx_storage_size_ >= (tx_buffer_size_ * 2U));
100
101 tx_active_buffer_ = tx_storage_;
102 tx_pending_buffer_ = tx_storage_ + tx_buffer_size_;
103 tx_active_length_ = 0;
104 tx_pending_length_ = 0;
105
106 _read_port = ReadFun;
107 _write_port = WriteFun;
108
109 if (InitUartHardware() != ErrorCode::OK)
110 {
111 ASSERT(false);
112 return;
113 }
114
115#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
116 if (dma_requested_)
117 {
118 if (InitDmaBackend() != ErrorCode::OK)
119 {
120 ASSERT(false);
121 return;
122 }
123 }
124
125 if (!dma_backend_enabled_)
126 {
127 if (InstallUartIsr() != ErrorCode::OK)
128 {
129 ASSERT(false);
130 return;
131 }
132 ConfigureRxInterruptPath();
133 }
134#else
135 if (InstallUartIsr() != ErrorCode::OK)
136 {
137 ASSERT(false);
138 return;
139 }
140 ConfigureRxInterruptPath();
141#endif
142}
143
144void ESP32UART::ConfigureRxInterruptPath()
145{
146 const size_t rx_full_floor = 16;
147 const size_t rx_full_ceil = std::max<size_t>(rx_full_floor, SOC_UART_FIFO_LEN / 4);
148 const uint16_t full_thr = static_cast<uint16_t>(std::min<size_t>(
149 rx_full_ceil, std::max<size_t>(rx_full_floor, rx_isr_buffer_size_ / 16)));
150
151 uart_hal_set_rxfifo_full_thr(&uart_hal_, full_thr);
152 uart_hal_set_rx_timeout(&uart_hal_, kRxToutThreshold);
153 uart_hal_clr_intsts_mask(&uart_hal_, kUartRxIntrMask);
154 uart_hal_ena_intr_mask(&uart_hal_, kUartRxIntrMask);
155}
156
157ErrorCode ESP32UART::SetConfig(UART::Configuration config)
158{
159 if (!uart_hw_enabled_)
160 {
161 return ErrorCode::STATE_ERR;
162 }
163
164 uart_word_length_t word_length = UART_DATA_8_BITS;
165 uart_stop_bits_t stop_bits = UART_STOP_BITS_1;
166
167 if (!ResolveWordLength(config.data_bits, word_length))
168 {
169 return ErrorCode::ARG_ERR;
170 }
171
172 if (!ResolveStopBits(config.stop_bits, stop_bits))
173 {
174 return ErrorCode::ARG_ERR;
175 }
176
177 const uart_sclk_t sclk = UART_SCLK_DEFAULT;
178 uart_hal_set_sclk(&uart_hal_, static_cast<soc_module_clk_t>(sclk));
179
180 uint32_t sclk_hz = 0;
181 if ((esp_clk_tree_src_get_freq_hz(static_cast<soc_module_clk_t>(sclk),
182 ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED,
183 &sclk_hz) != ESP_OK) ||
184 (sclk_hz == 0))
185 {
186 return ErrorCode::INIT_ERR;
187 }
188
189 if (!uart_hal_set_baudrate(&uart_hal_, config.baudrate, sclk_hz))
190 {
191 return ErrorCode::INIT_ERR;
192 }
193
194 uart_hal_set_data_bit_num(&uart_hal_, word_length);
195 uart_hal_set_stop_bits(&uart_hal_, stop_bits);
196 uart_hal_set_parity(&uart_hal_, ResolveParity(config.parity));
197 uart_hal_set_hw_flow_ctrl(&uart_hal_, UART_HW_FLOWCTRL_DISABLE, 0);
198 uart_hal_set_mode(&uart_hal_, UART_MODE_UART);
199 uart_hal_set_txfifo_empty_thr(&uart_hal_, kTxEmptyThreshold);
200 // Drop stale hardware RX FIFO bytes from the previous baud.
201 // Keep software read queue semantics aligned with ST/CH (no read_port reset).
202 uart_hal_rxfifo_rst(&uart_hal_);
203 uart_hal_clr_intsts_mask(&uart_hal_, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);
204
205#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
206 if (dma_backend_enabled_)
207 {
208 // Re-align the circular RX DMA window after reconfig to avoid
209 // carrying stale pre-switch bytes into the next frame.
210 HandleDmaRxError();
211 }
212#endif
213
214 // Align with ST/CH SetConfig semantics: if TX was in-flight during
215 // reconfiguration, keep transfer progression instead of surfacing BUSY.
216 if (tx_busy_.IsSet() && tx_active_valid_)
217 {
218#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
219 if (dma_backend_enabled_)
220 {
221 (void)StartDmaTx();
222 }
223 else
224#endif
225 {
226 uart_hal_clr_intsts_mask(&uart_hal_, kUartTxIntrMask);
227 uart_hal_ena_intr_mask(&uart_hal_, kUartTxIntrMask);
228 FillTxFifo(false);
229 }
230 }
231
232 config_ = config;
233 return ErrorCode::OK;
234}
235
236ErrorCode ESP32UART::SetLoopback(bool enable)
237{
238 if (!uart_hw_enabled_)
239 {
240 return ErrorCode::STATE_ERR;
241 }
242
243 uart_ll_set_loop_back(uart_hal_.dev, enable);
244 return ErrorCode::OK;
245}
246
247ErrorCode IRAM_ATTR ESP32UART::WriteFun(WritePort& port, bool in_isr)
248{
249 auto* uart = CONTAINER_OF(&port, ESP32UART, _write_port);
250 return uart->TryStartTx(in_isr);
251}
252
253ErrorCode ESP32UART::ReadFun(ReadPort&, bool) { return ErrorCode::PENDING; }
254
255bool ESP32UART::ResolveWordLength(uint8_t data_bits, uart_word_length_t& out)
256{
257 switch (data_bits)
258 {
259 case 5:
260 out = UART_DATA_5_BITS;
261 return true;
262 case 6:
263 out = UART_DATA_6_BITS;
264 return true;
265 case 7:
266 out = UART_DATA_7_BITS;
267 return true;
268 case 8:
269 out = UART_DATA_8_BITS;
270 return true;
271 default:
272 return false;
273 }
274}
275
276bool ESP32UART::ResolveStopBits(uint8_t stop_bits, uart_stop_bits_t& out)
277{
278 switch (stop_bits)
279 {
280 case 1:
281 out = UART_STOP_BITS_1;
282 return true;
283 case 2:
284 out = UART_STOP_BITS_2;
285 return true;
286 default:
287 return false;
288 }
289}
290
291uart_parity_t ESP32UART::ResolveParity(UART::Parity parity)
292{
293 switch (parity)
294 {
295 case UART::Parity::NO_PARITY:
296 return UART_PARITY_DISABLE;
297 case UART::Parity::EVEN:
298 return UART_PARITY_EVEN;
299 case UART::Parity::ODD:
300 return UART_PARITY_ODD;
301 default:
302 return UART_PARITY_DISABLE;
303 }
304}
305
306ErrorCode ESP32UART::InitUartHardware()
307{
308 if (uart_num_ >= UART_NUM_MAX)
309 {
310 return ErrorCode::NOT_SUPPORT;
311 }
312
313 periph_module_t uart_module = PERIPH_MODULE_MAX;
314 if (ResolveUartPeriph(uart_num_, uart_module) != ErrorCode::OK)
315 {
316 return ErrorCode::NOT_SUPPORT;
317 }
318
319 uart_hal_.dev = UART_LL_GET_HW(uart_num_);
320 if (uart_hal_.dev == nullptr)
321 {
322 return ErrorCode::NOT_SUPPORT;
323 }
324
325 periph_module_enable(uart_module);
326 periph_module_reset(uart_module);
327
328 uart_ll_sclk_enable(uart_hal_.dev);
329 uart_hal_init(&uart_hal_, uart_num_);
330
331 uart_hw_enabled_ = true;
332 if (SetConfig(config_) != ErrorCode::OK)
333 {
334 uart_hw_enabled_ = false;
335 return ErrorCode::INIT_ERR;
336 }
337
338 if (ConfigurePins() != ErrorCode::OK)
339 {
340 uart_hw_enabled_ = false;
341 return ErrorCode::INIT_ERR;
342 }
343
344 uart_hal_txfifo_rst(&uart_hal_);
345 uart_hal_rxfifo_rst(&uart_hal_);
346 uart_hal_clr_intsts_mask(&uart_hal_, UINT32_MAX);
347 uart_hal_disable_intr_mask(&uart_hal_, UINT32_MAX);
348
349 return ErrorCode::OK;
350}
351
352ErrorCode ESP32UART::ConfigurePins()
353{
354 if (tx_pin_ >= 0)
355 {
356 if (!GPIO_IS_VALID_OUTPUT_GPIO(tx_pin_))
357 {
358 return ErrorCode::ARG_ERR;
359 }
360 esp_rom_gpio_pad_select_gpio(static_cast<uint32_t>(tx_pin_));
361 esp_rom_gpio_connect_out_signal(
362 tx_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_TX_PIN_IDX), false, false);
363 }
364
365 if (rx_pin_ >= 0)
366 {
367 if (!GPIO_IS_VALID_GPIO(rx_pin_))
368 {
369 return ErrorCode::ARG_ERR;
370 }
371 gpio_input_enable(static_cast<gpio_num_t>(rx_pin_));
372 esp_rom_gpio_connect_in_signal(
373 rx_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_RX_PIN_IDX), false);
374 }
375
376 if (rts_pin_ >= 0)
377 {
378 if (!GPIO_IS_VALID_OUTPUT_GPIO(rts_pin_))
379 {
380 return ErrorCode::ARG_ERR;
381 }
382 esp_rom_gpio_pad_select_gpio(static_cast<uint32_t>(rts_pin_));
383 esp_rom_gpio_connect_out_signal(
384 rts_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_RTS_PIN_IDX), false, false);
385 }
386
387 if (cts_pin_ >= 0)
388 {
389 if (!GPIO_IS_VALID_GPIO(cts_pin_))
390 {
391 return ErrorCode::ARG_ERR;
392 }
393 gpio_pullup_en(static_cast<gpio_num_t>(cts_pin_));
394 gpio_input_enable(static_cast<gpio_num_t>(cts_pin_));
395 esp_rom_gpio_connect_in_signal(
396 cts_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_CTS_PIN_IDX), false);
397 }
398
399 return ErrorCode::OK;
400}
401
402void IRAM_ATTR ESP32UART::ClearActiveTx()
403{
404 tx_active_length_ = 0;
405 tx_active_offset_ = 0;
406 tx_active_info_ = {};
407 tx_active_valid_ = false;
408}
409
410void IRAM_ATTR ESP32UART::ClearPendingTx()
411{
412 tx_pending_length_ = 0;
413 tx_pending_info_ = {};
414 tx_pending_valid_ = false;
415}
416
417bool IRAM_ATTR ESP32UART::StartAndReportActive(bool in_isr)
418{
419 if (!StartActiveTransfer(in_isr))
420 {
421 write_port_->Finish(in_isr, ErrorCode::FAILED, tx_active_info_);
422 ClearActiveTx();
423 return false;
424 }
425
426 // Align with STM/CH semantics: once the active write is kicked to HW,
427 // WritePort owns the completion notification and the ISR only advances queues.
428 write_port_->Finish(in_isr, ErrorCode::OK, tx_active_info_);
429 return true;
430}
431
432ErrorCode IRAM_ATTR ESP32UART::TryStartTx(bool in_isr)
433{
434 if (in_tx_isr_.IsSet())
435 {
436 return ErrorCode::PENDING;
437 }
438 if (!tx_active_valid_)
439 {
440 (void)LoadActiveTxFromQueue(in_isr);
441 }
442
443 if (!tx_busy_.IsSet() && tx_active_valid_)
444 {
445 if (!StartActiveTransfer(in_isr))
446 {
447 ClearActiveTx();
448 return ErrorCode::FAILED;
449 }
450
451 // Current op completion is reported by WritePort when TryStartTx returns OK.
452 if (!tx_pending_valid_)
453 {
454 (void)LoadPendingTxFromQueue(in_isr);
455 }
456 return ErrorCode::OK;
457 }
458
459 if (!tx_pending_valid_)
460 {
461 (void)LoadPendingTxFromQueue(in_isr);
462 }
463
464 return ErrorCode::PENDING;
465}
466
467bool IRAM_ATTR ESP32UART::LoadActiveTxFromQueue(bool in_isr)
468{
469 (void)in_isr;
470
471 size_t active_length = 0U;
472 if (!DequeueTxToBuffer(tx_active_buffer_, active_length, tx_active_info_, in_isr))
473 {
474 return false;
475 }
476
477 tx_active_length_ = active_length;
478 tx_active_valid_ = true;
479 return true;
480}
481
482bool IRAM_ATTR ESP32UART::LoadPendingTxFromQueue(bool in_isr)
483{
484 (void)in_isr;
485
486 if (tx_pending_valid_)
487 {
488 return false;
489 }
490
491 size_t pending_length = 0U;
492 if (!DequeueTxToBuffer(tx_pending_buffer_, pending_length, tx_pending_info_, in_isr))
493 {
494 return false;
495 }
496
497 tx_pending_length_ = pending_length;
498 tx_pending_valid_ = true;
499 return true;
500}
501
502bool IRAM_ATTR ESP32UART::DequeueTxToBuffer(uint8_t* buffer, size_t& size,
503 WriteInfoBlock& info, bool in_isr)
504{
505 (void)in_isr;
506 (void)buffer;
507
508 WriteInfoBlock peek_info = {};
509 if (write_port_->queue_info_->Peek(peek_info) != ErrorCode::OK)
510 {
511 return false;
512 }
513
514 if (peek_info.data.size_ > tx_buffer_size_)
515 {
516 ASSERT(false);
517 return false;
518 }
519
520#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
521 if (dma_backend_enabled_)
522 {
523 if (write_port_->queue_data_->PopBatch(buffer, peek_info.data.size_) != ErrorCode::OK)
524 {
525 return false;
526 }
527 }
528#endif
529
530 if (write_port_->queue_info_->Pop(info) != ErrorCode::OK)
531 {
532 return false;
533 }
534
535 size = peek_info.data.size_;
536 return true;
537}
538
539bool IRAM_ATTR ESP32UART::StartActiveTransfer(bool)
540{
541 if (!tx_active_valid_)
542 {
543 return false;
544 }
545
546 if (tx_busy_.TestAndSet())
547 {
548 return true;
549 }
550
551 tx_active_offset_ = 0;
552
553#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
554 if (dma_backend_enabled_)
555 {
556 if (StartDmaTx())
557 {
558 return true;
559 }
560
561 tx_busy_.Clear();
562 return false;
563 }
564#endif
565
566 uart_hal_clr_intsts_mask(&uart_hal_, kUartTxIntrMask);
567 uart_hal_ena_intr_mask(&uart_hal_, kUartTxIntrMask);
568 FillTxFifo(false);
569
570 return true;
571}
572
573void IRAM_ATTR ESP32UART::PushRxBytes(const uint8_t* data, size_t size, bool in_isr)
574{
575 size_t offset = 0;
576 bool pushed_any = false;
577 while (offset < size)
578 {
579 const size_t free_space = read_port_->queue_data_->EmptySize();
580 if (free_space == 0)
581 {
582 break;
583 }
584
585 const size_t chunk = std::min(free_space, size - offset);
586 if (read_port_->queue_data_->PushBatch(data + offset, chunk) != ErrorCode::OK)
587 {
588 break;
589 }
590
591 offset += chunk;
592 pushed_any = true;
593 }
594
595 if (pushed_any)
596 {
597 read_port_->ProcessPendingReads(in_isr);
598 }
599}
600void IRAM_ATTR ESP32UART::OnTxTransferDone(bool in_isr, ErrorCode result)
601{
602 Flag::ScopedRestore tx_flag(in_tx_isr_);
603 tx_busy_.Clear();
604
605 ClearActiveTx();
606
607 if ((result != ErrorCode::OK) && tx_pending_valid_)
608 {
609 write_port_->Finish(in_isr, ErrorCode::FAILED, tx_pending_info_);
610 ClearPendingTx();
611 }
612
613 if (result != ErrorCode::OK)
614 {
615 ClearPendingTx();
616 return;
617 }
618
619 if (tx_pending_valid_)
620 {
621 std::swap(tx_active_buffer_, tx_pending_buffer_);
622 tx_active_length_ = tx_pending_length_;
623 tx_pending_length_ = 0;
624 tx_active_info_ = tx_pending_info_;
625 tx_active_valid_ = true;
626 ClearPendingTx();
627 (void)StartAndReportActive(in_isr);
628 }
629 else
630 {
631 if (LoadActiveTxFromQueue(in_isr))
632 {
633 (void)StartAndReportActive(in_isr);
634 }
635 }
636
637 if (!tx_pending_valid_)
638 {
639 (void)LoadPendingTxFromQueue(in_isr);
640 }
641}
642
643} // namespace LibXR
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举
Definition libxr_def.hpp:64
@ NOT_SUPPORT
不支持 | Not supported
@ OK
操作成功 | Operation successful
ErrorCode(* ReadFun)(ReadPort &port, bool in_isr)
Function pointer type for read operations.
Definition libxr_rw.hpp:356
ErrorCode(* WriteFun)(WritePort &port, bool in_isr)
Function pointer type for write operations.
Definition libxr_rw.hpp:352
UART 配置结构体 / UART configuration structure.
Definition uart.hpp:44
uint8_t stop_bits
停止位长度 / Number of stop bits
Definition uart.hpp:50
Parity parity
校验模式 / Parity mode
Definition uart.hpp:47
uint8_t data_bits
数据位长度 / Number of data bits
Definition uart.hpp:48
uint32_t baudrate
波特率 / Baud rate
Definition uart.hpp:45