libxr
Embedded C++ framework for bare-metal, RTOS and Linux
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// 非 DMA 路径处理的 FIFO RX 中断原因。
10// FIFO RX interrupt reasons handled by the non-DMA path.
11constexpr uint32_t UART_RX_INTR_MASK =
12 UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_OVF;
13
14// 非 DMA 路径处理的 FIFO TX 中断原因。
15// FIFO TX interrupt reason handled by the non-DMA path.
16constexpr uint32_t UART_TX_INTR_MASK = UART_INTR_TXFIFO_EMPTY;
17constexpr uint32_t UART_TX_IDLE_INTR_MASK = UART_INTR_TX_DONE;
18} // namespace
19
20namespace LibXR
21{
22
23// ISR 入口只负责把控制转发给对象实例。
24// ISR entry only forwards control into the object instance.
25void IRAM_ATTR ESP32UART::UartIsrEntry(void* arg)
26{
27 auto* self = static_cast<ESP32UART*>(arg);
28 if (self != nullptr)
29 {
30 self->HandleUartInterrupt();
31 }
32}
33
34ErrorCode ESP32UART::InstallUartIsr()
35{
36 if (uart_isr_installed_)
37 {
38 return ErrorCode::OK;
39 }
40
41 // RW 接口和用户回调可能位于 Flash,缓存关闭时由 SDK 暂缓处理中断。
42 // RW methods and user callbacks may reside in flash; defer IRQs while cache is off.
43 constexpr int UART_INTR_FLAGS = 0;
44
45 const esp_err_t err = esp_intr_alloc(uart_periph_signal[uart_num_].irq, UART_INTR_FLAGS,
46 UartIsrEntry, this, &uart_intr_handle_);
47 if (err != ESP_OK)
48 {
50 }
51
52 uart_isr_installed_ = true;
53 return ErrorCode::OK;
54}
55
56// FIFO TX 路径会直接把字节从队列流式写入硬件 FIFO;与 DMA 模式不同,它不
57// 会暂存第二块 payload。
58// FIFO TX path streams bytes directly from the queue into the hardware FIFO.
59// Unlike DMA mode, it does not stage a second payload block.
60void IRAM_ATTR ESP32UART::FillTxFifo(bool in_isr)
61{
62 const uint32_t fifo_space = uart_hal_get_txfifo_len(&uart_hal_);
63 if (fifo_space == 0U)
64 {
65 if (_write_port.Size() != 0U)
66 {
67 EnableUartInterrupt(UART_INTR_TXFIFO_EMPTY);
68 }
69 return;
70 }
71
72 size_t accepted = 0U;
73 {
74 auto queue = _write_port.GetWriteQueue(in_isr);
75 if (queue.Empty())
76 {
77 DisableUartInterrupt(UART_INTR_TXFIFO_EMPTY);
78 return;
79 }
80
81 accepted = queue.PopWithWriter(
82 std::min<size_t>(fifo_space, queue.AvailableSize()),
83 [this](const uint8_t* first, size_t first_size, const uint8_t* second,
84 size_t second_size) -> size_t
85 {
86 size_t written = 0U;
87 if (first_size != 0U)
88 {
89 uint32_t first_written = 0U;
90 uart_hal_write_txfifo(&uart_hal_, first, static_cast<uint32_t>(first_size),
91 &first_written);
92 written = std::min<size_t>(first_written, first_size);
93 if (written != first_size)
94 {
95 return written;
96 }
97 }
98 if (second_size != 0U)
99 {
100 uint32_t second_written = 0U;
101 uart_hal_write_txfifo(&uart_hal_, second, static_cast<uint32_t>(second_size),
102 &second_written);
103 written += std::min<size_t>(second_written, second_size);
104 }
105 return written;
106 });
107 }
108
109 if (accepted == 0U || _write_port.Size() != 0U)
110 {
111 EnableUartInterrupt(UART_INTR_TXFIFO_EMPTY);
112 }
113 else
114 {
115 DisableUartInterrupt(UART_INTR_TXFIFO_EMPTY);
116 }
117}
118
119// RX FIFO 清空采用尽力而为策略:尽量读取软件队列还能容纳的部分,然后停止,
120// 不会粗暴重置整个硬件 FIFO。
121// RX FIFO draining is best-effort: read as much as the software queue can
122// absorb, then stop without resetting the whole hardware FIFO.
123void IRAM_ATTR ESP32UART::DrainRxFifo(bool in_isr)
124{
125 auto queue = _read_port.GetReadQueue(in_isr);
126 while (uart_hal_get_rxfifo_len(&uart_hal_) > 0U)
127 {
128 const size_t free_space = queue.EmptySize();
129 if (free_space == 0U)
130 {
131 break;
132 }
133
134 const size_t chunk =
135 std::min({free_space, rx_isr_buffer_size_,
136 static_cast<size_t>(uart_hal_get_rxfifo_len(&uart_hal_))});
137 DEV_ASSERT_FROM_CALLBACK(chunk != 0U, in_isr);
138 int read_size = static_cast<int>(chunk);
139 uart_hal_read_rxfifo(&uart_hal_, rx_isr_buffer_, &read_size);
140 DEV_ASSERT_FROM_CALLBACK(read_size > 0 && static_cast<size_t>(read_size) <= chunk,
141 in_isr);
142 [[maybe_unused]] const auto push_batch_result =
143 queue.PushBatch(rx_isr_buffer_, static_cast<size_t>(read_size));
144 DEV_ASSERT_FROM_CALLBACK(push_batch_result == ErrorCode::OK, in_isr);
145 }
146
147 queue.Publish();
148 if (queue.EmptySize() == 0U)
149 {
150 DisableUartInterrupt(UART_RX_INTR_MASK);
151 }
152 else
153 {
154 EnableUartInterrupt(UART_RX_INTR_MASK);
155 }
156}
157
158// FIFO 后端会在离开 ISR 前排空当前所有待处理 UART 中断原因,避免同周期的
159// RX/TX 事件被遗留。
160// The FIFO backend drains all currently pending UART interrupt reasons before
161// leaving the ISR to avoid orphaning a same-cycle RX/TX event.
162void IRAM_ATTR ESP32UART::HandleUartInterrupt()
163{
164 const uint32_t handled_mask =
165 UART_RX_INTR_MASK | UART_TX_INTR_MASK | UART_TX_IDLE_INTR_MASK;
166 uint32_t handled = CaptureUartInterrupt(handled_mask);
167 while (handled != 0U)
168 {
169 uint32_t events = 0U;
170 if ((handled & UART_RX_INTR_MASK) != 0U)
171 {
172 events |= EVENT_RX_WORK;
173 }
174 if ((handled & (UART_TX_INTR_MASK | UART_TX_IDLE_INTR_MASK)) != 0U)
175 {
176 events |= EVENT_TX_WORK;
177 }
178 service_.Invoke(events, true, [this](uint32_t owner_events, bool owner_isr)
179 { ServiceEvents(owner_events, owner_isr); });
180 handled = CaptureUartInterrupt(handled_mask);
181 }
182}
183
184uint32_t IRAM_ATTR ESP32UART::CaptureUartInterrupt(uint32_t mask)
185{
186 esp_os_enter_critical_safe(&irq_lock_);
187 const uint32_t handled = uart_hal_get_intsts_mask(&uart_hal_) & mask;
188 if (handled != 0U)
189 {
190 uart_hal_disable_intr_mask(&uart_hal_, handled);
191 // 在中断使能寄存器仍受保护时确认本次快照。
192 // Acknowledge the snapshot while the enable register is still protected.
193 uart_hal_clr_intsts_mask(&uart_hal_, handled);
194 }
195 esp_os_exit_critical_safe(&irq_lock_);
196 return handled;
197}
198
199} // namespace LibXR
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)
构造并初始化 ESP UART / Construct and initialize an ESP UART.
Definition esp_uart.cpp:146
size_t PopWithWriter(size_t limit, Writer &&writer)
通过回调取走队头前缀 / Consume a front prefix through a callback.
WriteQueue GetWriteQueue(bool in_isr=false)
获取已提交队头的出队接口 / Obtain a consumer interface for the released front.
size_t Size() const
获取字节队列当前数据量 / Get current byte queue occupancy.
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ INIT_ERR
初始化错误 / Initialization error
@ OK
操作成功 / Operation successful