libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
esp_uart_fifo.cpp
1#include <algorithm>
2
3#include "esp_attr.h"
4#include "esp_uart.hpp"
5#include "soc/uart_periph.h"
6
7namespace
8{
9// FIFO RX interrupt reasons handled by the non-DMA path.
10// 非 DMA 路径处理的 FIFO RX 中断原因。
11constexpr uint32_t UART_RX_INTR_MASK =
12 UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_OVF;
13
14// FIFO TX interrupt reason handled by the non-DMA path.
15// 非 DMA 路径处理的 FIFO TX 中断原因。
16constexpr uint32_t UART_TX_INTR_MASK = UART_INTR_TXFIFO_EMPTY;
17} // namespace
18
19namespace LibXR
20{
21
22// ISR entry only forwards control into the object instance.
23// ISR 入口只负责把控制转发给对象实例。
24void IRAM_ATTR ESP32UART::UartIsrEntry(void* arg)
25{
26 auto* self = static_cast<ESP32UART*>(arg);
27 if (self != nullptr)
28 {
29 self->HandleUartInterrupt();
30 }
31}
32
33// FIFO mode relies on a UART peripheral interrupt instead of the UHCI/GDMA
34// backend, and some classic ESP targets cannot safely keep this interrupt in
35// IRAM because the write-side queue path still touches flash-resident code.
36// FIFO 模式依赖 UART 外设中断而不是 UHCI/GDMA 后端;同时某些经典 ESP 目标
37// 由于写侧队列路径仍会触碰 flash 代码,因此不能安全地把该中断放入 IRAM。
39{
41 {
42 return ErrorCode::OK;
43 }
44
45#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3
46 // Classic ESP32/S3 can enter cache-disabled flash windows while UART IRQ is active.
47 // The current WritePort path is not fully IRAM-safe, so keep this IRQ non-IRAM.
48 constexpr int UART_INTR_FLAGS = 0;
49#else
50 constexpr int UART_INTR_FLAGS = ESP_INTR_FLAG_IRAM;
51#endif
52
53 const esp_err_t err = esp_intr_alloc(uart_periph_signal[uart_num_].irq, UART_INTR_FLAGS,
55 if (err != ESP_OK)
56 {
58 }
59
61 return ErrorCode::OK;
62}
63
64// FIFO TX path streams bytes directly from the queue into the hardware FIFO.
65// Unlike DMA mode, it does not stage a second payload block.
66// FIFO TX 路径会直接把字节从队列流式写入硬件 FIFO;与 DMA 模式不同,它不
67// 会暂存第二块 payload。
68void IRAM_ATTR ESP32UART::FillTxFifo(bool in_isr)
69{
71 {
72 return;
73 }
74
76 {
77 const uint32_t fifo_space = uart_hal_get_txfifo_len(&uart_hal_);
78 if (fifo_space == 0U)
79 {
80 break;
81 }
82
83 const size_t remaining = tx_active_length_ - tx_active_offset_;
84 const size_t chunk_size = std::min<size_t>(remaining, fifo_space);
85
87 chunk_size,
88 [this](const uint8_t* src, size_t size) -> ErrorCode
89 {
90 uint32_t write_size = 0;
91 uart_hal_write_txfifo(&uart_hal_, src, static_cast<uint32_t>(size),
92 &write_size);
93 return (write_size == static_cast<uint32_t>(size)) ? ErrorCode::OK
95 });
96 if (pop_ec != ErrorCode::OK)
97 {
98 ASSERT(false);
99 break;
100 }
101
102 tx_active_offset_ += chunk_size;
103 }
104
105 if ((tx_active_offset_ < tx_active_length_) || !in_isr)
106 {
107 return;
108 }
109
110 uart_hal_disable_intr_mask(&uart_hal_, UART_INTR_TXFIFO_EMPTY);
112}
113
114// RX FIFO draining is best-effort: read as much as the software queue can
115// absorb, then stop without resetting the whole hardware FIFO.
116// RX FIFO 清空采用尽力而为策略:尽量读取软件队列还能容纳的部分,然后停止,
117// 不会粗暴重置整个硬件 FIFO。
118void IRAM_ATTR ESP32UART::DrainRxFifo(bool in_isr)
119{
120 if (!rx_config_gate_.TryEnterRx())
121 {
122 return;
123 }
124
125 bool pushed_any = false;
126 while (uart_hal_get_rxfifo_len(&uart_hal_) > 0U)
127 {
128 const size_t fifo_len = uart_hal_get_rxfifo_len(&uart_hal_);
129 const size_t write_len = std::min(fifo_len, read_port_->queue_data_->EmptySize());
130 if (write_len == 0U)
131 {
132 break;
133 }
134
136 write_len,
137 [this](uint8_t* buffer, size_t chunk_size) -> ErrorCode
138 {
139 int read_len = static_cast<int>(chunk_size);
140 uart_hal_read_rxfifo(&uart_hal_, buffer, &read_len);
141 return (read_len == static_cast<int>(chunk_size)) ? ErrorCode::OK
143 });
144
145 if ((push_ec == ErrorCode::FULL) || (push_ec == ErrorCode::EMPTY))
146 {
147 break;
148 }
149 if (push_ec != ErrorCode::OK)
150 {
151 ASSERT(false);
152 break;
153 }
154
155 pushed_any = true;
156 }
157
158 if (pushed_any)
159 {
161 }
162
163 if (rx_config_gate_.LeaveRx())
164 {
165 tx_dma_model_.RequestConfig(in_isr);
166 }
167}
168
169// RX interrupt handling distinguishes overflow from ordinary data-ready events
170// so that overflow can be acknowledged without discarding remaining FIFO bytes.
171// RX 中断处理区分 overflow 和普通 data-ready 事件,以便在确认 overflow
172// 的同时保留剩余 FIFO 字节。
173void IRAM_ATTR ESP32UART::HandleRxInterrupt(uint32_t uart_intr_status)
174{
175 const bool has_overflow = (uart_intr_status & UART_INTR_RXFIFO_OVF) != 0U;
176 const bool has_rx_data =
177 (uart_intr_status & (UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT)) != 0U;
178
179 if (!has_overflow && !has_rx_data)
180 {
181 return;
182 }
183
184 if (has_overflow)
185 {
186 // Overrun means at least one incoming byte was dropped. Keep remaining FIFO
187 // bytes to minimize extra loss instead of resetting the whole RX FIFO.
188 uart_hal_clr_intsts_mask(&uart_hal_, UART_INTR_RXFIFO_OVF);
189 }
190
191 DrainRxFifo(true);
192 uart_hal_clr_intsts_mask(&uart_hal_, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);
193}
194
195// TX interrupt handling enters the FIFO refill path under a scoped reentry
196// guard so that queue callbacks cannot recursively restart TX.
197// TX 中断处理在受控的重入保护下进入 FIFO 补料路径,避免队列回调递归重启 TX。
198void IRAM_ATTR ESP32UART::HandleTxInterrupt(uint32_t uart_intr_status)
199{
200 if ((uart_intr_status & UART_INTR_TXFIFO_EMPTY) == 0U)
201 {
202 return;
203 }
204
206 FillTxFifo(true);
207 uart_hal_clr_intsts_mask(&uart_hal_, UART_INTR_TXFIFO_EMPTY);
208}
209
210// The FIFO backend drains all currently pending UART interrupt reasons before
211// leaving the ISR to avoid orphaning a same-cycle RX/TX event.
212// FIFO 后端会在离开 ISR 前排空当前所有待处理 UART 中断原因,避免同周期的
213// RX/TX 事件被遗留。
215{
216 uint32_t uart_intr_status = uart_hal_get_intsts_mask(&uart_hal_);
217
218 while (uart_intr_status != 0)
219 {
220 if (uart_intr_status & UART_RX_INTR_MASK)
221 {
222 HandleRxInterrupt(uart_intr_status);
223 }
224
225 if (uart_intr_status & UART_TX_INTR_MASK)
226 {
227 HandleTxInterrupt(uart_intr_status);
228 }
229
230 uart_intr_status = uart_hal_get_intsts_mask(&uart_hal_);
231 }
232}
233
234} // namespace LibXR
ESP32 UART backend with FIFO and optional GDMA fast paths.
Definition esp_uart.hpp:79
void HandleUartInterrupt()
Dispatch pending UART interrupt reasons.
intr_handle_t uart_intr_handle_
Registered UART interrupt handle.
Definition esp_uart.hpp:357
uart_hal_context_t uart_hal_
ESP-IDF UART HAL context.
Definition esp_uart.hpp:356
void FillTxFifo(bool in_isr)
Drain active TX bytes into the UART FIFO backend.
bool tx_active_valid_
Whether the active TX metadata is valid.
Definition esp_uart.hpp:351
void OnTxTransferDone(bool in_isr, ErrorCode result)
Finalize one TX transfer result.
Definition esp_uart.cpp:686
void DrainRxFifo(bool in_isr)
Drain pending bytes from the hardware RX FIFO.
void HandleTxInterrupt(uint32_t uart_intr_status)
Handle TX-side UART interrupt reasons.
Flag::Plain in_tx_isr_
Reentry guard while processing TX ISR work.
Definition esp_uart.hpp:353
void HandleRxInterrupt(uint32_t uart_intr_status)
Handle RX-side UART interrupt reasons.
bool uart_isr_installed_
UART ISR installation state.
Definition esp_uart.hpp:358
UartDmaTxModel< ESP32UART > tx_dma_model_
One-shot DMA TX execution model.
Definition esp_uart.hpp:363
uart_port_t uart_num_
Selected UART peripheral index.
Definition esp_uart.hpp:334
size_t tx_active_offset_
Bytes already emitted for the active request.
Definition esp_uart.hpp:350
size_t tx_active_length_
Active TX payload length in bytes.
Definition esp_uart.hpp:349
ErrorCode InstallUartIsr()
Install the UART interrupt handler.
static void UartIsrEntry(void *arg)
UART ISR trampoline.
作用域标志管理器:构造时写入指定值,析构时恢复原值 / Scoped flag restorer: set on entry, restore on exit
Definition flag.hpp:199
SPSCQueue< uint8_t > * queue_data_
RX payload queue. 接收数据字节队列。
Definition read_port.hpp:55
void ProcessPendingReads(bool in_isr)
Processes pending reads.
size_t EmptySize() const
获取剩余空槽数 / Get the current free-slot count
ErrorCode PopWithReader(Reader &&reader)
通过读取器回调弹出一个 payload。
ErrorCode PushWithWriter(Writer &&writer)
通过写入器回调推入一个 payload。
ReadPort * read_port_
读取端口 / Read port
Definition uart.hpp:53
WritePort * write_port_
写入端口 / Write port
Definition uart.hpp:54
SPSCQueue< uint8_t > * queue_data_
Payload queue for pending write bytes. 挂起写入字节的数据队列。
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举
@ INIT_ERR
初始化错误 | Initialization error
@ EMPTY
为空 | Empty
@ FULL
已满 | Full
@ OK
操作成功 | Operation successful