串口通信初始化过程是怎样的???

一、简介

本文简单介绍一下 Linux嵌入式开发中,串口通信的初始化过程。

二、串口通信的初始化过程

串口通信初始化过程涉及配置串行端口的参数,以确保设备间能够正确地进行数据传输。这通常包括设置波特率、数据位、停止位、校验位等。以下是一个详细的步骤指南,以及如何在 Linux 环境下通过 C 语言完成串口通信的初始化。

1. 初始化串口的基本步骤:

打开串口设备:

使用 open() 函数打开串口设备文件(例如 /dev/ttyS0 或 /dev/ttyUSB0),并指定适当的访问模式(读写权限)和标志。

获取当前串口属性:

使用 tcgetattr() 函数获取串口当前的配置信息到一个 termios 结构体中,以便后续修改。

修改串口属性:

设置波特率:cfsetspeed()设置波特率。

配置数据位。

配置停止位;

配置校验类型,也可以配置无校验(是否有奇偶校验)。

根据需要启用或禁用硬件流控(CRTSCTS)、或者软件流控(IXON, IXOFF, IXANY)等。

设置超时和最小读取字符数(目的是控制read()函数从串口读取数据的阻塞行为,这样非常重要,防止读取阻塞);

设置本地模式并启用接收。

禁用回显等等其他配置;

应用新的串口属性:

使用 tcsetattr() 函数将修改后的串口设置应用于串口设备。

2. 初始化过程中,控制从串口读取数据的阻塞行为

串口通信初始化过程中,设置超时和最小读取字符数配置是很重要的,这两个配置是为了控制read()函数从串口读取数据的阻塞行为!!!

在串口通信中,设置超时和最小读取字符数是非常重要的,这可以帮助你控制数据读取的行为,避免程序无限期等待数据或过早读取不完整的数据。这些设置是通过 termios 结构体中的 c_cc 成员来配置的,具体来说是使用 VMINVTIME 控制字符。

一、参数定义与作用
这两个参数位于 termios 结构体的 c_cc 数组中,用于配置串口的读取规则:

VMIN:最小读取字符数(c_cc[VMIN])
定义 read() 函数返回前需要接收到的最小字符数量。
VTIME:超时时间(c_cc[VTIME])
单位为 0.1 秒,定义等待数据的最长时间(与 VMIN 配合使用)。

二、常见配置组合及效果
VMIN 和 VTIME 的不同组合会产生完全不同的读取行为,需根据实际场景选择:

三、Linux应用层串口通信示例代码

下面是Linux应用层串口通信示例代码:

复制代码
int initComm(char *ttyDir, unsigned speed)
{
    int retry = 60;  // 设备锁定重试次数(60秒超时)

    // 以读写方式打开串口设备,不设为控制终端
    int mfd = open(ttyDir, O_RDWR | O_NOCTTY);
    if (mfd < 0)
    {
        perror(ttyDir);  
        return -1;
    }

    // 尝试获取设备锁,防止多进程同时访问
    while (lockf(mfd, F_TLOCK, 0) < 0)
    {
        sleep(1);    
        retry--;     
        if (retry <= 0)
        {
            printf("Device %s locked.\n", ttyDir);  
            close(mfd); 
            return -1;
        }
    }

    // 将数值波特率转换为系统定义的波特率常量
    switch (speed)
    {
    case 4800:
        speed = B4800;
        break;
    case 9600:
        speed = B9600;
        break;
    case 19200:
        speed = B19200;
        break;
    case 38400:
        speed = B38400;
        break;
    case 57600:
        speed = B57600;
        break;
    case 115200:
        speed = B115200;
        break;
    case 230400:
        speed = B230400;
        break;
    case 460800:
        speed = B460800;
        break;
    default:
        printf("speed error");  
        return -1;
    }

    // 获取当前串口属性
    struct termios Opt;
    tcgetattr(mfd, &Opt);

    // 设置波特率
    cfsetispeed(&Opt, speed);  // 输入速度
    cfsetospeed(&Opt, speed);  // 输出速度
    tcsetattr(mfd, TCSANOW, &Opt);  //立即应用设置

    tcflush(mfd, TCIOFLUSH);  // 清空输入输出缓冲区
    // 重新获取属性以确保设置生效
    tcgetattr(mfd, &Opt);

    // 配置串口参数
    Opt.c_cflag &= ~CSIZE;  // 清除数据位掩码
    Opt.c_cflag |= CS8;     // 设置8位数据位

    // 启用软件流控(XON/XOFF)
    Opt.c_cflag |= IXON | IXOFF | IXANY;  
    Opt.c_cflag &= ~PARENB;  // 禁用奇偶校验
    Opt.c_cflag &= ~CSTOPB;  // 设置1位停止位(清除2位停止位标志)
    // 设置本地模式标志
    // 原始模式(非规范模式),禁用回显和信号 
    Opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);    
    // 设置输入模式标志
    // 禁用特殊输入处理 
    Opt.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);    
    // 设置输出模式标志
    // 原始输出(禁用输出处理)
    Opt.c_oflag &= ~OPOST;  

    // 设置超时和最小读取字符数
    // 读取超时(单位:0.1秒,这里15秒)
    // 最小读取字符数(0表示非阻塞)
    Opt.c_cc[VTIME] = 150;  
    Opt.c_cc[VMIN] = 0;    
     
    // 应用新的串口属性设置
    if (tcsetattr(mfd, TCSANOW, &Opt) < 0)
    {
        perror(ttyDir);  
        close(mfd);      
        return -1;
    }
    
    tcflush(mfd, TCIOFLUSH);  //再次清空输入输出缓冲区
    return mfd;  // 返回配置好的串口文件描述符
}

Linux嵌入式开发