Linux串口编程 -- 1.打开和初始化串口

目录

1.简介

2.开发环境

3.打开串口编程

4.串口初始化常用函数

4.1获取终端属性--tcgetattr

4.2结构体-- struct termios

4.2.1输入模式标志--tcflag_t c_iflag

4.2.2输出模式标志--tcflag_t c_oflag

4.2.3控制模式标志--tcflag_t c_cflag

4.2.4本地模式标志--tcflag_t c_lflag

4.2.5控制字符数组--cc_t c_cc[NCCS]

4.2.6输入和输出波特率--speed_t c_ispeed 和 speed_t c_ospeed

4.3设置原始模式--cfmakeraw

4.4设置输入输出波特率--cfsetispeed、cfsetospeed

4.5设置终端属性--tcsetattr

4.6清空串口缓冲区--tcflush

5.初始化代码示例

1.简介

        该博客主要介绍在Linux操作系统下使用C语言编写打开串口和串口的初始化函数。

2.开发环境

        操作系统:Ubuntu18.04

        串口设备:USB转RS232线(PL2303GC)、DTU数传电台x2

3.打开串口编程

        在LINUX操作系统下,一切皆是文件,串口设备也不例外。串口是在日常开发中使用频率非常高的外设,在此,介绍一下如何打开串口设备。

        首先输入命令 ,查看一下有哪些串口设备。

cd /dev
ls -l

        如下图所示的ttyS*和ttyUSB0就是串口设备:

       我使用的是USB转RS232的设备,所以我的是ttyUSB0。

       打开这个设备,和打开其他文件一样,使用open函数。代码如下:

#include <fcntl.h>
#include <termios.h>

int Serial_Init(const char *deviceName)
{
    /*  1.打开设备  */
    int fd = -1;
    fd = open(deviceName, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if(fd < 0)
    {
        perror("Serial_Init:open");
        return -1;
    }
    
    return fd;
}

        这里使用O_RDWR | O_NOCTTY | O_NONBLOCK这三个属性,表示这个文件可读可写,告知进程该串口设备不成为控制终端,非阻塞模式。

        如果不用O_NONBLOCK打开串口的话,有时候会打不开设备,程序在open时就阻塞住了。

4.串口初始化常用函数

        串口初始化,主要就进行波特率,数据位,停止位,校验位,CTS/RTS等参数进行设置。下面先介绍常用的有关配置的函数。

4.1获取终端属性--tcgetattr

       函数原型:

#include <termios.h>
#include <unistd.h>

int tcgetattr(int fd, struct termios *termios_p);

        函数功能:用于获取终端设备属性的函数。

        函数参数:1.fd:这是一个文件描述符,它指向要获取属性的终端设备。

                          2.termios_p:这是一个指向 struct termios 结构体的指针。struct termios 结构体用于存储终端设备的各种属性,tcgetattr 函数会将获取到的终端属性填充到该结构体中。

        函数返回值:成功返回0,失败返回-1,并且设置错误号。

4.2结构体-- struct termios

        对于本节,简单的过一下就好,不用深究,主要看4.2.3和4.2.6。

        struct termios 是用于存储终端设备属性的结构体,它是终端控制(termios)函数族的重要组成部分,通过该结构体可以对终端设备的输入、输出、控制和本地模式等进行配置和管理。下面详细介绍该结构体的各个成员:

#include <termios.h>

struct termios {
    tcflag_t c_iflag;    /* 输入模式标志 */
    tcflag_t c_oflag;    /* 输出模式标志 */
    tcflag_t c_cflag;    /* 控制模式标志 */
    tcflag_t c_lflag;    /* 本地模式标志 */
    cc_t     c_cc[NCCS]; /* 控制字符数组 */
    speed_t  c_ispeed;   /* 输入波特率 */
    speed_t  c_ospeed;   /* 输出波特率 */
};

4.2.1输入模式标志--tcflag_t c_iflag

        该成员用于控制输入数据的处理方式,常见的标志位及其含义如下表所示:

IGNBRK忽略输入中的 BREAK 条件(通常是一个特殊的信号,表示线路上的异常)
BRKINT如果 IGNBRK 未设置,接收到 BREAK 条件时将产生一个中断信号
IGNPAR忽略输入中的奇偶校验错误
PARMRK如果 IGNPAR 未设置,在奇偶校验错误的字符前插入一个特殊的标记字符
INPCK启用输入奇偶校验
ISTRIP将输入字符截断为 7 位
INLCR将输入的换行符(\n)转换为回车符(\r)
IGNCR忽略输入中的回车符(\r)
ICRNL将输入的回车符(\r)转换为换行符(\n)
IXON启用输出的 XON/XOFF 流控制(当输入缓冲区满时发送 XOFF 信号,缓冲区有空间时发送 XON 信号)
IXOFF启用输入的 XON/XOFF 流控制

4.2.2输出模式标志--tcflag_t c_oflag

        该成员用于控制输出数据的处理方式,常见的标志位及其含义如下:

OPOST启用输出处理,即对输出数据进行一些转换和处理
ONLCR如果 OPOST 已设置,将输出的换行符(\n)转换为回车符和换行符(\r\n)
OCRNL如果 OPOST 已设置,将输出的回车符(\r)转换为换行符(\n)
ONOCR如果 OPOST 已设置,在第 0 列不输出回车符
ONLRET如果 OPOST 已设置,换行符(\n)会自动执行回车操作

4.2.3控制模式标志--tcflag_t c_cflag

        该成员用于控制终端设备的硬件特性和数据传输参数,常见的标志位及其含义如下:

CBAUD波特率掩码,用于选择波特率。B115200,B9600等等
CSIZE数据位掩码,用于选择数据位的位数(如 5、6、7 或 8 位)CS5,CS6,CS7,CS8
CSTOPB设置两个停止位(默认是一个停止位)
CREAD启用接收器,使能数据接收
PARENB启用奇偶校验
PARODD使用奇校验(默认是偶校验)
CLOCAL忽略调制解调器的控制信号,允许本地连接。

4.2.4本地模式标志--tcflag_t c_lflag

        该成员用于控制终端设备的本地特性,如信号处理、回显等,常见的标志位及其含义如下:

ECHO启用字符回显,即输入的字符会在终端上显示出来
ECHOE如果 ECHO 已设置,在删除字符时会显示删除效果(如退格和空格)
ECHOK如果 ECHO 已设置,在输入换行符或回车符时会回显删除字符
ECHONL如果 ECHO 未设置,仍然回显换行符
ICANON启用规范模式,即输入以行为单位进行处理,用户输入的字符会被缓存,直到按下换行符才会将整行数据发送给程序。
ISIG启用信号生成,即当用户输入特定的字符(如 Ctrl+C、Ctrl+Z)时会产生相应的信号
NOFLSH在产生信号时不刷新输入和输出缓冲区

4.2.5控制字符数组--cc_t c_cc[NCCS]

        该数组用于存储一些特殊的控制字符,常见的控制字符及其索引如下:

VEOF文件结束符,通常是 Ctrl+D
VEOL行结束符
VERASE删除字符,通常是 Backspace 或 Delete 键
VWERASE删除一个单词的字符
VKILL删除整行输入的字符
VINTR中断信号字符,通常是 Ctrl+C
VQUIT退出信号字符,通常是 Ctrl+\
VSUSP挂起信号字符,通常是 Ctrl+Z

4.2.6输入和输出波特率--speed_t c_ispeed 和 speed_t c_ospeed

        c_ispeed :用于设置输入数据的波特率,即数据从终端设备传输到计算机的速度。

        c_ospeed :用于设置输出数据的波特率,即数据从计算机传输到终端设备的速度。 常见的波特率值有 B9600、B115200 等。

4.3设置原始模式--cfmakeraw

        cfmakeraw()用于将终端设备设置为原始模式(raw mode)的函数。在原始模式下,终端设备对输入和输出数据的处理变得非常简单,几乎不进行任何特殊的转换或处理,数据以最原始的形式进行传输。

        函数原型:

#include <termios.h>

void cfmakeraw(struct termios *termios_p);

        函数参数:termios_p:这是一个指向 struct termios 结构体的指针。struct termios 结构体用于存储终端设备的各种属性,cfmakeraw() 函数会修改该结构体中的成员,将终端设备的属性设置为原始模式。

        对传递进来的结构体修改:

        1.输入模式(c_iflag) :清除 IGNBRK、BRKINT、PARMRK、ISTRIP、INLCR、IGNCR、ICRNL 和 IXON 标志位。这意味着不会忽略 BREAK 条件、不处理奇偶校验错误、不进行换行符和回车符的转换,也不启用 XON/XOFF 流控制。

        2.输出模式(c_oflag) :清除 OPOST 标志位,禁用输出处理,即不会对输出数据进行任何特殊的转换,如将换行符转换为回车符和换行符。

        3.控制模式(c_cflag) :设置 CS8 标志位,将数据位设置为 8 位。清除 PARENB 标志位,禁用奇偶校验。清除 CSTOPB 标志位,设置一个停止位。设置 CREAD 标志位,启用接收器。

        4.本地模式(c_lflag) :清除 ECHO、ECHONL、ICANON、ISIG 和 IEXTEN 标志位。这意味着不进行字符回显、不回显换行符、禁用规范模式(输入不以行为单位处理)、不生成信号,也不进行扩展输入处理。

        5.控制字符(c_cc) :将 VMIN 设置为 1,表示最少需要读取一个字符。将 VTIME 设置为 0,表示不设置超时时间。

        一般地,在使用串口读取传感器数据时,需要设置成原始模式!

4.4设置输入输出波特率--cfsetispeed、cfsetospeed

        cfsetispeed函数和cfsetospeed函数分别设置输入输出波特率的函数。

        函数原型:

#include <termios.h>

int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);

        函数参数:

        1.termios_p :指向 struct termios 结构体的指针。struct termios 结构体用于存储终端设备的各种属性,该函数会修改此结构体中的输入波特率设置。

        2.speed :指定要设置的输入波特率,其类型为 speed_t。比如B9600,B115200等。

        函数返回值:如果函数调用成功,返回值为 0。如果指定的波特率无效,返回值为 -1,并设置 errno 为 EINVAL。

4.5设置终端属性--tcsetattr

        tcsetattr函数用于设置终端设备属性的函数,它属于终端控制(termios)函数族,和 tcgetattr 等函数配合使用,能实现对终端设备输入、输出、控制等多种属性的灵活配置。

        函数原型:

#include <termios.h>
#include <unistd.h>

int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);

        函数参数:

        1.fd :文件描述符。传递对应终端设备的文件描述符。

        2.optional_actions :用于指定属性变更的生效时间,有以下几种可选值:

TCSANOW立即将新的终端属性应用到指定的终端设备,当前正在进行的输入输出操作不受影响。
TCSADRAIN等待所有输出数据传输完成后,再将新的终端属性应用到指定的终端设备。适用于需要保证输出数据完整传输的场景。
TCSAFLUSH等待所有输出数据传输完成,并且丢弃当前输入缓冲区中的所有数据后,再应用新的终端属性。这可避免旧的输入数据干扰新属性下的操作。

        3.termios_p :指向 struct termios 结构体的指针,该结构体包含了要设置的终端设备的属性。通常先通过 tcgetattr 获取当前属性,进行修改后再使用 tcsetattr 进行设置。

        函数返回值:若函数调用成功,返回值为 0。若函数调用失败,返回值为 -1,并设置 errno 来指示具体的错误类型。

4.6清空串口缓冲区--tcflush

        tcflush 是用于清空终端设备输入或输出缓冲区的函数,它是终端控制(termios)函数族的一部分,能够帮助开发者管理终端设备的数据流,确保数据的一致性和准确性。

        函数原型:

#include <termios.h>
#include <unistd.h>

int tcflush(int fd, int queue_selector);

        函数参数:

        1.fd :这是一个文件描述符,代表要操作的终端设备。

        2.queue_selector :用于指定要清空的缓冲区类型,有以下几种可选值:

TCIFLUSH清空输入缓冲区,即丢弃所有已经接收但尚未被程序读取的数据。
TCOFLUSH清空输出缓冲区,即丢弃所有已经准备好但尚未发送到终端设备的数据。
TCIOFLUSH同时清空输入缓冲区和输出缓冲区,既丢弃未读取的输入数据,也丢弃未发送的输出数据。

        函数返回值:若函数调用成功,返回值为 0。若函数调用失败,返回值为 -1,并设置 errno 来指示具体的错误类型。

5.初始化代码示例

        笔者一般使用串口连接传感器,进而进行发送数据读取数据。笔者使用的是DTU数传电台作为实验设备,使用USB转RS232线。电脑上插USB端,RS232插DTU数传电台上的接口。

        将打开和串口初始化封装成一个函数,思路如下:

        1.打开设备,设置可读可写,不成为终端设备,非阻塞模式

        2.设置成原始模式

        3.设置接收使能,设置停止位,数据位,校验位,波特率

        4.设置属性

        5.清空串口缓冲区

代码如下:

#include <stdio.h>
#include <fcntl.h>     
#include <termios.h>    
#include <unistd.h>    

/*******************************************************************
 * 函数原型:int DTU_Init(const char *deviceName)
 * 函数简介:打开DTU串口设备
 * 函数参数:dtuDeviceName:串口设备字符串
 * 函数返回值: 成功打开串口设备时,返回文件描述符,失败返回-1
 *****************************************************************/
int DTU_Init(const char *deviceName)
{
    /*  1.打开设备  */
    int fd = -1;
    fd = open(deviceName, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if(fd < 0)
    {
        perror("DTU_Init:open");
        return -1;
    }

    /*  2.配置串口参数 - 设置为原始模式
                         设置8个数据位,  1个停止位, 无奇偶校验, 波特率115200  */
    struct termios options = {0}; 
    cfmakeraw(&options);
    options.c_iflag &=~(CSIZE | CSTOPB | PARENB);         //清除有关数据位的控制位,设置1个停止位,不使用校验位
    options.c_cflag |= (CS8 | CREAD | CLOCAL);            //设置数据位位8位,接收使能,忽略调制解调器控制信号
    options.c_oflag &= ~OPOST ;                           //禁用输出处理
    cfsetispeed(&options, B115200);                       //设置输入输出波特率为115200
    cfsetospeed(&options, B115200);

    options.c_cc[VMIN] = 1;                    //设置最少读取的字符数为1
	options.c_cc[VTIME] = 10;                  //设置读取超时时间为 1 秒(单位为 0.1 秒)

    /*  3.写入串口配置  */
    int ret = -1;
    ret = tcsetattr(fd, TCSANOW, &options);
    if(ret < 0)
    {
        perror("DTU_Init:tcsetattr");
        return -1;
    }

    /*  4.清空串口缓冲区    */
    tcflush(fd, TCIOFLUSH);

    usleep (10000) ;
    return fd;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值