7#include "esp_clk_tree.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"
20constexpr uint32_t UART_RX_INTR_MASK =
21 UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_OVF;
25constexpr uint32_t UART_TX_INTR_MASK = UART_INTR_TXFIFO_EMPTY;
26constexpr uint32_t UART_TX_IDLE_INTR_MASK = UART_INTR_TX_DONE;
31constexpr uint8_t RX_TOUT_THRESHOLD = 1;
35constexpr uint16_t TX_EMPTY_THRESHOLD = SOC_UART_FIFO_LEN / 2U;
39bool IsConsoleUartInUse(uart_port_t uart_num)
41#if defined(CONFIG_ESP_CONSOLE_UART) && CONFIG_ESP_CONSOLE_UART
42 return static_cast<int>(uart_num) == CONFIG_ESP_CONSOLE_UART_NUM;
49bool IsBaudrateRepresentable(uint32_t baudrate, uint32_t source_clock_hz)
51 if ((baudrate == 0U) || (source_clock_hz == 0U))
56#if defined(UART_SCLK_DIV_NUM_V) || defined(PCR_UART0_SCLK_DIV_NUM_V) || \
57 defined(HP_SYS_CLKRST_REG_UART0_SCLK_DIV_NUM_V)
58#if defined(UART_SCLK_DIV_NUM_V)
59 constexpr uint32_t max_source_divider = UART_SCLK_DIV_NUM_V + 1U;
60#elif defined(PCR_UART0_SCLK_DIV_NUM_V)
61 constexpr uint32_t max_source_divider = PCR_UART0_SCLK_DIV_NUM_V + 1U;
63 constexpr uint32_t max_source_divider = HP_SYS_CLKRST_REG_UART0_SCLK_DIV_NUM_V + 1U;
66 const uint64_t max_uart_divider = UART_CLKDIV_V;
67 const uint64_t denominator = max_uart_divider * baudrate;
68 const uint64_t source_divider =
69 (
static_cast<uint64_t
>(source_clock_hz) + denominator - 1U) / denominator;
70 if ((source_divider == 0U) || (source_divider > max_source_divider))
75 const uint64_t clock_divider = (
static_cast<uint64_t
>(source_clock_hz) << 4U) /
76 (
static_cast<uint64_t
>(baudrate) * source_divider);
77 return (clock_divider >> 4U) <= max_uart_divider;
79 const uint64_t clock_divider =
80 (
static_cast<uint64_t
>(source_clock_hz) << 4U) / baudrate;
81 return (clock_divider >> 4U) <= UART_CLKDIV_V;
85bool GetUartClockFrequency(uint32_t& out_frequency_hz)
87 out_frequency_hz = 0U;
88 return (esp_clk_tree_src_get_freq_hz(
static_cast<soc_module_clk_t
>(UART_SCLK_DEFAULT),
89 ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED,
90 &out_frequency_hz) == ESP_OK) &&
91 (out_frequency_hz != 0U);
100 owner_.service_.
Invoke(ESP32UART::EVENT_RX_WORK, in_isr,
101 [
this](uint32_t events,
bool owner_isr)
102 { owner_.ServiceEvents(events, owner_isr); });
108uint8_t* ESP32UART::AllocateTxStorage(
size_t size)
110 void* aligned = heap_caps_aligned_alloc(
111 4, size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
112 if (aligned !=
nullptr)
114 return static_cast<uint8_t*
>(aligned);
117 return static_cast<uint8_t*
>(
118 heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT));
123ErrorCode ESP32UART::ResolveUartPeriph(uart_port_t uart_num, periph_module_t& out)
128 out = PERIPH_UART0_MODULE;
131 out = PERIPH_UART1_MODULE;
133#if SOC_UART_HP_NUM > 2
135 out = PERIPH_UART2_MODULE;
147 int cts_pin,
size_t rx_buffer_size,
size_t tx_buffer_size,
149 :
UART(&_read_port, &_write_port),
156 rx_isr_buffer_(new uint8_t[rx_buffer_size]),
157 rx_isr_buffer_size_(rx_buffer_size),
158 tx_storage_(AllocateTxStorage(tx_buffer_size * 2)),
159 dma_requested_(enable_dma),
160 _read_port(rx_buffer_size, *this),
161 _write_port(tx_queue_size, tx_buffer_size)
163 ASSERT(!IsConsoleUartInUse(uart_num_));
164 ASSERT(uart_num_ < UART_NUM_MAX);
165 ASSERT(uart_num_ < SOC_UART_HP_NUM);
166 ASSERT(rx_isr_buffer_size_ > 0);
167 ASSERT(tx_buffer_size > 0);
168 ASSERT(tx_queue_size > 0U);
170 INIT_CRIT_SECTION_LOCK_RUNTIME(&irq_lock_);
172 tx_dma_buffer_.
Init({tx_storage_, tx_buffer_size * 2U});
182#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
192 if (!dma_backend_enabled_)
199 ConfigureRxInterruptPath();
207 ConfigureRxInterruptPath();
214void ESP32UART::ConfigureRxInterruptPath()
216 const uint16_t full_thr =
static_cast<uint16_t
>(SOC_UART_FIFO_LEN / 2U);
218 uart_hal_set_rxfifo_full_thr(&uart_hal_, full_thr);
219 uart_hal_set_rx_timeout(&uart_hal_, RX_TOUT_THRESHOLD);
220 ClearUartInterrupt(UART_RX_INTR_MASK);
221 EnableUartInterrupt(UART_RX_INTR_MASK);
228 if (!uart_hw_enabled_)
233 uart_word_length_t word_length = UART_DATA_8_BITS;
234 uart_stop_bits_t stop_bits = UART_STOP_BITS_1;
235 if (config.
baudrate == 0U || !ResolveWordLength(config.
data_bits, word_length) ||
236 !ResolveStopBits(config.
stop_bits, stop_bits) ||
245 if (!IsBaudrateRepresentable(config.
baudrate, uart_sclk_hz_))
250 uint8_t expected =
static_cast<uint8_t
>(ConfigState::EMPTY);
251 if (!config_state_.compare_exchange_strong(
252 expected,
static_cast<uint8_t
>(ConfigState::RESERVED),
253 std::memory_order_acq_rel, std::memory_order_acquire))
258 requested_config_ = config;
259 config_state_.store(
static_cast<uint8_t
>(ConfigState::PUBLISHED),
260 std::memory_order_release);
261 service_.
Invoke(EVENT_CONFIG, in_isr, [
this](uint32_t events,
bool owner_isr)
262 { ServiceEvents(events, owner_isr); });
268 if (!uart_hw_enabled_)
273 uart_word_length_t word_length = UART_DATA_8_BITS;
274 uart_stop_bits_t stop_bits = UART_STOP_BITS_1;
275 [[maybe_unused]]
const auto resolve_word_length_result =
276 ResolveWordLength(config.
data_bits, word_length);
277 DEV_ASSERT(resolve_word_length_result);
278 [[maybe_unused]]
const auto resolve_stop_bits_result =
279 ResolveStopBits(config.
stop_bits, stop_bits);
280 DEV_ASSERT(resolve_stop_bits_result);
282 const uart_sclk_t sclk = UART_SCLK_DEFAULT;
283 uart_hal_set_sclk(&uart_hal_,
static_cast<soc_module_clk_t
>(sclk));
285 if (uart_sclk_hz_ == 0U)
290 if (!uart_hal_set_baudrate(&uart_hal_, config.
baudrate, uart_sclk_hz_))
295 uart_hal_set_data_bit_num(&uart_hal_, word_length);
296 uart_hal_set_stop_bits(&uart_hal_, stop_bits);
297 uart_hal_set_parity(&uart_hal_, ResolveParity(config.
parity));
298 uart_hal_set_hw_flow_ctrl(&uart_hal_, UART_HW_FLOWCTRL_DISABLE, 0);
299 uart_hal_set_mode(&uart_hal_, UART_MODE_UART);
300 uart_hal_set_txfifo_empty_thr(&uart_hal_, TX_EMPTY_THRESHOLD);
301 uart_hal_rxfifo_rst(&uart_hal_);
302 ClearUartInterrupt(UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);
306void IRAM_ATTR ESP32UART::EnableUartInterrupt(uint32_t mask)
308 esp_os_enter_critical_safe(&irq_lock_);
309 uart_hal_ena_intr_mask(&uart_hal_, mask);
310 esp_os_exit_critical_safe(&irq_lock_);
313void IRAM_ATTR ESP32UART::DisableUartInterrupt(uint32_t mask)
315 esp_os_enter_critical_safe(&irq_lock_);
316 uart_hal_disable_intr_mask(&uart_hal_, mask);
317 esp_os_exit_critical_safe(&irq_lock_);
320void IRAM_ATTR ESP32UART::ClearUartInterrupt(uint32_t mask)
322 esp_os_enter_critical_safe(&irq_lock_);
323 uart_hal_clr_intsts_mask(&uart_hal_, mask);
324 esp_os_exit_critical_safe(&irq_lock_);
327void IRAM_ATTR ESP32UART::DisableAndClearUartInterrupt(uint32_t mask)
329 esp_os_enter_critical_safe(&irq_lock_);
330 uart_hal_disable_intr_mask(&uart_hal_, mask);
331 uart_hal_clr_intsts_mask(&uart_hal_, mask);
332 esp_os_exit_critical_safe(&irq_lock_);
335bool IRAM_ATTR ESP32UART::TryApplyPublishedConfig(
bool in_isr)
337 if (config_state_.load(std::memory_order_acquire) !=
338 static_cast<uint8_t
>(ConfigState::PUBLISHED))
343 bool tx_idle = !tx_busy_.
IsSet() && !tx_active_valid_
344#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
348#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
349 if (!dma_backend_enabled_)
352 tx_idle = tx_idle && (_write_port.
Size() == 0U) && uart_hal_is_tx_idle(&uart_hal_);
357#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
358 if (!dma_backend_enabled_)
363 EnableUartInterrupt(UART_TX_IDLE_INTR_MASK);
368#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
369 if (!dma_backend_enabled_)
372 DisableAndClearUartInterrupt(UART_TX_IDLE_INTR_MASK);
375#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
376 if (dma_backend_enabled_)
383 [[maybe_unused]]
const auto apply_config_result = ApplyConfig(requested_config_);
384 REQUIRE_FROM_CALLBACK(apply_config_result ==
ErrorCode::OK, in_isr);
385 config_ = requested_config_;
386 config_state_.store(
static_cast<uint8_t
>(ConfigState::EMPTY),
387 std::memory_order_release);
388#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
389 if (dma_backend_enabled_)
391 ResetDmaRxDescriptors(in_isr);
398void IRAM_ATTR ESP32UART::ServiceEvents(uint32_t events,
bool in_isr)
400 if ((events & EVENT_RX_ERROR) != 0U)
402#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
403 if (dma_backend_enabled_)
405 HandleDmaRxError(in_isr);
410 const bool rx_error = (events & EVENT_RX_ERROR) != 0U;
411 const bool tx_error = (events & EVENT_TX_ERROR) != 0U;
414#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
415 if (dma_backend_enabled_)
417 HandleDmaTxError(in_isr);
422 if (!tx_error && (events & EVENT_TX_DONE) != 0U)
427 if ((events & (EVENT_WRITE | EVENT_TX_DONE | EVENT_TX_ERROR | EVENT_TX_WORK |
428 EVENT_CONFIG)) != 0U)
430 (void)TryStartTx(in_isr);
435 if (TryApplyPublishedConfig(in_isr))
439 (void)TryStartTx(in_isr);
442 if (!rx_error && (events & EVENT_RX_WORK) != 0U)
444#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
445 if (dma_backend_enabled_)
447 HandleDmaRxDone(
nullptr, in_isr);
462 if (!uart_hw_enabled_)
467 uart_ll_set_loop_back(uart_hal_.dev, enable);
476 uart->service_.Invoke(ESP32UART::EVENT_WRITE, in_isr,
477 [uart](uint32_t events,
bool owner_isr)
478 { uart->ServiceEvents(events, owner_isr); });
483bool ESP32UART::ResolveWordLength(uint8_t data_bits, uart_word_length_t& out)
488 out = UART_DATA_5_BITS;
491 out = UART_DATA_6_BITS;
494 out = UART_DATA_7_BITS;
497 out = UART_DATA_8_BITS;
506bool ESP32UART::ResolveStopBits(uint8_t stop_bits, uart_stop_bits_t& out)
511 out = UART_STOP_BITS_1;
514 out = UART_STOP_BITS_2;
523uart_parity_t ESP32UART::ResolveParity(
UART::Parity parity)
528 return UART_PARITY_DISABLE;
530 return UART_PARITY_EVEN;
532 return UART_PARITY_ODD;
534 return UART_PARITY_DISABLE;
543 if (uart_num_ >= UART_NUM_MAX)
548 periph_module_t uart_module = PERIPH_MODULE_MAX;
549 if (ResolveUartPeriph(uart_num_, uart_module) !=
ErrorCode::OK)
554 uart_hal_.dev = UART_LL_GET_HW(uart_num_);
555 if (uart_hal_.dev ==
nullptr)
560 periph_module_enable(uart_module);
561 periph_module_reset(uart_module);
563 uart_ll_sclk_enable(uart_hal_.dev);
564 uart_hal_init(&uart_hal_, uart_num_);
566 const uart_sclk_t sclk = UART_SCLK_DEFAULT;
567 uart_hal_set_sclk(&uart_hal_,
static_cast<soc_module_clk_t
>(sclk));
568 if (!GetUartClockFrequency(uart_sclk_hz_))
573 uart_hw_enabled_ =
true;
576 uart_hw_enabled_ =
false;
582 uart_hw_enabled_ =
false;
586 uart_hal_txfifo_rst(&uart_hal_);
587 uart_hal_rxfifo_rst(&uart_hal_);
588 DisableAndClearUartInterrupt(UINT32_MAX);
599 if (!GPIO_IS_VALID_OUTPUT_GPIO(tx_pin_))
603 esp_rom_gpio_pad_select_gpio(
static_cast<uint32_t
>(tx_pin_));
604 esp_rom_gpio_connect_out_signal(
605 tx_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_TX_PIN_IDX),
false,
false);
610 if (!GPIO_IS_VALID_GPIO(rx_pin_))
614 gpio_input_enable(
static_cast<gpio_num_t
>(rx_pin_));
615 esp_rom_gpio_connect_in_signal(
616 rx_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_RX_PIN_IDX),
false);
621 if (!GPIO_IS_VALID_OUTPUT_GPIO(rts_pin_))
625 esp_rom_gpio_pad_select_gpio(
static_cast<uint32_t
>(rts_pin_));
626 esp_rom_gpio_connect_out_signal(
627 rts_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_RTS_PIN_IDX),
false,
false);
632 if (!GPIO_IS_VALID_GPIO(cts_pin_))
636 gpio_pullup_en(
static_cast<gpio_num_t
>(cts_pin_));
637 gpio_input_enable(
static_cast<gpio_num_t
>(cts_pin_));
638 esp_rom_gpio_connect_in_signal(
639 cts_pin_, UART_PERIPH_SIGNAL(uart_num_, SOC_UART_CTS_PIN_IDX),
false);
647void IRAM_ATTR ESP32UART::ClearActiveTx()
649 tx_active_length_ = 0U;
650 tx_active_valid_ =
false;
655bool IRAM_ATTR ESP32UART::StartAndReportActive(
bool in_isr)
657 const bool started = StartActiveTransfer(in_isr);
658 REQUIRE_FROM_CALLBACK(started, in_isr);
672ErrorCode IRAM_ATTR ESP32UART::TryStartTx(
bool in_isr)
674#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
675 if (dma_backend_enabled_)
677 if (!tx_busy_.
IsSet())
679 if (!tx_active_valid_ && config_state_.load(std::memory_order_acquire) !=
680 static_cast<uint8_t
>(ConfigState::EMPTY))
684 if (!tx_active_valid_ && !LoadActiveTxFromQueue(in_isr))
688 if (tx_active_valid_ && !StartAndReportActive(in_isr))
694 config_state_.load(std::memory_order_acquire) ==
695 static_cast<uint8_t
>(ConfigState::EMPTY))
697 (void)LoadPendingTxFromQueue(in_isr);
709bool IRAM_ATTR ESP32UART::LoadActiveTxFromQueue(
bool in_isr)
711#if !(SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED)
715 if (!dma_backend_enabled_)
727 DEV_ASSERT_FROM_CALLBACK(size <= tx_dma_buffer_.
Size(), in_isr);
730 tx_active_length_ = size;
731 tx_active_valid_ =
true;
740bool IRAM_ATTR ESP32UART::LoadPendingTxFromQueue(
bool in_isr)
742#if !(SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED)
746 if (!dma_backend_enabled_ || tx_dma_buffer_.
HasPending())
758 DEV_ASSERT_FROM_CALLBACK(size <= tx_dma_buffer_.
Size(), in_isr);
770bool IRAM_ATTR ESP32UART::PromotePendingTxToActive()
772#if !(SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED)
775 if (!dma_backend_enabled_)
780 if (tx_active_valid_ || !tx_dma_buffer_.
HasPending())
789 tx_active_length_ = pending_length;
790 tx_active_valid_ =
true;
798bool IRAM_ATTR ESP32UART::StartActiveTransfer(
bool)
800 if (!tx_active_valid_)
810#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
811 if (dma_backend_enabled_)
823 ClearUartInterrupt(UART_TX_INTR_MASK);
824 EnableUartInterrupt(UART_TX_INTR_MASK);
833void IRAM_ATTR ESP32UART::PushRxBytes(
const uint8_t* data,
size_t size,
bool in_isr)
837 while (offset < size)
839 const size_t free_space = queue.EmptySize();
845 const size_t chunk = std::min(free_space, size - offset);
846 [[maybe_unused]]
const auto push_batch_result = queue.PushBatch(data + offset, chunk);
847 DEV_ASSERT_FROM_CALLBACK(push_batch_result ==
ErrorCode::OK, in_isr);
856void IRAM_ATTR ESP32UART::OnTxTransferDone(
bool in_isr,
ErrorCode result)
862#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
863 if (dma_backend_enabled_)
867 tx_dma_buffer_.
Reset();
868 (void)TryStartTx(in_isr);
874 [[maybe_unused]]
const auto promote_pending_tx_to_active_result =
875 PromotePendingTxToActive();
876 DEV_ASSERT_FROM_CALLBACK(promote_pending_tx_to_active_result, in_isr);
877 [[maybe_unused]]
const auto start_and_report_active_result =
878 StartAndReportActive(in_isr);
879 REQUIRE_FROM_CALLBACK(start_and_report_active_result, in_isr);
881 else if (config_state_.load(std::memory_order_acquire) ==
882 static_cast<uint8_t
>(ConfigState::EMPTY) &&
883 LoadActiveTxFromQueue(in_isr))
885 [[maybe_unused]]
const auto start_and_report_active_result =
886 StartAndReportActive(in_isr);
887 REQUIRE_FROM_CALLBACK(start_and_report_active_result, in_isr);
891 config_state_.load(std::memory_order_acquire) ==
892 static_cast<uint8_t
>(ConfigState::EMPTY))
894 (void)LoadPendingTxFromQueue(in_isr);
905 if (LoadActiveTxFromQueue(in_isr))
907 (void)StartAndReportActive(in_isr);
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
ErrorCode SetLoopback(bool enable)
切换 UART 内部环回模式 / Toggle UART internal loopback.
static void WriteFun(WritePort &port, bool in_isr)
WritePort 使用的队列驱动 TX 入口 / Queue-driven TX entry used by WritePort.
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.
ErrorCode SetConfig(UART::Configuration config, bool in_isr=false) override
提交帧格式和波特率配置 / Submit framing and baud configuration.
void OnReadQueueSpaceAvailable(bool in_isr) override
通知后端处理接收空间 / Notify the backend of RX space.
bool TestAndSet() noexcept
测试并置位:置位并返回旧状态 / Test-and-set: set and return previous state
bool IsSet() const noexcept
判断是否已置位 / Check whether the flag is set
void Clear() noexcept
清除标志 / Clear the flag
void Publish()
结束本次写入并通知挂起读 / End production and notify a pending read.
ReadQueue GetReadQueue(bool in_isr=false)
获取后端接收入队接口 / Obtain the backend RX producer interface.
bool Invoke(uint32_t events, bool in_isr, Handler &&handler) noexcept
发布事件并尝试取得执行权 / Publish events and attempt to claim execution.
通用异步收发传输(UART)基类 / Abstract base class for Universal Asynchronous Receiver-Transmitter (UART)
Parity
奇偶校验模式 / Parity mode
@ NO_PARITY
无校验 / No parity
size_t AvailableSize() const
获取当前队头的剩余长度 / Get the current front remainder.
基于 SPSC 字节队列的写端口 / Write endpoint backed by an SPSC byte queue.
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.
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ INIT_ERR
初始化错误 / Initialization error
@ STATE_ERR
状态错误 / State error
@ NOT_SUPPORT
不支持 / Not supported
@ OK
操作成功 / Operation successful
@ ARG_ERR
参数错误 / Argument error
void(* WriteFun)(WritePort &port, bool in_isr)
写入进度通知函数类型 / Write progress notification function type.
OwnerType * ContainerOf(MemberType *ptr, MemberType OwnerType::*member) noexcept
通过成员指针恢复其所属对象指针 Recover the owning object pointer from a member pointer
UART 配置结构体 / UART configuration structure.
uint8_t stop_bits
停止位长度 / Number of stop bits
uint32_t baudrate
波特率 / Baud rate