按照对linux系统的理解,串口编程的顺序无非就是open,read,write,close,而串口有波特率、数据位等重要参数需要设置,因此还应该用到设置函数,那么接下来就带着这几个问题去学习linux下的串口编程。

linux系统通常使用termios结构存储串口参数,该结构在termios.h头文件定义如下:

 

struct termios
{
//输入模式标志
//输出模式标志
//控制模式标志
//本地模式标志
//行控制
//控制字符 
};

 

串口编程常用到的函数如下:

读取当前参数函数:int tcgetattr(int fd, struct termios *termios_p);

1:fd是open返回的句柄

2:*termios_p是前面介绍的结构体

  -在初始化开始调用这个函数

波特率设置函数:int cfsetispeed(const struct termios *termios_p, speed_t speed);

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

1:前面介绍的结构体

2:speed波特率

0,失败返回-1

清空串口BUFFER中的数据函数:int tcflush(int fd, int queue_selector);

1:fd是open返回的句柄

2:控制tcflush的操作。

  -常用的有三个值,

TCIFLUSH清除正收到的数据,且不会读取出来;

TCOFLUSH清除正写入的数据,且不会发送至终端;

TCIOFLUSH清除所有正在发生的I/O数据

0,失败返回-1

设置串口参数函数:int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);

fd:open返回的文件句柄

optional_actions:参数生效的时间。

    -有三个常用的值,

TCSANOW:不等数据传输完毕就立即改变属性;

TCSADRAIN:等待所有数据传输结束才改变属性;

TCSAFLUSH:清空输入输出缓冲区才改变属性;

termios_p:在旧的参数基础上修改的后的参数

  -返回值:执行成功返回0,失败返回-1

  -一般初始化最后会使用这个函数

 

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

int uart_set(int fd, int baudrate, int bits, int stop, char parity, char flow)
{
    int ret;
    speed_t uart_speed;
    struct termios termios_uart;

    memset(&termios_uart, 0, sizeof(termios_uart));

//读取当前串口参数值
    ret = tcgetattr(fd, &termios_uart);
    if(ret < 0)
    { 
        perror("tcgetattr error");
        return -1;
    }

//设置波特率
    switch(baudrate) 
    {
        case 9600:   
                        cfsetspeed(&termios_uart, B9600);            break;
        case 38400:  
                        cfsetspeed(&termios_uart, B38400);           break;
        case 115200: 
                        cfsetspeed(&termios_uart, B115200);          break;
        case 230400: 
                        cfsetspeed(&termios_uart, B230400);          break;
        default:
                        printf("Baudrate not supported \n");         return -1;
    }

//设置数据位
    switch(bits) 
    {
        case 6:
                        termios_uart.c_cflag &= ~CSIZE;
                        termios_uart.c_cflag |= CS6; 
                        break;
        case 7:
                        termios_uart.c_cflag &= ~CSIZE;
                        termios_uart.c_cflag |= CS7; 
                        break;
        case 8:
                        termios_uart.c_cflag &= ~CSIZE;
                        termios_uart.c_cflag |= CS8; 
                        break;
        default: 
                        printf("Data bits not supported \n"); 
                        return -1;
    }

//设置停止位
    switch(stop) 
    {
        case 1: 
                        termios_uart.c_cflag &= ~CSTOPB;        break;
        case 2:
                        termios_uart.c_cflag |= CSTOPB;         break;
        default: 
                        printf("Stop bits not supported\n");    return -1;
    }

//设置校验位
    switch(parity) 
    {
//无校验
        case 'N': 
                        termios_uart.c_cflag &= ~PARENB;
                        termios_uart.c_iflag &= ~INPCK;        
                        break;

//奇校验
        case 'O':
                        termios_uart.c_cflag |= PARENB;
                        termios_uart.c_cflag |= PARODD;
                        termios_uart.c_iflag |= INPCK;
                        termios_uart.c_iflag |= ISTRIP;
                        break;

//偶校验
        case 'E':
                        termios_uart.c_cflag |= PARENB;
                        termios_uart.c_cflag &= ~PARODD;
                        termios_uart.c_iflag |= INPCK;
                        termios_uart.c_iflag |= ISTRIP;
                        break;

        default:
                        printf("Parity not supported\n"); 
                        return -1;
    }



//设置流控
    switch(flow) 
    {
//无流控
        case 'N':   
                        termios_uart.c_cflag &= ~CRTSCTS;
                        termios_uart.c_iflag &= ~(IXON | IXOFF | IXANY);
                        break;

//硬件流控
        case 'H':   
                        termios_uart.c_cflag |= CRTSCTS;
                        termios_uart.c_iflag &= ~(IXON | IXOFF | IXANY);
                        break;

//软件流控
        case 'S':   
                        termios_uart.c_cflag &= ~CRTSCTS;
                        termios_uart.c_iflag |= (IXON | IXOFF | IXANY);
                        break;

        default:
                        printf("Flow control parameter error\n");
                        return -1;
    }

//其他设置
//忽略modem(调制解调器)控制线
//使能接收

//禁能执行定义(implementation-defined)输出处理,意思就是输出的某些特殊数据会作特殊处理,如果禁能的话那么就按原始数据输出
    termios_uart.c_oflag &= ~OPOST;    


    /*

设置本地模式位原始模式

规范输入模式,如果设置了那么退格等特殊字符会产生实际动作

则将输入字符回送到终端设备

如果ICANON也设置了,那么收到ERASE字符后会从显示字符中擦除一个字符(通俗点理解就是收到退格键后显示内容会往回删一个字符)

使终端产生的信号起作用(比如按ctrl+c可以使程序退出)

*/
    termios_uart.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);



    /*

设置等待时间和最小接收字符

这两个值只有在阻塞模式下有意义,也就是说open的时候不能传入O_NONBLOCK,如果经过了c_cc[VTIME]这么长时间,缓冲区内有数据,但是还没达到c_cc[VMIN]个

数据,read也会返回。而如果当缓冲区内有了c_cc[VMIN]个数据时,无论等待时间是否到了c_cc[VTIME],read都会返回,但返回值可能比c_cc[VMIN]还大。如果将

的值设置为0,那么当经过c_cc[VTIME]时间后read也会返回,返回值为0。如果将c_cc[VTIME]和c_cc[VMIN]都设置为0,那么程序运行的效果与设置

类似,不同的是如果设置了O_NONBLOCK,那么在没有数据时read返回-1,而如果没有设置O_NONBLOCK,那么在没有数据时read返回的是0。

 

*/
//设置等待时间,单位1/10秒
//最少读取一个字符

//清空读缓冲区

//写入配置
    ret = tcsetattr(fd, TCSANOW, &termios_uart);
    if(ret == -1) 
    {
        printf("tcsetattr failed\n");
    }

    return ret;
}

int main(int argc, char *argv[])
{
    int fd;
    int len;
    char *filename;
    char buf[512];

    char *buffer = "hello lianmeng!\n";

    memset(buf, 0, sizeof(buf));

    filename = argv[1];

    fd = open(filename, O_RDWR | O_NONBLOCK);
    if(fd < 0)
    {
        printf("can't open file %s \r\n", filename);
        return -1;
    }

    uart_set(fd, 115200, 8, 1, 'N', 'N');

    while(1)
    {
        len = read(fd, buf, sizeof(buf));

        if(len > 0)
        {
            write(fd, buf, len);
        }
    }

    close(fd);
}