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
6#include "esp_attr.h"
7#include "esp_clk_tree.h"
8#include "esp_err.h"
9#include "esp_heap_caps.h"
10#include "esp_private/periph_ctrl.h"
11#include "esp_rom_gpio.h"
12#include "hal/uart_ll.h"
13#include "soc/gpio_sig_map.h"
14#include "soc/uart_periph.h"
15
16namespace
17{
18// RX interrupt reasons handled by the FIFO receive path.
19// FIFO 接收路径处理的 RX 中断原因。
20constexpr uint32_t UART_RX_INTR_MASK =
21 UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_OVF;
22
23// TX interrupt reason used by the FIFO transmit path.
24// FIFO 发送路径使用的 TX 中断原因。
25constexpr uint32_t UART_TX_INTR_MASK = UART_INTR_TXFIFO_EMPTY;
26
27// Use the minimum non-zero timeout so short RX tails are flushed as close as
28// possible to an idle-style boundary.
29// 使用最小非零 timeout,让短 RX 尾包尽量贴近 idle 风格边界被冲刷出来。
30constexpr uint8_t RX_TOUT_THRESHOLD = 1;
31
32// Ask for more TX bytes once the hardware FIFO drops to roughly half depth.
33// 当硬件 FIFO 下降到约一半深度时,请求补充更多 TX 字节。
34constexpr uint16_t TX_EMPTY_THRESHOLD = SOC_UART_FIFO_LEN / 2U;
35
36// This backend cannot coexist with the ESP-IDF console UART reservation.
37// 该后端不能与 ESP-IDF 的控制台 UART 占用同时存在。
38bool IsConsoleUartInUse(uart_port_t uart_num)
39{
40#if defined(CONFIG_ESP_CONSOLE_UART) && CONFIG_ESP_CONSOLE_UART
41 return static_cast<int>(uart_num) == CONFIG_ESP_CONSOLE_UART_NUM;
42#else
43 (void)uart_num;
44 return false;
45#endif
46}
47} // namespace
48
49namespace LibXR
50{
51
53{
54#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
55 if (owner_.dma_backend_enabled_)
56 {
57 return;
58 }
59#endif
60 owner_.DrainRxFifo(in_isr);
61}
62
63// Prefer aligned DMA-capable allocation first, then fall back to the broader
64// DMA-capable heap if the strict aligned allocation API is unavailable.
65// 优先使用对齐的 DMA 可访问分配;若失败,再退回到更宽松的 DMA heap。
66uint8_t* ESP32UART::AllocateTxStorage(size_t size)
67{
68 void* aligned = heap_caps_aligned_alloc(
69 4, size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
70 if (aligned != nullptr)
71 {
72 return static_cast<uint8_t*>(aligned);
73 }
74
75 return static_cast<uint8_t*>(
76 heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT));
77}
78
79// Translate the public UART index into the ESP peripheral module gate.
80// 将公开 UART 序号转换为 ESP 外设模块门控对象。
81ErrorCode ESP32UART::ResolveUartPeriph(uart_port_t uart_num, periph_module_t& out)
82{
83 switch (uart_num)
84 {
85 case UART_NUM_0:
86 out = PERIPH_UART0_MODULE;
87 return ErrorCode::OK;
88 case UART_NUM_1:
89 out = PERIPH_UART1_MODULE;
90 return ErrorCode::OK;
91#if SOC_UART_HP_NUM > 2
92 case UART_NUM_2:
93 out = PERIPH_UART2_MODULE;
94 return ErrorCode::OK;
95#endif
96 default:
98 }
99}
100
101// Construct queue plumbing first, then bind the storage into the TX
102// double-buffer view before touching hardware.
103// 先构造队列连接,再把 storage 绑定到 TX 双缓冲视图,最后再触碰硬件。
104ESP32UART::ESP32UART(uart_port_t uart_num, int tx_pin, int rx_pin, int rts_pin,
105 int cts_pin, size_t rx_buffer_size, size_t tx_buffer_size,
106 uint32_t tx_queue_size, UART::Configuration config, bool enable_dma)
107 : UART(&_read_port, &_write_port),
108 uart_num_(uart_num),
109 tx_pin_(tx_pin),
110 rx_pin_(rx_pin),
111 rts_pin_(rts_pin),
112 cts_pin_(cts_pin),
113 config_(config),
114 rx_isr_buffer_(new uint8_t[rx_buffer_size]),
115 rx_isr_buffer_size_(rx_buffer_size),
116 tx_storage_(AllocateTxStorage(tx_buffer_size * 2)),
117 dma_requested_(enable_dma),
118 _read_port(rx_buffer_size, *this),
119 _write_port(tx_queue_size, tx_buffer_size)
120{
121 ASSERT(!IsConsoleUartInUse(uart_num_));
122 ASSERT(uart_num_ < UART_NUM_MAX);
123 ASSERT(uart_num_ < SOC_UART_HP_NUM);
124 ASSERT(rx_isr_buffer_size_ > 0);
125 ASSERT(tx_buffer_size > 0);
126
127 tx_dma_buffer_.Init({tx_storage_, tx_buffer_size * 2U});
128
131
133 {
134 ASSERT(false);
135 return;
136 }
137
138#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
139 if (dma_requested_)
140 {
141 if (InitDmaBackend() != ErrorCode::OK)
142 {
143 ASSERT(false);
144 return;
145 }
146 }
147
148 if (!dma_backend_enabled_)
149 {
151 {
152 ASSERT(false);
153 return;
154 }
156 }
157#else
159 {
160 ASSERT(false);
161 return;
162 }
164#endif
165}
166
167// Use a half-FIFO RX threshold in FIFO mode. Short residual tails are handled
168// by the minimum non-zero RX timeout above.
169// FIFO 模式下使用半 FIFO 的 RX 阈值,剩余短尾包交给上面的最小非零 RX timeout。
171{
172 const uint16_t full_thr = static_cast<uint16_t>(SOC_UART_FIFO_LEN / 2U);
173
174 uart_hal_set_rxfifo_full_thr(&uart_hal_, full_thr);
175 uart_hal_set_rx_timeout(&uart_hal_, RX_TOUT_THRESHOLD);
176 uart_hal_clr_intsts_mask(&uart_hal_, UART_RX_INTR_MASK);
177 uart_hal_ena_intr_mask(&uart_hal_, UART_RX_INTR_MASK);
178}
179
180// Reconfigure framing in place while preserving the current software queue
181// model. If TX is already in progress, resume the backend instead of surfacing
182// a synthetic BUSY state.
183// 原地重配帧格式,同时保持软件队列模型不变。若 TX 已在进行,则恢复后端,
184// 而不是人为抛出 BUSY 状态。
186{
187 if (!uart_hw_enabled_)
188 {
190 }
191
192 uart_word_length_t word_length = UART_DATA_8_BITS;
193 uart_stop_bits_t stop_bits = UART_STOP_BITS_1;
194
195 if (!ResolveWordLength(config.data_bits, word_length))
196 {
197 return ErrorCode::ARG_ERR;
198 }
199
200 if (!ResolveStopBits(config.stop_bits, stop_bits))
201 {
202 return ErrorCode::ARG_ERR;
203 }
204
205 const uart_sclk_t sclk = UART_SCLK_DEFAULT;
206 uart_hal_set_sclk(&uart_hal_, static_cast<soc_module_clk_t>(sclk));
207
208 uint32_t sclk_hz = 0;
209 if ((esp_clk_tree_src_get_freq_hz(static_cast<soc_module_clk_t>(sclk),
210 ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED,
211 &sclk_hz) != ESP_OK) ||
212 (sclk_hz == 0))
213 {
214 return ErrorCode::INIT_ERR;
215 }
216
217 if (!uart_hal_set_baudrate(&uart_hal_, config.baudrate, sclk_hz))
218 {
219 return ErrorCode::INIT_ERR;
220 }
221
222 uart_hal_set_data_bit_num(&uart_hal_, word_length);
223 uart_hal_set_stop_bits(&uart_hal_, stop_bits);
224 uart_hal_set_parity(&uart_hal_, ResolveParity(config.parity));
225 uart_hal_set_hw_flow_ctrl(&uart_hal_, UART_HW_FLOWCTRL_DISABLE, 0);
226 uart_hal_set_mode(&uart_hal_, UART_MODE_UART);
227 uart_hal_set_txfifo_empty_thr(&uart_hal_, TX_EMPTY_THRESHOLD);
228 // Drop stale hardware RX FIFO bytes from the previous baud.
229 // Keep software read queue semantics aligned with ST/CH (no read_port reset).
230 uart_hal_rxfifo_rst(&uart_hal_);
231 uart_hal_clr_intsts_mask(&uart_hal_, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);
232
233#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
234 if (dma_backend_enabled_)
235 {
236 // Re-align the circular RX DMA window after reconfig to avoid
237 // carrying stale pre-switch bytes into the next frame.
238 HandleDmaRxError();
239 }
240#endif
241
242 // Align with ST/CH SetConfig semantics: if TX was in-flight during
243 // reconfiguration, keep transfer progression instead of surfacing BUSY.
245 {
246#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
247 if (dma_backend_enabled_)
248 {
249 (void)StartDmaTx();
250 }
251 else
252#endif
253 {
254 uart_hal_clr_intsts_mask(&uart_hal_, UART_TX_INTR_MASK);
255 uart_hal_ena_intr_mask(&uart_hal_, UART_TX_INTR_MASK);
256 FillTxFifo(false);
257 }
258 }
259
260 config_ = config;
261 return ErrorCode::OK;
262}
263
264// Internal UART loopback is exposed as a direct peripheral toggle for backend
265// self-test and board-free link checks.
266// UART 内部环回直接作为外设开关暴露,用于后端自测和无需外部短接的链路检查。
268{
269 if (!uart_hw_enabled_)
270 {
272 }
273
274 uart_ll_set_loop_back(uart_hal_.dev, enable);
275 return ErrorCode::OK;
276}
277
278// `WritePort` only needs a trampoline back into the owning UART instance.
279// `WritePort` 只需要一个回跳到所属 UART 实例的跳板。
280ErrorCode IRAM_ATTR ESP32UART::WriteFun(WritePort& port, bool in_isr)
281{
282 auto* uart = LibXR::ContainerOf(&port, &ESP32UART::_write_port);
283 return uart->TryStartTx(in_isr);
284}
285
286// RX is interrupt-driven. Publishing a read waiter does not need any extra
287// backend kick here.
288// RX 由中断驱动,因此这里只需要表明:发布读 waiter 时不需要额外 kick 后端。
290
291// Convert libxr data-bit semantics into the ESP HAL value set.
292// 将 libxr 数据位语义转换为 ESP HAL 取值。
293bool ESP32UART::ResolveWordLength(uint8_t data_bits, uart_word_length_t& out)
294{
295 switch (data_bits)
296 {
297 case 5:
298 out = UART_DATA_5_BITS;
299 return true;
300 case 6:
301 out = UART_DATA_6_BITS;
302 return true;
303 case 7:
304 out = UART_DATA_7_BITS;
305 return true;
306 case 8:
307 out = UART_DATA_8_BITS;
308 return true;
309 default:
310 return false;
311 }
312}
313
314// Convert libxr stop-bit semantics into the ESP HAL value set.
315// 将 libxr 停止位语义转换为 ESP HAL 取值。
316bool ESP32UART::ResolveStopBits(uint8_t stop_bits, uart_stop_bits_t& out)
317{
318 switch (stop_bits)
319 {
320 case 1:
321 out = UART_STOP_BITS_1;
322 return true;
323 case 2:
324 out = UART_STOP_BITS_2;
325 return true;
326 default:
327 return false;
328 }
329}
330
331// Convert libxr parity semantics into the ESP HAL value set.
332// 将 libxr 校验位语义转换为 ESP HAL 取值。
334{
335 switch (parity)
336 {
338 return UART_PARITY_DISABLE;
340 return UART_PARITY_EVEN;
342 return UART_PARITY_ODD;
343 default:
344 return UART_PARITY_DISABLE;
345 }
346}
347
348// Bring the UART block into a known idle state before higher-level ISR or DMA
349// plumbing is attached.
350// 在挂接更高层 ISR 或 DMA 连接前,先把 UART 模块拉到已知空闲状态。
352{
353 if (uart_num_ >= UART_NUM_MAX)
354 {
356 }
357
358 periph_module_t uart_module = PERIPH_MODULE_MAX;
359 if (ResolveUartPeriph(uart_num_, uart_module) != ErrorCode::OK)
360 {
362 }
363
364 uart_hal_.dev = UART_LL_GET_HW(uart_num_);
365 if (uart_hal_.dev == nullptr)
366 {
368 }
369
370 periph_module_enable(uart_module);
371 periph_module_reset(uart_module);
372
373 uart_ll_sclk_enable(uart_hal_.dev);
374 uart_hal_init(&uart_hal_, uart_num_);
375
376 uart_hw_enabled_ = true;
378 {
379 uart_hw_enabled_ = false;
380 return ErrorCode::INIT_ERR;
381 }
382
384 {
385 uart_hw_enabled_ = false;
386 return ErrorCode::INIT_ERR;
387 }
388
389 uart_hal_txfifo_rst(&uart_hal_);
390 uart_hal_rxfifo_rst(&uart_hal_);
391 uart_hal_clr_intsts_mask(&uart_hal_, UINT32_MAX);
392 uart_hal_disable_intr_mask(&uart_hal_, UINT32_MAX);
393
394 return ErrorCode::OK;
395}
396
397// GPIO mapping stays explicit because ESP UART routing is per-pin configurable.
398// GPIO 映射保持显式写法,因为 ESP UART 路由是逐引脚可配置的。
400{
401 if (tx_pin_ >= 0)
402 {
403 if (!GPIO_IS_VALID_OUTPUT_GPIO(tx_pin_))
404 {
405 return ErrorCode::ARG_ERR;
406 }
407 esp_rom_gpio_pad_select_gpio(static_cast<uint32_t>(tx_pin_));
408 esp_rom_gpio_connect_out_signal(
409 tx_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_TX_PIN_IDX), false, false);
410 }
411
412 if (rx_pin_ >= 0)
413 {
414 if (!GPIO_IS_VALID_GPIO(rx_pin_))
415 {
416 return ErrorCode::ARG_ERR;
417 }
418 gpio_input_enable(static_cast<gpio_num_t>(rx_pin_));
419 esp_rom_gpio_connect_in_signal(
420 rx_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_RX_PIN_IDX), false);
421 }
422
423 if (rts_pin_ >= 0)
424 {
425 if (!GPIO_IS_VALID_OUTPUT_GPIO(rts_pin_))
426 {
427 return ErrorCode::ARG_ERR;
428 }
429 esp_rom_gpio_pad_select_gpio(static_cast<uint32_t>(rts_pin_));
430 esp_rom_gpio_connect_out_signal(
431 rts_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_RTS_PIN_IDX), false, false);
432 }
433
434 if (cts_pin_ >= 0)
435 {
436 if (!GPIO_IS_VALID_GPIO(cts_pin_))
437 {
438 return ErrorCode::ARG_ERR;
439 }
440 gpio_pullup_en(static_cast<gpio_num_t>(cts_pin_));
441 gpio_input_enable(static_cast<gpio_num_t>(cts_pin_));
442 esp_rom_gpio_connect_in_signal(
443 cts_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_CTS_PIN_IDX), false);
444 }
445
446 return ErrorCode::OK;
447}
448
449// Active state is shared by FIFO and DMA TX backends.
450// Active 状态由 FIFO 和 DMA 两条 TX 后端共用。
452{
455 tx_active_info_ = {};
456 tx_active_valid_ = false;
457}
458
459// Only the DMA backend carries a distinct pending TX preload state.
460// 只有 DMA 后端维护独立的 pending TX 预装状态。
462{
463#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
464 if (dma_backend_enabled_)
465 {
467 }
468#endif
469}
470
471// Once hardware accepts the active request, completion ownership moves to the
472// backend ISR path and the queued write can be reported as accepted.
473// 一旦硬件接管 active 请求,完成所有权就转移到后端 ISR 路径,队列侧即可
474// 报告该写请求已经被接受。
475bool IRAM_ATTR ESP32UART::StartAndReportActive(bool in_isr)
476{
477 if (!StartActiveTransfer(in_isr))
478 {
481 return false;
482 }
483
484 // Align with STM/CH semantics: once the active write is kicked to HW,
485 // WritePort owns the completion notification and the ISR only advances queues.
487 return true;
488}
489
490// TX start only does two things:
491// 1. when TX is fully idle, start one active request;
492// 2. when DMA TX is busy and no pending request is preloaded yet, preload one.
493// TX 发起路径只做两件事:
494// 1. TX 完全空闲时,启动一个 active 请求;
495// 2. DMA TX 已忙且还没有预装 pending 时,补装一个。
496ErrorCode IRAM_ATTR ESP32UART::TryStartTx(bool in_isr)
497{
498 if (in_tx_isr_.IsSet())
499 {
500 return ErrorCode::PENDING;
501 }
502
503#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
504 if (dma_backend_enabled_ && !tx_busy_.IsSet() && StartPendingTxIfIdle(in_isr))
505 {
506 return ErrorCode::PENDING;
507 }
508#endif
509
510 if (!tx_active_valid_)
511 {
512 (void)LoadActiveTxFromQueue(in_isr);
513 }
514
516 {
517 if (!StartActiveTransfer(in_isr))
518 {
520 return ErrorCode::FAILED;
521 }
522
523 return ErrorCode::OK;
524 }
525
526#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
527 if (dma_backend_enabled_ && tx_busy_.IsSet() && !tx_dma_buffer_.HasPending())
528 {
529 (void)LoadPendingTxFromQueue(in_isr);
530 }
531#endif
532
533 return ErrorCode::PENDING;
534}
535
536// Active TX load is shared by both backends. DMA writes payload bytes into the
537// double buffer, while FIFO mode only claims metadata and length.
538// Active TX 装载由两条后端共用。DMA 会把 payload 写进双缓冲;FIFO 模式只
539// 认领元数据和长度。
540bool IRAM_ATTR ESP32UART::LoadActiveTxFromQueue(bool in_isr)
541{
542 (void)in_isr;
543
544 size_t active_length = 0U;
545 WriteInfoBlock active_info = {};
546 uint8_t* active_buffer = nullptr;
547#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
548 if (dma_backend_enabled_)
549 {
550 active_buffer = tx_dma_buffer_.ActiveBuffer();
551 }
552#endif
553 if (!DequeueTxToBuffer(active_buffer, active_length, active_info, in_isr))
554 {
555 return false;
556 }
557
558#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
559 if (dma_backend_enabled_)
560 {
561 tx_dma_buffer_.SetActiveLength(active_length);
562 }
563 else
564#endif
565 {
566 tx_active_length_ = active_length;
567 }
569 tx_active_info_ = active_info;
570 tx_active_valid_ = true;
571 return true;
572}
573
574// Pending preload exists only for the DMA backend because FIFO mode can stream
575// directly from the queue without staging a second payload block.
576// Pending 预装只存在于 DMA 后端,因为 FIFO 模式可以直接从队列流式发送,
577// 不需要再暂存第二块 payload。
578bool IRAM_ATTR ESP32UART::LoadPendingTxFromQueue(bool in_isr)
579{
580 (void)in_isr;
581
582#if !(SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED)
583 return false;
584#else
585 if (!dma_backend_enabled_ || tx_dma_buffer_.HasPending())
586 {
587 return false;
588 }
589
590 size_t pending_length = 0U;
591 WriteInfoBlock pending_info = {};
592 if (write_port_->queue_info_->Peek(pending_info) != ErrorCode::OK)
593 {
594 return false;
595 }
596
597 if (pending_info.data.size_ > tx_dma_buffer_.Size())
598 {
599 ASSERT(false);
600 return false;
601 }
602
604 pending_info.data.size_) != ErrorCode::OK)
605 {
606 return false;
607 }
608
609 pending_length = pending_info.data.size_;
610 tx_dma_buffer_.SetPendingLength(pending_length);
612 return true;
613#endif
614}
615
616// Promotion only moves one preloaded pending request into the active slot.
617// Hardware start is a separate step.
618// 提升步骤只负责把一个已经预装的 pending 请求转成 active;
619// 真正启动硬件传输是单独一步。
621{
622#if !(SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED)
623 return false;
624#else
625 if (!dma_backend_enabled_)
626 {
627 return false;
628 }
629
631 {
632 return false;
633 }
634
635 const size_t pending_length = tx_dma_buffer_.GetPendingLength();
637 tx_dma_buffer_.SetActiveLength(pending_length);
638
640 {
641 ASSERT(false);
642 return false;
643 }
644 tx_active_length_ = pending_length;
646 tx_active_valid_ = true;
647
648 return true;
649#endif
650}
651
652// Starting the pending DMA request is allowed only when TX is fully idle.
653// 启动 pending DMA 请求只允许发生在 TX 完全空闲时。
654bool IRAM_ATTR ESP32UART::StartPendingTxIfIdle(bool in_isr)
655{
656#if !(SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED)
657 (void)in_isr;
658 return false;
659#else
660 if (!dma_backend_enabled_)
661 {
662 (void)in_isr;
663 return false;
664 }
665
666 if (tx_busy_.IsSet())
667 {
668 return false;
669 }
670
672 {
673 return false;
674 }
675
676 return StartAndReportActive(in_isr);
677#endif
678}
679
680// Queue-data and queue-info stay decoupled, so dequeue first validates the
681// next metadata entry and only then moves bytes or ownership forward.
682// `queue_data_` 和 `queue_info_` 保持解耦,因此这里先验证下一条元数据,
683// 再推进字节或所有权。
684bool IRAM_ATTR ESP32UART::DequeueTxToBuffer(uint8_t* buffer, size_t& size,
685 WriteInfoBlock& info, bool in_isr)
686{
687 (void)in_isr;
688 (void)buffer;
689
690 WriteInfoBlock peek_info = {};
691 if (write_port_->queue_info_->Peek(peek_info) != ErrorCode::OK)
692 {
693 return false;
694 }
695
696 size_t max_size = write_port_->queue_data_->MaxSize();
697#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
698 if (dma_backend_enabled_)
699 {
700 max_size = tx_dma_buffer_.Size();
701 }
702#endif
703 if (peek_info.data.size_ > max_size)
704 {
705 ASSERT(false);
706 return false;
707 }
708
709#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
710 if (dma_backend_enabled_)
711 {
712 if (write_port_->queue_data_->PopBatch(buffer, peek_info.data.size_) != ErrorCode::OK)
713 {
714 return false;
715 }
716 }
717#endif
718
719 if (write_port_->queue_info_->Pop(info) != ErrorCode::OK)
720 {
721 return false;
722 }
723
724 size = peek_info.data.size_;
725 return true;
726}
727
728// Backend start forks here:
729// - DMA path launches the staged active payload as one DMA transfer.
730// - FIFO path enables TX-empty interrupts and lets ISR-side refill drain data.
731// 后端启动在这里分叉:
732// - DMA 路径把已暂存的 active payload 作为一次 DMA 传输发出去。
733// - FIFO 路径开启 TX-empty 中断,让 ISR 侧补料并持续排空。
735{
736 if (!tx_active_valid_)
737 {
738 return false;
739 }
740
741 if (tx_busy_.TestAndSet())
742 {
743 return true;
744 }
745
747
748#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
749 if (dma_backend_enabled_)
750 {
751 if (StartDmaTx())
752 {
753 return true;
754 }
755
756 tx_busy_.Clear();
757 return false;
758 }
759#endif
760
761 uart_hal_clr_intsts_mask(&uart_hal_, UART_TX_INTR_MASK);
762 uart_hal_ena_intr_mask(&uart_hal_, UART_TX_INTR_MASK);
763 FillTxFifo(false);
764
765 return true;
766}
767
768// RX bytes are pushed opportunistically until the software queue is full, then
769// pending read callbacks are serviced once per batch.
770// RX 字节会尽量推进软件队列,直到队列满;完成后按批次触发待读回调。
771void IRAM_ATTR ESP32UART::PushRxBytes(const uint8_t* data, size_t size, bool in_isr)
772{
773 size_t offset = 0;
774 bool pushed_any = false;
775 while (offset < size)
776 {
777 const size_t free_space = read_port_->queue_data_->EmptySize();
778 if (free_space == 0)
779 {
780 break;
781 }
782
783 const size_t chunk = std::min(free_space, size - offset);
784 if (read_port_->queue_data_->PushBatch(data + offset, chunk) != ErrorCode::OK)
785 {
786 break;
787 }
788
789 offset += chunk;
790 pushed_any = true;
791 }
792
793 if (pushed_any)
794 {
796 }
797}
798
799// Completion does the complementary TX handoff work:
800// - if DMA has no pending request, TX is done here;
801// - if DMA has one pending request, start it and then try to preload the next one;
802// - FIFO mode directly loads and starts the next active request.
803// 完成路径负责与发起路径互补的交接工作:
804// - DMA 若没有 pending,请求链在这里结束;
805// - DMA 若有一个 pending,就先启动它,再尝试补装下一个;
806// - FIFO 模式则直接装载并启动下一个 active 请求。
807void IRAM_ATTR ESP32UART::OnTxTransferDone(bool in_isr, ErrorCode result)
808{
810 tx_busy_.Clear();
811
813
814#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
815 if (dma_backend_enabled_)
816 {
817 if ((result != ErrorCode::OK) && tx_dma_buffer_.HasPending())
818 {
819 WriteInfoBlock dropped_info = {};
820 if (write_port_->queue_info_->Pop(dropped_info) == ErrorCode::OK)
821 {
822 write_port_->Finish(in_isr, ErrorCode::FAILED, dropped_info);
823 }
824 else
825 {
826 ASSERT(false);
827 }
829 }
830
831 if (result != ErrorCode::OK)
832 {
833 return;
834 }
835
837 {
838 return;
839 }
840
841 if (!StartAndReportActive(in_isr))
842 {
843 return;
844 }
845
847 {
848 (void)LoadPendingTxFromQueue(in_isr);
849 }
850 return;
851 }
852#endif
853
854 if (result != ErrorCode::OK)
855 {
856 return;
857 }
858
859 if (LoadActiveTxFromQueue(in_isr))
860 {
861 (void)StartAndReportActive(in_isr);
862 }
863}
864
865} // namespace LibXR
size_t size_
数据字节数 / Data size in bytes
void SetActiveLength(size_t length)
设置当前活动缓冲区的数据长度 Sets the size of the active buffer
void Reset()
重置 active/pending 状态,但保留已绑定的 backing storage Resets active/pending state while keeping bound backing ...
void SetPendingLength(size_t length)
设置备用缓冲区的数据长度 Sets the size of the pending buffer
void EnablePending()
手动启用 pending 状态 Manually sets the pending state to true
bool HasPending() const
判断是否有待切换的缓冲区 Checks whether a pending buffer is ready
size_t Size() const
获取每个缓冲区的大小(单位:字节) Gets the size of each buffer in bytes
uint8_t * ActiveBuffer() const
获取当前正在使用的缓冲区指针 Returns the currently active buffer
void Init(const LibXR::RawData &raw_data)
绑定连续 backing storage 并重置双缓冲状态 Binds continuous backing storage and resets double-buffer state
uint8_t * PendingBuffer() const
获取备用缓冲区的指针 Returns the pending (inactive) buffer
void Switch()
切换到备用缓冲区(若其有效) Switches to the pending buffer if it's valid
size_t GetPendingLength() const
获取 pending 缓冲区中准备好的数据长度 Gets the size of valid data in pending buffer
static ErrorCode ResolveUartPeriph(uart_port_t uart_num, periph_module_t &out)
Map one UART index to its peripheral module.
Definition esp_uart.cpp:81
ErrorCode TryStartTx(bool in_isr)
Try to start queued transmit work.
Definition esp_uart.cpp:496
ErrorCode SetConfig(UART::Configuration config) override
Apply a new UART framing and baud configuration.
Definition esp_uart.cpp:185
UART::Configuration config_
Current UART framing configuration.
Definition esp_uart.hpp:354
ErrorCode SetLoopback(bool enable)
Toggle UART peripheral internal loopback mode.
Definition esp_uart.cpp:267
int rx_pin_
RX GPIO pin or PIN_NO_CHANGE.
Definition esp_uart.hpp:350
int tx_pin_
TX GPIO pin or PIN_NO_CHANGE.
Definition esp_uart.hpp:349
bool LoadActiveTxFromQueue(bool in_isr)
Load one active TX request from the queue.
Definition esp_uart.cpp:540
void ConfigureRxInterruptPath()
Program RX interrupt thresholds and masks.
Definition esp_uart.cpp:170
bool dma_requested_
Constructor preference for DMA mode.
Definition esp_uart.hpp:372
Flag::Plain tx_busy_
Hardware TX engine currently owns the request.
Definition esp_uart.hpp:365
bool StartActiveTransfer(bool in_isr)
Start the active request on the selected TX backend.
Definition esp_uart.cpp:734
static ErrorCode WriteFun(WritePort &port, bool in_isr)
Queue-driven TX entry used by WritePort.
Definition esp_uart.cpp:280
uart_hal_context_t uart_hal_
ESP-IDF UART HAL context.
Definition esp_uart.hpp:369
uint8_t * tx_storage_
Backing storage for the TX half-buffers.
Definition esp_uart.hpp:359
ErrorCode InitUartHardware()
Initialize UART hardware and base HAL state.
Definition esp_uart.cpp:351
static uart_parity_t ResolveParity(UART::Parity parity)
Convert configured parity into the HAL enum.
Definition esp_uart.cpp:333
size_t rx_isr_buffer_size_
Size of rx_isr_buffer_.
Definition esp_uart.hpp:357
void FillTxFifo(bool in_isr)
Drain active TX bytes into the UART FIFO backend.
ESP32UART(uart_port_t uart_num, int tx_pin, int rx_pin, int rts_pin=PIN_NO_CHANGE, int cts_pin=PIN_NO_CHANGE, size_t rx_buffer_size=1024, size_t tx_buffer_size=512, uint32_t tx_queue_size=5, UART::Configuration config={115200, UART::Parity::NO_PARITY, 8, 1}, bool enable_dma=true)
Create and initialize one ESP32 UART instance.
Definition esp_uart.cpp:104
bool tx_active_valid_
Whether the active TX metadata is valid.
Definition esp_uart.hpp:364
void OnTxTransferDone(bool in_isr, ErrorCode result)
Finalize one TX transfer result.
Definition esp_uart.cpp:807
WritePort _write_port
Write-side queue bridge exposed to UART.
Definition esp_uart.hpp:375
void DrainRxFifo(bool in_isr)
Drain pending bytes from the hardware RX FIFO.
WriteInfoBlock tx_active_info_
Metadata for the active TX request.
Definition esp_uart.hpp:361
int cts_pin_
CTS GPIO pin or PIN_NO_CHANGE.
Definition esp_uart.hpp:352
bool LoadPendingTxFromQueue(bool in_isr)
Preload one pending TX request for DMA mode.
Definition esp_uart.cpp:578
Flag::Plain in_tx_isr_
Reentry guard while processing TX ISR work.
Definition esp_uart.hpp:366
bool StartAndReportActive(bool in_isr)
Start the active request and report queue completion ownership.
Definition esp_uart.cpp:475
int rts_pin_
RTS GPIO pin or PIN_NO_CHANGE.
Definition esp_uart.hpp:351
bool StartPendingTxIfIdle(bool in_isr)
Start the current pending DMA request when TX is fully idle.
Definition esp_uart.cpp:654
static ErrorCode ReadFun(ReadPort &port, bool in_isr)
Queue-driven RX entry used by ReadPort.
Definition esp_uart.cpp:289
bool DequeueTxToBuffer(uint8_t *buffer, size_t &size, WriteInfoBlock &info, bool in_isr)
Dequeue one TX payload into the selected buffer.
Definition esp_uart.cpp:684
ESP32UARTReadPort _read_port
Read-side queue bridge exposed to UART.
Definition esp_uart.hpp:374
DoubleBuffer tx_dma_buffer_
TX double-buffer view for the DMA path.
Definition esp_uart.hpp:360
void ClearActiveTx()
Clear active TX state.
Definition esp_uart.cpp:451
static uint8_t * AllocateTxStorage(size_t size)
Allocate DMA-capable backing storage for TX buffers.
Definition esp_uart.cpp:66
static bool ResolveStopBits(uint8_t stop_bits, uart_stop_bits_t &out)
Convert configured stop bits into the HAL enum.
Definition esp_uart.cpp:316
uart_port_t uart_num_
Selected UART peripheral index.
Definition esp_uart.hpp:348
size_t tx_active_offset_
Bytes already emitted for the active request.
Definition esp_uart.hpp:363
bool PromotePendingTxToActive()
Promote one preloaded DMA pending request into the active slot.
Definition esp_uart.cpp:620
static bool ResolveWordLength(uint8_t data_bits, uart_word_length_t &out)
Convert configured data bits into the HAL enum.
Definition esp_uart.cpp:293
bool uart_hw_enabled_
UART hardware block was initialized.
Definition esp_uart.hpp:368
void PushRxBytes(const uint8_t *data, size_t size, bool in_isr)
Push RX bytes into the software queue.
Definition esp_uart.cpp:771
size_t tx_active_length_
Active TX payload length in bytes.
Definition esp_uart.hpp:362
void ClearPendingTx()
Clear pending TX state.
Definition esp_uart.cpp:461
ErrorCode InstallUartIsr()
Install the UART interrupt handler.
ErrorCode ConfigurePins()
Configure the selected GPIO pins.
Definition esp_uart.cpp:399
ESP32UART & owner_
所属 UART 后端 / Owning UART backend
Definition esp_uart.hpp:63
void OnRxDequeue(bool in_isr) override
软件队列出队后的回调 / Callback after software RX dequeue
Definition esp_uart.cpp:52
bool TestAndSet() noexcept
测试并置位:置位并返回旧状态 / Test-and-set: set and return previous state
Definition flag.hpp:146
bool IsSet() const noexcept
判断是否已置位 / Check whether the flag is set
Definition flag.hpp:138
void Clear() noexcept
清除标志 / Clear the flag
Definition flag.hpp:130
作用域标志管理器:构造时写入指定值,析构时恢复原值 / Scoped flag restorer: set on entry, restore on exit
Definition flag.hpp:199
ReadPort class for handling read operations.
Definition read_port.hpp:18
SPSCQueue< uint8_t > * queue_data_
RX payload queue. 接收数据字节队列。
Definition read_port.hpp:55
void ProcessPendingReads(bool in_isr)
Processes pending reads.
size_t MaxSize() const
获取队列最大容量 / Get the maximum queue capacity
size_t EmptySize() const
获取剩余空槽数 / Get the current free-slot count
ErrorCode PopBatch(Data *data, size_t size)
批量弹出多个 payload。
ErrorCode PushBatch(const Data *data, size_t size)
批量推入多个 payload。
通用异步收发传输(UART)基类 / Abstract base class for Universal Asynchronous Receiver-Transmitter (UART)
Definition uart.hpp:19
ReadPort * read_port_
读取端口 / Read port
Definition uart.hpp:53
Parity
奇偶校验模式 / Parity mode
Definition uart.hpp:29
@ NO_PARITY
无校验 / No parity
@ ODD
奇校验 / Odd parity
@ EVEN
偶校验 / Even parity
WritePort * write_port_
写入端口 / Write port
Definition uart.hpp:54
WritePort class for handling write operations.
void Finish(bool in_isr, ErrorCode ans, WriteInfoBlock &info)
更新写入操作的状态。 Updates the status of the write operation.
SPSCQueue< WriteInfoBlock > * queue_info_
Metadata queue for pending write batches. 挂起写批次的元数据队列。
SPSCQueue< uint8_t > * queue_data_
Payload queue for pending write bytes. 挂起写入字节的数据队列。
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举
@ INIT_ERR
初始化错误 | Initialization error
@ STATE_ERR
状态错误 | State error
@ NOT_SUPPORT
不支持 | Not supported
@ FAILED
操作失败 | Operation failed
@ PENDING
等待中 | Pending
@ OK
操作成功 | Operation successful
@ ARG_ERR
参数错误 | Argument error
ErrorCode(* ReadFun)(ReadPort &port, bool in_isr)
Function pointer type for read notifications.
ErrorCode(* WriteFun)(WritePort &port, bool in_isr)
Function pointer type for write operations.
OwnerType * ContainerOf(MemberType *ptr, MemberType OwnerType::*member) noexcept
通过成员指针恢复其所属对象指针
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
ConstRawData data
Data buffer. 数据缓冲区。