libxr 1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
linux_uart.hpp
1#pragma once
2
3#define termios asmtermios
4#include <asm/termbits.h>
5#undef termios
6#include <fcntl.h>
7#include <linux/serial.h>
8#include <sys/ioctl.h>
9#include <sys/select.h>
10#include <termios.h>
11#include <unistd.h>
12
13#include <cstddef>
14#include <cstdint>
15#include <filesystem>
16
17#include "libxr_def.hpp"
18#include "libxr_rw.hpp"
19#include "semaphore.hpp"
20#include "uart.hpp"
21
22namespace LibXR
23{
24
25class LinuxUART : public UART
26{
27 public:
28 LinuxUART(const char *dev_path, unsigned int baudrate = 115200,
29 Parity parity = Parity::NO_PARITY, uint8_t data_bits = 8,
30 uint8_t stop_bits = 1, uint32_t rx_queue_size = 5, uint32_t tx_queue_size = 5,
31 size_t buffer_size = 512)
32 : UART(rx_queue_size, buffer_size, tx_queue_size, buffer_size),
33 write_sem_(0),
34 rx_buff_(new uint8_t[buffer_size]),
35 tx_buff_(new uint8_t[buffer_size]),
36 buff_size_(buffer_size)
37 {
38 if (std::filesystem::exists(dev_path) == false)
39 {
40 XR_LOG_ERROR("Cannot find UART device: %s", dev_path);
41 ASSERT(false);
42 }
43
44 device_path_ = GetByPathForTTY(dev_path);
45
46 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
47 if (fd_ < 0)
48 {
49 XR_LOG_ERROR("Cannot open UART device: %s", device_path_.c_str());
50 ASSERT(false);
51 }
52 else
53 {
54 XR_LOG_PASS("Open UART device: %s", device_path_.c_str());
55 }
56
57 config_ = {.baudrate = baudrate,
58 .parity = parity,
59 .data_bits = data_bits,
60 .stop_bits = stop_bits};
61
62 SetConfig(config_);
63
66
67 rx_thread_.Create<LinuxUART *>(
68 this, [](LinuxUART *self) { self->RxLoop(); }, "rx_uart", 8192,
70
71 tx_thread_.Create<LinuxUART *>(
72 this, [](LinuxUART *self) { self->TxLoop(); }, "tx_uart", 8192,
74 }
75
76 std::string GetByPathForTTY(const std::string &tty_name)
77 {
78 const std::string BASE = "/dev/serial/by-path";
79 if (strncmp(tty_name.c_str(), BASE.c_str(), BASE.length()) == 0 ||
80 !std::filesystem::exists(BASE))
81 {
82 return tty_name;
83 }
84 for (const auto &entry : std::filesystem::directory_iterator(BASE))
85 {
86 std::string full = std::filesystem::canonical(entry.path());
87 if (full == tty_name)
88 {
89 return entry.path(); // 返回符号链接路径
90 }
91 }
92 return ""; // 没找到
93 }
94
95 void SetLowLatency(int fd)
96 {
97 struct serial_struct serinfo;
98 ioctl(fd, TIOCGSERIAL, &serinfo);
99 serinfo.flags |= ASYNC_LOW_LATENCY;
100 ioctl(fd, TIOCSSERIAL, &serinfo);
101 }
102
103 ErrorCode SetConfig(UART::Configuration config) override
104 {
105 if (&config != &config_)
106 {
107 config_ = config;
108 }
109
110 struct termios2 tio{};
111 if (ioctl(fd_, TCGETS2, &tio) != 0)
112 {
113 return ErrorCode::INIT_ERR;
114 }
115
116 // 设置自定义波特率
117 tio.c_cflag &= ~CBAUD;
118 tio.c_cflag |= BOTHER;
119 tio.c_ispeed = config.baudrate;
120 tio.c_ospeed = config.baudrate;
121
122 // 输入模式:关闭软件流控、特殊字符处理
123 tio.c_iflag &= ~(IXON | IXOFF | IXANY | ISTRIP | IGNCR | INLCR | ICRNL
124#ifdef IUCLC
125 | IUCLC
126#endif
127 );
128
129 // 输出模式:关闭所有加工
130 tio.c_oflag &= ~(OPOST
131#ifdef ONLCR
132 | ONLCR
133#endif
134#ifdef OCRNL
135 | OCRNL
136#endif
137#ifdef ONOCR
138 | ONOCR
139#endif
140#ifdef ONLRET
141 | ONLRET
142#endif
143 );
144
145 // 本地模式:禁用行缓冲、回显、信号中断
146 tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
147
148 // 控制模式:设置数据位、校验、停止位、流控
149 tio.c_cflag &= ~CSIZE;
150 switch (config.data_bits)
151 {
152 case 5:
153 tio.c_cflag |= CS5;
154 break;
155 case 6:
156 tio.c_cflag |= CS6;
157 break;
158 case 7:
159 tio.c_cflag |= CS7;
160 break;
161 case 8:
162 tio.c_cflag |= CS8;
163 break;
164 default:
165 return ErrorCode::ARG_ERR;
166 }
167
168 // 停止位
169 tio.c_cflag &= ~CSTOPB;
170 if (config.stop_bits == 2)
171 {
172 tio.c_cflag |= CSTOPB;
173 }
174
175 // 奇偶校验
176 switch (config.parity)
177 {
179 tio.c_cflag &= ~PARENB;
180 break;
182 tio.c_cflag |= PARENB;
183 tio.c_cflag &= ~PARODD;
184 break;
186 tio.c_cflag |= PARENB;
187 tio.c_cflag |= PARODD;
188 break;
189 }
190
191 // 禁用硬件流控
192 tio.c_cflag &= ~CRTSCTS;
193
194 // 启用本地模式、读功能
195 tio.c_cflag |= (CLOCAL | CREAD);
196
197 // 控制字符配置:阻塞直到读到 1 字节
198 // for (int i = 0; i < NCCS; ++i) tio.c_cc[i] = 0;
199 tio.c_cc[VTIME] = 0;
200 tio.c_cc[VMIN] = 1;
201
202 if (ioctl(fd_, TCSETS2, &tio) != 0)
203 {
204 return ErrorCode::INIT_ERR;
205 }
206
207 SetLowLatency(fd_);
208
209 tcflush(fd_, TCIOFLUSH);
210
211 return ErrorCode::OK;
212 }
213
214 static ErrorCode ReadFun(ReadPort &port)
215 {
216 auto uart = CONTAINER_OF(&port, LinuxUART, read_port_);
217 Mutex::LockGuard guard(uart->read_mutex_);
218 port.ProcessPendingReads();
219 return ErrorCode::OK;
220 }
221
222 static ErrorCode WriteFun(WritePort &port)
223 {
224 auto uart = CONTAINER_OF(&port, LinuxUART, write_port_);
225 WritePort::WriteInfo info;
226 if (port.queue_info_->Peek(info) != ErrorCode::OK)
227 {
228 return ErrorCode::EMPTY;
229 }
230 port.UpdateStatus(info.op);
231 uart->write_sem_.Post();
232 return ErrorCode::OK;
233 }
234
235 private:
236 void RxLoop()
237 {
238 while (true)
239 {
240 if (!connected_)
241 {
242 close(fd_);
243 fd_ = open(device_path_.c_str(), O_RDWR | O_NOCTTY);
244
245 if (fd_ < 0)
246 {
247 XR_LOG_WARN("Cannot open UART device: %s", device_path_.c_str());
248 Thread::Sleep(1000);
249 }
250 else
251 {
252 SetConfig(config_);
253 XR_LOG_PASS("Reopen UART device: %s", device_path_.c_str());
254 connected_ = true;
255 }
256 }
257 auto n = read(fd_, rx_buff_, buff_size_);
258 if (n > 0)
259 {
260 read_port_.queue_data_->PushBatch(rx_buff_, n);
261 Mutex::LockGuard guard(read_mutex_);
263 }
264 else
265 {
266 XR_LOG_WARN("Cannot read UART device: %s", device_path_.c_str());
267 connected_ = false;
268 }
269 }
270 }
271
272 void TxLoop()
273 {
274 WritePort::WriteInfo info;
275 while (true)
276 {
277 if (!connected_)
278 {
279 Thread::Sleep(1);
280 continue;
281 }
282
283 write_sem_.Wait();
284 if (write_port_.queue_info_->Pop(info) == ErrorCode::OK)
285 {
286 if (write_port_.queue_data_->PopBatch(tx_buff_, info.size) == ErrorCode::OK)
287 {
288 auto written = write(fd_, tx_buff_, info.size);
289 if (written < 0)
290 {
291 XR_LOG_WARN("Cannot write UART device: %s", device_path_.c_str());
292 connected_ = false;
293 }
294 info.op.UpdateStatus(false, (written == static_cast<int>(info.size))
295 ? ErrorCode::OK
296 : ErrorCode::FAILED);
297 }
298 else
299 {
300 ASSERT(false);
301 info.op.UpdateStatus(false, ErrorCode::FAILED);
302 }
303 }
304 }
305 }
306
307 int fd_ = -1;
308 bool connected_ = true;
309 Configuration config_;
310 std::string device_path_;
311 Thread rx_thread_;
312 Thread tx_thread_;
313 uint8_t *rx_buff_ = nullptr;
314 uint8_t *tx_buff_ = nullptr;
315 size_t buff_size_ = 0;
316 Semaphore write_sem_;
317 Mutex read_mutex_;
318};
319
320} // namespace LibXR
ErrorCode PushBatch(const void *data, size_t size)
批量推入多个元素 (Push multiple elements into the queue).
Definition queue.hpp:175
ErrorCode SetConfig(UART::Configuration config) override
设置 UART 配置 / Sets the UART configuration
ErrorCode PopBatch(Data *data, size_t batch_size)
批量弹出数据 / Pops multiple elements from the queue
互斥锁的 RAII 机制封装 (RAII-style mechanism for automatic mutex management).
Definition mutex.hpp:95
ReadPort class for handling read operations.
Definition libxr_rw.hpp:309
void ProcessPendingReads()
Processes pending reads.
Definition libxr_rw.hpp:469
ErrorCode Wait(uint32_t timeout=UINT32_MAX)
等待(减少)信号量 Waits (decrements) the semaphore
Definition semaphore.cpp:15
@ REALTIME
实时优先级 Realtime priority
void Create(ArgType arg, void(*function)(ArgType arg), const char *name, size_t stack_depth, Thread::Priority priority)
创建新线程 Creates a new thread
Definition thread.hpp:64
static void Sleep(uint32_t milliseconds)
让线程进入休眠状态 Puts the thread to sleep
Definition thread.cpp:9
通用异步收发传输(UART)基类 / Abstract base class for Universal Asynchronous Receiver-Transmitter (UART)
Definition uart.hpp:19
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:52
ReadPort read_port_
读取端口 / Read port
Definition uart.hpp:51
LibXR Color Control Library / LibXR终端颜色控制库
ErrorCode(* ReadFun)(ReadPort &port)
Function pointer type for read operations.
Definition libxr_rw.hpp:279
ErrorCode(* WriteFun)(WritePort &port)
Function pointer type for write operations.
Definition libxr_rw.hpp:275
UART 配置结构体 / UART configuration structure.
Definition uart.hpp:44
uint8_t stop_bits
停止位长度 / Number of stop bits
Definition uart.hpp:48
Parity parity
校验模式 / Parity mode
Definition uart.hpp:46
uint8_t data_bits
数据位长度 / Number of data bits
Definition uart.hpp:47
uint32_t baudrate
波特率 / Baud rate
Definition uart.hpp:45