libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
cdc_uart.hpp
1#pragma once
2
3#include "cdc_base.hpp"
4#include "ep.hpp"
5#include "flag.hpp"
6#include "libxr_def.hpp"
7#include "libxr_rw.hpp"
8
9namespace LibXR
10{
16{
17 public:
24 explicit CDCUartTxOpDequeueHelper(WritePort& port) : port_(port) {}
25
30 void Reset()
31 {
32 head_valid_ = false;
33 offset_ = 0;
34 }
35
44 bool HasOp() { return head_valid_ || (port_.queue_info_->Size() > 0); }
45
56 ErrorCode Take(uint8_t* dst, std::size_t cap, std::size_t& out_len)
57 {
58 auto ec = EnsureHead();
59 if (ec != ErrorCode::OK)
60 {
61 out_len = 0;
62 return ec; // EMPTY / FAILED
63 }
64
65 const std::size_t REMAINING = Remaining();
66 if (REMAINING == 0)
67 {
68 out_len = 0;
69 return ErrorCode::FAILED;
70 }
71
72 const std::size_t TAKE = (REMAINING < cap) ? REMAINING : cap;
73
74 if (port_.queue_data_->PopBatch(dst, TAKE) != ErrorCode::OK)
75 {
76 out_len = 0;
77 return ErrorCode::FAILED;
78 }
79
80 offset_ += TAKE;
81 out_len = TAKE;
82
84 }
85
93 bool HeadCompleted() const { return head_valid_ && (offset_ == head_.data.size_); }
94
103 ErrorCode PopCompleted(WriteInfoBlock* completed_info = nullptr)
104 {
105 if (!HeadCompleted())
106 {
107 return ErrorCode::FAILED;
108 }
109
110 WriteInfoBlock popped{};
111 auto ans = port_.queue_info_->Pop(popped);
112 ASSERT(ans == ErrorCode::OK);
113
114 if (completed_info)
115 {
116 *completed_info = popped;
117 }
118
119 Reset();
120 return ErrorCode::OK;
121 }
122
132 ErrorCode DropHead(WriteInfoBlock* dropped_info = nullptr)
133 {
134 auto ec = EnsureHead();
135 if (ec != ErrorCode::OK)
136 {
137 return ec;
138 }
139
140 const std::size_t REMAINING = Remaining();
141 if (REMAINING > 0)
142 {
143 auto drop_ans = port_.queue_data_->PopBatch(nullptr, REMAINING);
144 if (drop_ans != ErrorCode::OK)
145 {
146 return drop_ans;
147 }
148 }
149
150 WriteInfoBlock popped{};
151 auto pop_ans = port_.queue_info_->Pop(popped);
152 ASSERT(pop_ans == ErrorCode::OK);
153 if (pop_ans != ErrorCode::OK)
154 {
155 return pop_ans;
156 }
157
158 if (dropped_info)
159 {
160 *dropped_info = popped;
161 }
162
163 Reset();
164 return ErrorCode::OK;
165 }
166
167 private:
176 {
177 if (head_valid_)
178 {
179 return ErrorCode::OK;
180 }
181
182 WriteInfoBlock info{};
183 if (port_.queue_info_->Peek(info) != ErrorCode::OK)
184 {
185 return ErrorCode::EMPTY;
186 }
187
188 head_ = info;
189 head_valid_ = true;
190 offset_ = 0;
191 return ErrorCode::OK;
192 }
193
200 std::size_t Remaining() const
201 {
202 ASSERT(head_valid_);
203 ASSERT(head_.data.size_ >= offset_);
204 return head_.data.size_ - offset_;
205 }
206
207 private:
209 bool head_valid_ = false;
211 std::size_t offset_ = 0;
212};
213
214} // namespace LibXR
215
216namespace LibXR::USB
217{
218
219class CDCUart;
220
226{
227 public:
235 explicit CDCUartReadPort(uint32_t size, CDCUart& owner) : ReadPort(size), owner_(owner)
236 {
237 }
238
245 void OnRxDequeue(bool in_isr) override;
246
247 CDCUartReadPort& operator=(ReadFun fun)
248 {
250 return *this;
251 }
252
254
256 false;
258 nullptr,
259 0};
260};
261
266class CDCUart : public CDCBase, public LibXR::UART
267{
268 public:
269 using LibXR::UART::Read;
271 using LibXR::UART::Write;
273
286 size_t rx_buffer_size = 128, size_t tx_buffer_size = 128, size_t tx_queue_size = 5,
290 const char* control_interface_string = CDCBase::DEFAULT_CONTROL_INTERFACE_STRING,
291 const char* data_interface_string = CDCBase::DEFAULT_DATA_INTERFACE_STRING)
292 : CDCBase(data_in_ep_num, data_out_ep_num, comm_ep_num, control_interface_string,
293 data_interface_string),
295 read_port_cdc_(rx_buffer_size, *this),
296 write_port_cdc_(tx_queue_size, tx_buffer_size),
298 {
299 read_port_cdc_ = ReadFun; // NOLINT
300 write_port_cdc_ = WriteFun; // NOLINT
301 }
302
311 {
312 auto& line_coding = GetLineCoding();
313
314 switch (cfg.stop_bits)
315 {
316 case 1:
317 line_coding.bCharFormat = 0;
318 break;
319 case 2:
320 line_coding.bCharFormat = 2;
321 break;
322 default:
323 return ErrorCode::ARG_ERR;
324 }
325
326 switch (cfg.parity)
327 {
329 line_coding.bParityType = 0;
330 break;
332 line_coding.bParityType = 1;
333 break;
335 line_coding.bParityType = 2;
336 break;
337 default:
338 return ErrorCode::ARG_ERR;
339 }
340
341 switch (cfg.data_bits)
342 {
343 case 5:
344 case 6:
345 case 7:
346 case 8:
347 case 16:
348 line_coding.bDataBits = static_cast<uint8_t>(cfg.data_bits);
349 break;
350 default:
351 return ErrorCode::ARG_ERR;
352 }
353
354 line_coding.dwDTERate = cfg.baudrate;
356 return ErrorCode::OK;
357 }
358
368 bool TryRearmOut(bool in_isr)
369 {
370 auto ep_data_out = GetDataOutEndpoint();
371 if (ep_data_out == nullptr)
372 {
373 return false;
374 }
375
376 const std::size_t MPS = ep_data_out->MaxPacketSize();
377 if (MPS == 0 || read_port_cdc_.queue_data_ == nullptr)
378 {
379 return false;
380 }
381
383 {
385 {
386 auto push_ans = read_port_cdc_.queue_data_->PushBatch(
387 reinterpret_cast<const uint8_t*>(read_port_cdc_.pending_data_.addr_),
389 if (push_ans == ErrorCode::OK)
390 {
392 }
393 else
394 {
395 return false;
396 }
397 }
398 else
399 {
400 return false;
401 }
402 }
403
404 if (ep_data_out->GetState() == Endpoint::State::BUSY)
405 {
406 return false;
407 }
408
409 auto ans = ep_data_out->Transfer(MPS);
410 if (ans == ErrorCode::OK)
411 {
413 return true;
414 }
415
416 return false;
417 }
418
419 protected:
427 void UnbindEndpoints(EndpointPool& endpoint_pool, bool in_isr) override
428 {
429 CDCBase::UnbindEndpoints(endpoint_pool, in_isr);
430 tx_deq_.Reset();
433
434 need_write_zlp_ = false;
435
437 read_port_cdc_.pending_data_ = {nullptr, 0};
438 }
439
457 static ErrorCode WriteFun(WritePort& port, bool in_isr)
458 {
459 UNUSED(in_isr);
460
461 auto* cdc = LibXR::ContainerOf(&port, &CDCUart::write_port_cdc_);
462
468 if (cdc->in_write_isr_.IsSet())
469 {
470 return ErrorCode::PENDING;
471 }
472
473 auto ep = cdc->GetDataInEndpoint();
474 if (ep == nullptr)
475 {
476 auto drop_ans = cdc->tx_deq_.DropHead(nullptr);
477 if (drop_ans != ErrorCode::OK)
478 {
479 return drop_ans;
480 }
481 return ErrorCode::FAILED;
482 }
483
484 if (!cdc->Inited())
485 {
486 auto drop_ans = cdc->tx_deq_.DropHead(nullptr);
487 if (drop_ans != ErrorCode::OK)
488 {
489 return drop_ans;
490 }
491
492 return ErrorCode::INIT_ERR; // 非 PENDING -> 同步上报失败 / Non-PENDING reports
493 // synchronous failure upstream
494 }
495
501 if (ep->GetActiveLength() != 0)
502 {
503 return ErrorCode::PENDING;
504 }
505
511 if (cdc->tx_deq_.HasOp())
512 {
513 cdc->need_write_zlp_ = false;
514 }
515
521
522 // 预写第一段 / Prefill first segment
523 {
524 auto buffer = ep->GetBuffer();
525 std::size_t len = 0;
526
527 slot_ec =
528 cdc->tx_deq_.Take(reinterpret_cast<uint8_t*>(buffer.addr_), buffer.size_, len);
529 if (slot_ec == ErrorCode::EMPTY || len == 0)
530 {
531 return ErrorCode::PENDING;
532 }
533 if (slot_ec != ErrorCode::OK && slot_ec != ErrorCode::PENDING)
534 {
535 return slot_ec;
536 }
537
538 ep->SetActiveLength(len);
539 }
540
541 std::atomic_signal_fence(std::memory_order_seq_cst);
542
543 // 循环:可立即发送则发送;发送后预写下一段并继续检查 / Loop: send if possible; then
544 // prefill next segment
545 while (true)
546 {
547 const std::size_t TO_SEND = ep->GetActiveLength();
548
558 if (ep->GetState() != Endpoint::State::IDLE || TO_SEND == 0 ||
559 !cdc->tx_deq_.HasOp())
560 {
561 return ErrorCode::PENDING;
562 }
563
564 std::atomic_signal_fence(std::memory_order_seq_cst);
565
566 // 启动一次 Transfer / Kick one Transfer
567 ep->SetActiveLength(0);
568 auto ans = ep->Transfer(TO_SEND);
569 ASSERT(ans == ErrorCode::OK);
570
577 if (slot_ec == ErrorCode::OK && cdc->tx_deq_.HeadCompleted())
578 {
579 auto pop_ok = cdc->tx_deq_.PopCompleted(nullptr);
580 ASSERT(pop_ok == ErrorCode::OK);
581
582 // ZLP 判定。
583 // ZLP decision.
584 const std::size_t MPS = ep->MaxPacketSize();
585 if (MPS > 0 && (TO_SEND % MPS) == 0 && ep->GetActiveLength() == 0 &&
586 !cdc->tx_deq_.HasOp())
587 {
588 cdc->need_write_zlp_ = true;
589 }
590
591 return ErrorCode::OK; // 非 PENDING -> 上层完成一次 / Non-PENDING triggers one upstream finish
592 }
593
594 // 预写下一段。
595 // Prefill the next segment.
596 if (!cdc->tx_deq_.HasOp())
597 {
598 return ErrorCode::PENDING;
599 }
600
601 auto buffer = ep->GetBuffer();
602 std::size_t len2 = 0;
603
604 slot_ec =
605 cdc->tx_deq_.Take(reinterpret_cast<uint8_t*>(buffer.addr_), buffer.size_, len2);
606 if (slot_ec == ErrorCode::EMPTY || len2 == 0)
607 {
608 return ErrorCode::PENDING;
609 }
610 if (slot_ec != ErrorCode::OK && slot_ec != ErrorCode::PENDING)
611 {
612 return slot_ec;
613 }
614
615 ep->SetActiveLength(len2);
616 // 下一轮继续检查是否可立即发送。
617 // The next iteration checks whether sending can continue immediately.
618 }
619 }
620
621 static ErrorCode ReadFun(ReadPort&, bool) { return ErrorCode::PENDING; }
622
630 void OnDataOutComplete(bool in_isr, ConstRawData& data) override
631 {
632 if (data.size_ > 0)
633 {
634 auto push_ans = read_port_cdc_.queue_data_->PushBatch(
635 reinterpret_cast<const uint8_t*>(data.addr_), data.size_);
636 if (push_ans == ErrorCode::OK)
637 {
639 }
640 else
641 {
644 return;
645 }
646 }
647
648 (void)TryRearmOut(in_isr);
649 }
650
658 void OnDataInComplete(bool in_isr, ConstRawData& data) override
659 {
660 UNUSED(data);
661
663
664 auto ep = GetDataInEndpoint();
665 if (ep == nullptr)
666 {
667 return;
668 }
669
670 if (!Inited())
671 {
672 tx_deq_.Reset();
674 return;
675 }
676
677 // ZLP:仅在此刻跨-op 无数据时发送。
678 // Send the ZLP only when there is no data left across ops at this moment.
679 if (need_write_zlp_)
680 {
681 if (ep->GetActiveLength() == 0 && !tx_deq_.HasOp())
682 {
683 auto z = ep->TransferZLP();
684 ASSERT(z == ErrorCode::OK);
685 need_write_zlp_ = false;
686 return;
687 }
688 need_write_zlp_ = false;
689 }
690
691 // ActiveLength==0 时不读取队列。
692 // Do not read queues when ActiveLength==0.
693 const std::size_t PENDING_LEN = ep->GetActiveLength();
694 if (PENDING_LEN == 0)
695 {
696 return;
697 }
698
699 // 1) 续发:本 ISR 仅启动一次 Transfer。
700 // 1) Continue: kick only one Transfer in this ISR.
701 ep->SetActiveLength(0);
702 auto ans = ep->Transfer(PENDING_LEN);
703 ASSERT(ans == ErrorCode::OK);
704
705 // 2) 若为 head op 最后一段:启动后 pop + Finish 一次。
706 // 2) If this is the last segment of the head op, pop + Finish once after kick-off.
708 {
709 WriteInfoBlock completed{};
710 auto pop_ok = tx_deq_.PopCompleted(&completed);
711 ASSERT(pop_ok == ErrorCode::OK);
712 write_port_cdc_.Finish(in_isr, ErrorCode::OK, completed);
713 }
714
715 // 3) 预写:仅在已启动 Transfer 后允许读取队列。
716 // 3) Prefill: queue reads are allowed only after a Transfer has been kicked.
717 bool primed = false;
718 if (tx_deq_.HasOp())
719 {
720 auto buffer = ep->GetBuffer();
721 std::size_t len2 = 0;
722
723 auto ec2 =
724 tx_deq_.Take(reinterpret_cast<uint8_t*>(buffer.addr_), buffer.size_, len2);
725 if ((ec2 == ErrorCode::OK || ec2 == ErrorCode::PENDING) && len2 > 0)
726 {
727 ep->SetActiveLength(len2);
728 primed = true;
729 }
730 }
731
732 // 4) ZLP 判定 / ZLP decision
733 const std::size_t MPS = ep->MaxPacketSize();
734 if (!primed && PENDING_LEN > 0 && MPS > 0 && (PENDING_LEN % MPS) == 0 &&
735 ep->GetActiveLength() == 0 && !tx_deq_.HasOp())
736 {
737 need_write_zlp_ = true;
738 }
739 }
740
741 private:
744
746
748
749 bool need_write_zlp_{false};
750};
751
752inline void CDCUartReadPort::OnRxDequeue(bool in_isr)
753{
754 if (!recv_pause_)
755 {
756 return;
757 }
758
759 // pending_data_ 回填 / Push pending_data_ back into queue
760 if (pending_data_.size_ > 0)
761 {
763 {
764 auto ans = queue_data_->PushBatch(
765 reinterpret_cast<const uint8_t*>(pending_data_.addr_), pending_data_.size_);
766 if (ans == ErrorCode::OK)
767 {
768 pending_data_ = {nullptr, 0};
769 ProcessPendingReads(in_isr);
770 }
771 else
772 {
773 return;
774 }
775 }
776 else
777 {
778 return;
779 }
780 }
781
782 // 尝试恢复 rearm / Try to rearm OUT
783 (void)owner_.TryRearmOut(in_isr);
784}
785
786} // namespace LibXR::USB
WritePort(info 队列 + data 队列)的“单 op 不跨界”出队辅助器 Dequeue helper for WritePort (info + data) without cross...
Definition cdc_uart.hpp:16
WritePort & port_
写端口引用 / Write port reference
Definition cdc_uart.hpp:208
bool HeadCompleted() const
head op 是否已全部出队 Whether the cached head op is fully dequeued
Definition cdc_uart.hpp:93
CDCUartTxOpDequeueHelper(WritePort &port)
构造函数 Constructor
Definition cdc_uart.hpp:24
bool head_valid_
head 缓存有效标志 / Cached head valid flag
Definition cdc_uart.hpp:209
void Reset()
重置内部状态(head 缓存与偏移) Reset internal state (cached head and offset)
Definition cdc_uart.hpp:30
ErrorCode EnsureHead()
确保 head 缓存可用(必要时 Peek info) Ensure cached head is valid (Peek info if needed)
Definition cdc_uart.hpp:175
ErrorCode DropHead(WriteInfoBlock *dropped_info=nullptr)
丢弃当前 head op 的剩余数据并 pop info Drop the current head op's remaining bytes and pop its info.
Definition cdc_uart.hpp:132
WriteInfoBlock head_
缓存的 head info / Cached head info
Definition cdc_uart.hpp:210
ErrorCode PopCompleted(WriteInfoBlock *completed_info=nullptr)
在 head 完成后 pop info 并重置状态 Pop info after head completes and reset state
Definition cdc_uart.hpp:103
bool HasOp()
是否存在可处理的 op Whether any op exists
Definition cdc_uart.hpp:44
std::size_t offset_
当前 op 已出队偏移 / Dequeued offset within current op
Definition cdc_uart.hpp:211
std::size_t Remaining() const
当前 op 剩余未出队字节数 Remaining bytes of current op
Definition cdc_uart.hpp:200
ErrorCode Take(uint8_t *dst, std::size_t cap, std::size_t &out_len)
从 data 队列搬运数据到目标 buffer,并推进 offset(不 pop info) Dequeue bytes from data queue into destination buffer ...
Definition cdc_uart.hpp:56
只读原始数据视图 / Immutable raw data view
size_t size_
数据字节数 / Data size in bytes
const void * addr_
数据起始地址 / Data start address
普通标志位(非原子)/ Non-atomic flag
Definition flag.hpp:115
作用域标志管理器:构造时写入指定值,析构时恢复原值 / Scoped flag restorer: set on entry, restore on exit
Definition flag.hpp:199
ErrorCode PushBatch(const Data *data, size_t size)
批量推入数据 / Pushes multiple elements into the queue
size_t EmptySize()
计算队列剩余可用空间 / Calculates the remaining available space in the queue
ErrorCode PopBatch(Data *data, size_t size)
批量弹出数据 / Pops multiple elements from the queue
ReadPort class for handling read operations.
Definition read_port.hpp:18
LockFreeQueue< uint8_t > * queue_data_
RX payload queue. 接收数据字节队列。
Definition read_port.hpp:54
void ProcessPendingReads(bool in_isr)
Processes pending reads.
ReadPort & operator=(ReadFun fun)
赋值运算符重载,用于设置读取函数。 Overloaded assignment operator to set the read function.
Definition read_port.cpp:28
void FailAndClearAll(ErrorCode reason, bool in_isr)
失败完成并清空当前所有挂起读操作。
通用异步收发传输(UART)基类 / Abstract base class for Universal Asynchronous Receiver-Transmitter (UART)
Definition uart.hpp:19
ReadPort * read_port_
读取端口 / Read port
Definition uart.hpp:53
@ NO_PARITY
无校验 / No parity
@ ODD
奇校验 / Odd parity
@ EVEN
偶校验 / Even parity
WritePort * write_port_
写入端口 / Write port
Definition uart.hpp:54
USB CDC ACM (Abstract Control Model) 设备类实现 USB CDC ACM (Abstract Control Model) device class implemen...
Definition cdc_base.hpp:24
ErrorCode SendSerialState()
发送串行状态通知 Send serial state notification
Definition cdc_base.hpp:188
virtual void UnbindEndpoints(EndpointPool &endpoint_pool, bool) override
反初始化CDC设备 Deinitialize CDC device
Definition cdc_base.hpp:395
USB CDC-ACM UART 适配器 USB CDC-ACM UART adapter.
Definition cdc_uart.hpp:267
CDCUartReadPort read_port_cdc_
CDC RX 读端口 / CDC RX read port.
Definition cdc_uart.hpp:742
CDCUart(size_t rx_buffer_size=128, size_t tx_buffer_size=128, size_t tx_queue_size=5, Endpoint::EPNumber data_in_ep_num=Endpoint::EPNumber::EP_AUTO, Endpoint::EPNumber data_out_ep_num=Endpoint::EPNumber::EP_AUTO, Endpoint::EPNumber comm_ep_num=Endpoint::EPNumber::EP_AUTO, const char *control_interface_string=CDCBase::DEFAULT_CONTROL_INTERFACE_STRING, const char *data_interface_string=CDCBase::DEFAULT_DATA_INTERFACE_STRING)
构造函数 Constructor
Definition cdc_uart.hpp:285
bool TryRearmOut(bool in_isr)
尝试 rearm OUT(背压恢复/持续接收) Try to rearm OUT endpoint (backpressure recovery / continuous RX)
Definition cdc_uart.hpp:368
void UnbindEndpoints(EndpointPool &endpoint_pool, bool in_isr) override
解绑端点(清理队列、状态与背压标志) Unbind endpoints (cleanup queues, states, and backpressure flags)
Definition cdc_uart.hpp:427
static ErrorCode WriteFun(WritePort &port, bool in_isr)
写端口回调(TX) Write port callback (TX)
Definition cdc_uart.hpp:457
void OnDataInComplete(bool in_isr, ConstRawData &data) override
IN 完成回调(TX) IN complete callback (TX)
Definition cdc_uart.hpp:658
LibXR::CDCUartTxOpDequeueHelper tx_deq_
TX 出队辅助器 / TX dequeue helper.
Definition cdc_uart.hpp:745
void OnDataOutComplete(bool in_isr, ConstRawData &data) override
OUT 完成回调(RX) OUT complete callback (RX)
Definition cdc_uart.hpp:630
bool need_write_zlp_
ZLP 需求标志 / ZLP required flag.
Definition cdc_uart.hpp:749
LibXR::WritePort write_port_cdc_
CDC TX 写端口 / CDC TX write port.
Definition cdc_uart.hpp:743
ErrorCode SetConfig(UART::Configuration cfg) override
设置 UART 配置(CDC Line Coding) Set UART configuration (CDC Line Coding)
Definition cdc_uart.hpp:310
Flag::Plain in_write_isr_
写 ISR 保护标志 / Write ISR guard flag
Definition cdc_uart.hpp:747
CDC UART 读端口(背压 + pending 缓存) CDC UART read port (backpressure + pending cache)
Definition cdc_uart.hpp:226
CDCUartReadPort(uint32_t size, CDCUart &owner)
构造函数 Constructor
Definition cdc_uart.hpp:235
ConstRawData pending_data_
pending 数据(指向底层 USB buffer)/ Pending data pointing to USB buffer
Definition cdc_uart.hpp:257
CDCUart & owner_
所属 CDCUart / Owning CDCUart
Definition cdc_uart.hpp:253
bool recv_pause_
背压标志:true 表示 OUT 未 rearm / Backpressure flag: OUT not rearmed
Definition cdc_uart.hpp:255
void OnRxDequeue(bool in_isr) override
数据队列被消费时回调(解除背压并尝试恢复 OUT rearm) Called when RX queue is dequeued (lift backpressure and try to rearm ...
Definition cdc_uart.hpp:752
EPNumber
端点号 Endpoint number
Definition ep.hpp:42
@ EP_AUTO
自动分配端点号 / Auto allocate
USB端点池类 / USB endpoint pool class.
Definition ep_pool.hpp:23
WritePort class for handling write operations.
void FailAndClearAll(ErrorCode reason, bool in_isr)
失败完成并清空当前所有挂起写操作。
void Finish(bool in_isr, ErrorCode ans, WriteInfoBlock &info)
更新写入操作的状态。 Updates the status of the write operation.
LockFreeQueue< uint8_t > * queue_data_
Payload queue for pending write bytes. 挂起写入字节的数据队列。
LockFreeQueue< WriteInfoBlock > * queue_info_
Metadata queue for pending write batches. 挂起写批次的元数据队列。
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举
@ INIT_ERR
初始化错误 | Initialization error
@ FAILED
操作失败 | Operation failed
@ EMPTY
为空 | Empty
@ 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. 数据缓冲区。