嵌入式开发之linux下串口库的开发案例分享

下面我将分享一个在 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;
}

库使用方法

  1. 初始化与配置

    // 打开串口设备
    UartDevice* uart = uart_open("/dev/ttyS0", BAUD_115200);
    
    // 配置串口参数
    uart_config(uart, DATA_BITS_8, STOP_BITS_1, PARITY_NONE, FLOW_CONTROL_NONE);
    
  2. 数据发送

    const char* data = "Hello, UART!";
    int result = uart_write(uart, (const uint8_t*)data, strlen(data));
    if (result < 0) {
        // 处理错误
    }
    
  3. 数据接收

    uint8_t buffer[256];
    int result = uart_read(uart, buffer, sizeof(buffer), 1000); // 1秒超时
    if (result > 0) {
        // 处理接收到的数据
    } else if (result == UART_ERROR_TIMEOUT) {
        // 处理超时
    } else {
        // 处理错误
    }
    
  4. 资源释放

    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

扩展建议

  1. 添加多线程支持:在高并发场景下,可添加线程安全机制
  2. 实现异步 IO:使用 select/poll/epoll 实现非阻塞 IO
  3. 添加日志功能:增加详细的日志记录,便于调试
  4. 扩展错误处理:增强错误检测和恢复能力
  5. 添加硬件流控制支持:完善 RTS/CTS 流控制功能

这个串口库设计简洁,易于使用,适合嵌入式 Linux 系统中的串口通信应用。根据实际需求,你可以进一步扩展其功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

start_up_go

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值