|
|
@@ -0,0 +1,302 @@
|
|
|
+/**
|
|
|
+ * @file uart.c
|
|
|
+ * @brief UART driver source file.
|
|
|
+ *
|
|
|
+ * @details This file implements UART configuration, open/close,
|
|
|
+ * read/write functions using POSIX termios API.
|
|
|
+ *
|
|
|
+ * @author jlgwch
|
|
|
+ * @date 2025-06-06
|
|
|
+ * @version 1.0
|
|
|
+ *
|
|
|
+ * @history
|
|
|
+ * - 2025-06-06: jlgwch - Created the file.
|
|
|
+ *
|
|
|
+ * @copyright Copyright (c) 2025~ jlgwch. All rights reserved.
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+#include "uart.h"
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <string.h>
|
|
|
+#include <fcntl.h>
|
|
|
+#include <unistd.h>
|
|
|
+
|
|
|
+speed_t baudrate(int baudrate)
|
|
|
+{
|
|
|
+ switch (baudrate)
|
|
|
+ {
|
|
|
+ case 0: return B0;
|
|
|
+ case 50: return B50;
|
|
|
+ case 75: return B75;
|
|
|
+ case 110: return B110;
|
|
|
+ case 134: return B134;
|
|
|
+ case 150: return B150;
|
|
|
+ case 200: return B200;
|
|
|
+ case 300: return B300;
|
|
|
+ case 600: return B600;
|
|
|
+ case 1200: return B1200;
|
|
|
+ case 1800: return B1800;
|
|
|
+ case 2400: return B2400;
|
|
|
+ case 4800: return B4800;
|
|
|
+ case 9600: return B9600;
|
|
|
+ case 19200: return B19200;
|
|
|
+ case 38400: return B38400;
|
|
|
+ case 57600: return B57600;
|
|
|
+ case 115200: return B115200;
|
|
|
+ case 230400: return B230400;
|
|
|
+ case 460800: return B460800;
|
|
|
+ case 500000: return B500000;
|
|
|
+ case 576000: return B576000;
|
|
|
+ case 921600: return B921600;
|
|
|
+ case 1000000: return B1000000;
|
|
|
+ case 1152000: return B1152000;
|
|
|
+ case 1500000: return B1500000;
|
|
|
+ case 2000000: return B2000000;
|
|
|
+ case 2500000: return B2500000;
|
|
|
+ case 3000000: return B3000000;
|
|
|
+ case 3500000: return B3500000;
|
|
|
+ case 4000000: return B4000000;
|
|
|
+ default: return 0; // 不支持
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int uart_handle_set_device(uart_handle_st* handle, const char* device)
|
|
|
+{
|
|
|
+ if(!handle || !device)
|
|
|
+ return -1;
|
|
|
+ handle->config.device = device;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int uart_handle_set_baudrate(uart_handle_st* handle, int baudrate)
|
|
|
+{
|
|
|
+ if(!handle)
|
|
|
+ return -1;
|
|
|
+ handle->config.baudrate = baudrate;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int uart_handle_set_databits(uart_handle_st* handle, int databits)
|
|
|
+{
|
|
|
+ if(!handle)
|
|
|
+ return -1;
|
|
|
+ if(databits < 5 || databits > 8)
|
|
|
+ return -1;
|
|
|
+ handle->config.databits = databits;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int uart_handle_set_stopbits(uart_handle_st* handle, int stopbits)
|
|
|
+{
|
|
|
+ if(!handle)
|
|
|
+ return -1;
|
|
|
+ if(stopbits != 1 && stopbits != 2)
|
|
|
+ return -1;
|
|
|
+ handle->config.stopbits = stopbits;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int uart_handle_set_flow_control(uart_handle_st* handle, flow_control_t flow_control)
|
|
|
+{
|
|
|
+ if(!handle)
|
|
|
+ return -1;
|
|
|
+ handle->config.flow_control = flow_control;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int uart_handle_set_parity(uart_handle_st* handle, parity_t parity)
|
|
|
+{
|
|
|
+ if(!handle)
|
|
|
+ return -1;
|
|
|
+ handle->config.parity = parity;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int uart_handle_set_block(uart_handle_st* handle, int block)
|
|
|
+{
|
|
|
+ if(!handle)
|
|
|
+ return -1;
|
|
|
+ handle->config.block = (block != 0);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int uart_handle_open(uart_handle_st* handle)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ int flags;
|
|
|
+ int fd;
|
|
|
+ struct termios options;
|
|
|
+
|
|
|
+ if(!handle || !handle->config.device)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ flags = O_RDWR | O_NOCTTY;
|
|
|
+ if(!handle->config.block)
|
|
|
+ flags |= O_NONBLOCK;
|
|
|
+
|
|
|
+ fd = open(handle->config.device, flags);
|
|
|
+ if(fd < 0)
|
|
|
+ {
|
|
|
+ perror("open");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前串口配置
|
|
|
+ ret = tcgetattr(fd, &options);
|
|
|
+ if (ret != 0)
|
|
|
+ {
|
|
|
+ perror("tcgetattr");
|
|
|
+ close(fd);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置波特率
|
|
|
+ speed_t speed = baudrate(handle->config.baudrate);
|
|
|
+ if(speed == 0)
|
|
|
+ {
|
|
|
+ fprintf(stderr, "Unsupported baudrate %d\n", handle->config.baudrate);
|
|
|
+ close(fd);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ cfsetispeed(&options, speed);
|
|
|
+ cfsetospeed(&options, speed);
|
|
|
+
|
|
|
+ // 设置数据位
|
|
|
+ options.c_cflag &= ~CSIZE; // 清除数据位掩码 CSIZE
|
|
|
+ switch (handle->config.databits)
|
|
|
+ {
|
|
|
+ case 5:
|
|
|
+ options.c_cflag |= CS5;
|
|
|
+ break;
|
|
|
+ case 6:
|
|
|
+ options.c_cflag |= CS6;
|
|
|
+ break;
|
|
|
+ case 7:
|
|
|
+ options.c_cflag |= CS7;
|
|
|
+ break;
|
|
|
+ case 8:
|
|
|
+ options.c_cflag |= CS8;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ fprintf(stderr, "Invalid databits %d\n", handle->config.databits);
|
|
|
+ close(fd);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置停止位
|
|
|
+ if (handle->config.stopbits == 2)
|
|
|
+ options.c_cflag |= CSTOPB;
|
|
|
+ else
|
|
|
+ options.c_cflag &= ~CSTOPB;
|
|
|
+
|
|
|
+ // 设置校验位
|
|
|
+ options.c_cflag &= ~(PARENB | PARODD);
|
|
|
+ options.c_iflag &= ~(INPCK | ISTRIP);
|
|
|
+ switch (handle->config.parity)
|
|
|
+ {
|
|
|
+ case PARITY_NONE: // 无校验
|
|
|
+ // 已清除
|
|
|
+ break;
|
|
|
+ case PARITY_EVEN: // 偶校验
|
|
|
+ options.c_cflag |= PARENB;
|
|
|
+ options.c_iflag |= INPCK;
|
|
|
+ options.c_cflag &= ~PARODD;
|
|
|
+ break;
|
|
|
+ case PARITY_ODD: // 奇校验
|
|
|
+ options.c_cflag |= PARENB;
|
|
|
+ options.c_cflag |= PARODD;
|
|
|
+ options.c_iflag |= INPCK;
|
|
|
+ break;
|
|
|
+ case PARITY_MARK:
|
|
|
+ case PARITY_SPACE:
|
|
|
+ fprintf(stderr, "MARK/SPACE parity is not supported by termios\n");
|
|
|
+ // 需要额外实现
|
|
|
+ return -1;
|
|
|
+ default:
|
|
|
+ fprintf(stderr, "Invalid parity %c\n", handle->config.parity);
|
|
|
+ close(fd);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置流控
|
|
|
+ switch(handle->config.flow_control)
|
|
|
+ {
|
|
|
+ case FLOW_CONTROL_NONE: // 无流控
|
|
|
+ options.c_cflag &= ~CRTSCTS; // 关闭硬件流控
|
|
|
+ options.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控 (输入)
|
|
|
+ options.c_oflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控 (输出)
|
|
|
+ break;
|
|
|
+ case FLOW_CONTROL_SOFTWARE: // 软件流控
|
|
|
+ options.c_cflag &= ~CRTSCTS; // 关闭硬件流控
|
|
|
+ options.c_iflag |= (IXON | IXOFF); // 启用软件流控 (输入)
|
|
|
+ options.c_oflag |= (IXON | IXOFF); // 启用软件流控 (输出)
|
|
|
+ break;
|
|
|
+ case FLOW_CONTROL_HARDWARE: // 硬件流控
|
|
|
+ options.c_cflag |= CRTSCTS; // 启用RTS/CTS硬件流控
|
|
|
+ options.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控 (输入)
|
|
|
+ options.c_oflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控 (输出)
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ fprintf(stderr, "Unsupported flow control type\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 其他配置
|
|
|
+ // CLOCAL: 忽略调制解调器状态线, 保证程序不被终端控制。
|
|
|
+ // CREAD: 开启接收使能。
|
|
|
+ options.c_cflag |= (CLOCAL | CREAD);
|
|
|
+
|
|
|
+ if (handle->config.block)
|
|
|
+ {
|
|
|
+ options.c_cc[VMIN] = 1; // 阻塞模式,最少读1字节
|
|
|
+ options.c_cc[VTIME] = 0;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ options.c_cc[VMIN] = 0; // 非阻塞读
|
|
|
+ options.c_cc[VTIME] = 1; // 读等待0.1秒
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关闭特殊字符处理和输出处理
|
|
|
+ // ISIG 禁用信号 (如 Ctrl+C 发送 SIGINT、Ctrl+Z 发送 SIGTSTP)。
|
|
|
+ options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
|
|
+ options.c_lflag &= ~(ICANON | ECHO | ECHOE);
|
|
|
+ options.c_oflag &= ~OPOST;
|
|
|
+
|
|
|
+ // 应用配置
|
|
|
+ if (tcsetattr(fd, TCSANOW, &options) != 0)
|
|
|
+ {
|
|
|
+ perror("tcsetattr");
|
|
|
+ close(fd);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存句柄和配置
|
|
|
+ handle->fd = fd;
|
|
|
+ handle->config.options = options;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void uart_handle_close(uart_handle_st* handle)
|
|
|
+{
|
|
|
+ if(handle && handle->fd >= 0)
|
|
|
+ close(handle->fd);
|
|
|
+}
|
|
|
+
|
|
|
+int uart_write(uart_handle_st* handle, char* data, int size)
|
|
|
+{
|
|
|
+ if(handle && handle->fd >= 0)
|
|
|
+ return write(handle->fd, data, size);
|
|
|
+ else
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+int uart_read(uart_handle_st* handle, char* data, int size)
|
|
|
+{
|
|
|
+ if(handle && handle->fd >= 0)
|
|
|
+ return read(handle->fd, data, size);
|
|
|
+ else
|
|
|
+ return -1;
|
|
|
+}
|