下面我将分享一个在 Linux 环境下开发串口通信库的完整案例。这个库提供了基本的串口配置、读写和控制功能,适合嵌入式系统使用。
库设计思路
这个串口库采用面向对象的设计思想,将串口抽象为一个对象,提供初始化、配置、读写等接口。主要包含以下模块:
- 串口初始化与配置
- 数据读写操作
- 错误处理与状态检查
- 辅助功能 (如超时设置)
完整代码实现
下面是完整的串口库实现代码:
/* uart.h - 串口通信库头文件 */
#ifndef __UART_H__
#define __UART_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* 波特率定义 */
typedef enum {
BAUD_9600 = B9600,
BAUD_115200 = B115200,
BAUD_460800 = B460800,
BAUD_921600 = B921600
} UartBaudrate;
/* 数据位定义 */
typedef enum {
DATA_BITS_5 = CS5,
DATA_BITS_6 = CS6,
DATA_BITS_7 = CS7,
DATA_BITS_8 = CS8
} UartDataBits;
/* 停止位定义 */
typedef enum {
STOP_BITS_1 = 0,
STOP_BITS_2 = CSTOPB
} UartStopBits;
/* 校验位定义 */
typedef enum {
PARITY_NONE = 0,
PARITY_ODD = PARENB | PARODD,
PARITY_EVEN = PARENB
} UartParity;
/* 流控制定义 */
typedef enum {
FLOW_CONTROL_NONE = 0,
FLOW_CONTROL_HARDWARE = CRTSCTS,
FLOW_CONTROL_SOFTWARE = IXON | IXOFF
} UartFlowControl;
/* 串口设备结构 */
typedef struct {
int fd; /* 文件描述符 */
char device_path[64]; /* 设备路径 */
struct termios old_config; /* 原始配置 */
uint8_t is_open; /* 打开状态 */
} UartDevice;
/* API函数声明 */
UartDevice* uart_open(const char* device_path, UartBaudrate baudrate);
int uart_close(UartDevice* device);
int uart_config(UartDevice* device, UartDataBits data_bits,
UartStopBits stop_bits, UartParity parity,
UartFlowControl flow_control);
int uart_set_timeout(UartDevice* device, int timeout_ms);
int uart_write(UartDevice* device, const uint8_t* data, size_t length);
int uart_read(UartDevice* device, uint8_t* buffer, size_t max_length, int timeout_ms);
int uart_flush(UartDevice* device);
int uart_get_error(UartDevice* device);
const char* uart_strerror(int error_code);
#ifdef __cplusplus
}
#endif
#endif /* __UART_H__ */
/* uart.c - 串口通信库实现文件 */
#include "uart.h"
/* 错误码定义 */
typedef enum {
UART_SUCCESS = 0,
UART_ERROR_OPEN = -1,
UART_ERROR_CONFIG = -2,
UART_ERROR_CLOSE = -3,
UART_ERROR_WRITE = -4,
UART_ERROR_READ = -5,
UART_ERROR_TIMEOUT = -6,
UART_ERROR_PARAM = -7,
UART_ERROR_NOT_OPEN = -8
} UartErrorCode;
/* 错误信息表 */
static const char* error_messages[] = {
"Success",
"Failed to open device",
"Failed to configure device",
"Failed to close device",
"Write error",
"Read error",
"Operation timed out",
"Invalid parameter",
"Device not open"
};
/* 打开串口设备 */
UartDevice* uart_open(const char* device_path, UartBaudrate baudrate) {
UartDevice* device = (UartDevice*)malloc(sizeof(UartDevice));
if (!device) {
errno = ENOMEM;
return NULL;
}
memset(device, 0, sizeof(UartDevice));
strncpy(device->device_path, device_path, sizeof(device->device_path) - 1);
/* 打开设备 */
device->fd = open(device_path, O_RDWR | O_NOCTTY | O_NDELAY);
if (device->fd == -1) {
free(device);
return NULL;
}
/* 保存原始配置 */
if (tcgetattr(device->fd, &device->old_config) != 0) {
close(device->fd);
free(device);
return NULL;
}
/* 设置为阻塞模式 */
fcntl(device->fd, F_SETFL, 0);
/* 配置波特率 */
struct termios config;
memset(&config, 0, sizeof(config));
if (cfsetispeed(&config, baudrate) != 0 ||
cfsetospeed(&config, baudrate) != 0) {
close(device->fd);
free(device);
return NULL;
}
device->is_open = 1;
return device;
}
/* 关闭串口设备 */
int uart_close(UartDevice* device) {
if (!device || !device->is_open) {
errno = EINVAL;
return UART_ERROR_PARAM;
}
/* 恢复原始配置 */
if (tcsetattr(device->fd, TCSANOW, &device->old_config) != 0) {
return UART_ERROR_CONFIG;
}
/* 关闭设备 */
if (close(device->fd) != 0) {
return UART_ERROR_CLOSE;
}
device->is_open = 0;
free(device);
return UART_SUCCESS;
}
/* 配置串口参数 */
int uart_config(UartDevice* device, UartDataBits data_bits,
UartStopBits stop_bits, UartParity parity,
UartFlowControl flow_control) {
if (!device || !device->is_open) {
errno = EINVAL;
return UART_ERROR_PARAM;
}
struct termios config;
if (tcgetattr(device->fd, &config) != 0) {
return UART_ERROR_CONFIG;
}
/* 设置控制模式 */
config.c_cflag |= (CLOCAL | CREAD);
/* 设置数据位、停止位和校验位 */
config.c_cflag &= ~CSIZE;
config.c_cflag |= data_bits;
config.c_cflag &= ~CSTOPB;
config.c_cflag |= stop_bits;
/* 设置校验位 */
config.c_cflag &= ~(PARENB | PARODD);
config.c_iflag &= ~(INPCK | ISTRIP);
config.c_cflag |= parity;
if (parity != PARITY_NONE) {
config.c_iflag |= INPCK;
}
/* 设置流控制 */
config.c_cflag &= ~(CRTSCTS);
config.c_iflag &= ~(IXON | IXOFF | IXANY);
if (flow_control == FLOW_CONTROL_HARDWARE) {
config.c_cflag |= CRTSCTS;
} else if (flow_control == FLOW_CONTROL_SOFTWARE) {
config.c_iflag |= (IXON | IXOFF);
}
/* 设置本地模式 */
config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* 设置输入模式 */
config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
/* 设置输出模式 */
config.c_oflag &= ~OPOST;
/* 应用配置 */
if (tcsetattr(device->fd, TCSANOW, &config) != 0) {
return UART_ERROR_CONFIG;
}
return UART_SUCCESS;
}
/* 设置超时时间 */
int uart_set_timeout(UartDevice* device, int timeout_ms) {
if (!device || !device->is_open) {
errno = EINVAL;
return UART_ERROR_PARAM;
}
struct termios config;
if (tcgetattr(device->fd, &config) != 0) {
return UART_ERROR_CONFIG;
}
/* 设置超时参数 */
if (timeout_ms <= 0) {
/* 非阻塞模式 */
config.c_cc[VMIN] = 0;
config.c_cc[VTIME] = 0;
} else {
/* 阻塞模式,带超时 */
config.c_cc[VMIN] = 0;
config.c_cc[VTIME] = timeout_ms / 100; /* 以0.1秒为单位 */
}
if (tcsetattr(device->fd, TCSANOW, &config) != 0) {
return UART_ERROR_CONFIG;
}
return UART_SUCCESS;
}
/* 向串口写入数据 */
int uart_write(UartDevice* device, const uint8_t* data, size_t length) {
if (!device || !device->is_open || !data || length == 0) {
errno = EINVAL;
return UART_ERROR_PARAM;
}
ssize_t written = write(device->fd, data, length);
if (written == -1) {
return UART_ERROR_WRITE;
}
return (int)written;
}
/* 从串口读取数据 */
int uart_read(UartDevice* device, uint8_t* buffer, size_t max_length, int timeout_ms) {
if (!device || !device->is_open || !buffer || max_length == 0) {
errno = EINVAL;
return UART_ERROR_PARAM;
}
/* 临时设置超时 */
struct termios old_config;
if (tcgetattr(device->fd, &old_config) != 0) {
return UART_ERROR_CONFIG;
}
struct termios new_config = old_config;
if (timeout_ms > 0) {
new_config.c_cc[VMIN] = 0;
new_config.c_cc[VTIME] = timeout_ms / 100; /* 以0.1秒为单位 */
if (tcsetattr(device->fd, TCSANOW, &new_config) != 0) {
return UART_ERROR_CONFIG;
}
}
/* 读取数据 */
ssize_t read_bytes = read(device->fd, buffer, max_length);
/* 恢复原始配置 */
if (timeout_ms > 0) {
tcsetattr(device->fd, TCSANOW, &old_config);
}
if (read_bytes == -1) {
return UART_ERROR_READ;
} else if (read_bytes == 0 && timeout_ms > 0) {
return UART_ERROR_TIMEOUT;
}
return (int)read_bytes;
}
/* 清空串口缓冲区 */
int uart_flush(UartDevice* device) {
if (!device || !device->is_open) {
errno = EINVAL;
return UART_ERROR_PARAM;
}
if (tcflush(device->fd, TCIOFLUSH) != 0) {
return UART_ERROR_CONFIG;
}
return UART_SUCCESS;
}
/* 获取错误码 */
int uart_get_error(UartDevice* device) {
if (!device || !device->is_open) {
return UART_ERROR_NOT_OPEN;
}
int status;
if (ioctl(device->fd, TIOCMGET, &status) != 0) {
return UART_ERROR_CONFIG;
}
return UART_SUCCESS;
}
/* 获取错误信息 */
const char* uart_strerror(int error_code) {
if (error_code < UART_SUCCESS || error_code > UART_ERROR_NOT_OPEN) {
return "Unknown error";
}
return error_messages[error_code * (-1)];
}
/* uart_example.c - 串口库使用示例 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "uart.h"
#define UART_DEVICE "/dev/ttyS0"
#define BAUDRATE BAUD_115200
#define BUFFER_SIZE 256
int main() {
/* 打开串口设备 */
UartDevice* uart = uart_open(UART_DEVICE, BAUDRATE);
if (!uart) {
fprintf(stderr, "Failed to open UART device: %s\n", strerror(errno));
return EXIT_FAILURE;
}
printf("UART device opened successfully\n");
/* 配置串口参数 */
int result = uart_config(uart, DATA_BITS_8, STOP_BITS_1, PARITY_NONE, FLOW_CONTROL_NONE);
if (result != UART_SUCCESS) {
fprintf(stderr, "Failed to configure UART device: %s\n", uart_strerror(result));
uart_close(uart);
return EXIT_FAILURE;
}
printf("UART device configured successfully\n");
/* 清空缓冲区 */
uart_flush(uart);
/* 发送数据 */
const char* test_data = "Hello, UART world!\r\n";
result = uart_write(uart, (const uint8_t*)test_data, strlen(test_data));
if (result < 0) {
fprintf(stderr, "Failed to write data: %s\n", uart_strerror(result));
uart_close(uart);
return EXIT_FAILURE;
}
printf("Sent %d bytes: %s\n", result, test_data);
/* 接收数据 */
uint8_t buffer[BUFFER_SIZE];
memset(buffer, 0, BUFFER_SIZE);
result = uart_read(uart, buffer, BUFFER_SIZE - 1, 2000); /* 2秒超时 */
if (result < 0) {
if (result == UART_ERROR_TIMEOUT) {
printf("Read operation timed out\n");
} else {
fprintf(stderr, "Failed to read data: %s\n", uart_strerror(result));
}
} else {
printf("Received %d bytes: %s\n", result, buffer);
}
/* 关闭串口设备 */
result = uart_close(uart);
if (result != UART_SUCCESS) {
fprintf(stderr, "Failed to close UART device: %s\n", uart_strerror(result));
return EXIT_FAILURE;
}
printf("UART device closed successfully\n");
return EXIT_SUCCESS;
}
库使用方法
-
初始化与配置:
// 打开串口设备 UartDevice* uart = uart_open("/dev/ttyS0", BAUD_115200); // 配置串口参数 uart_config(uart, DATA_BITS_8, STOP_BITS_1, PARITY_NONE, FLOW_CONTROL_NONE);
-
数据发送:
const char* data = "Hello, UART!"; int result = uart_write(uart, (const uint8_t*)data, strlen(data)); if (result < 0) { // 处理错误 }
-
数据接收:
uint8_t buffer[256]; int result = uart_read(uart, buffer, sizeof(buffer), 1000); // 1秒超时 if (result > 0) { // 处理接收到的数据 } else if (result == UART_ERROR_TIMEOUT) { // 处理超时 } else { // 处理错误 }
-
资源释放:
uart_close(uart);
编译和测试
要编译这个库和示例程序,可以使用以下 Makefile:
makefile
CC = gcc
CFLAGS = -Wall -Werror -O2
LDFLAGS =
# 源文件和目标文件
SRCS = uart.c uart_example.c
OBJS = $(SRCS:.c=.o)
# 可执行文件
TARGET = uart_example
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(TARGET)
使用以下命令编译:
make
运行测试程序:
./uart_example
扩展建议
- 添加多线程支持:在高并发场景下,可添加线程安全机制
- 实现异步 IO:使用 select/poll/epoll 实现非阻塞 IO
- 添加日志功能:增加详细的日志记录,便于调试
- 扩展错误处理:增强错误检测和恢复能力
- 添加硬件流控制支持:完善 RTS/CTS 流控制功能
这个串口库设计简洁,易于使用,适合嵌入式 Linux 系统中的串口通信应用。根据实际需求,你可以进一步扩展其功能。