Linux下的串口编程及非阻塞模式

本篇介绍了如何在linux系统下向串口发送数据。包括read的阻塞和非阻塞。以及select方法。

打开串口

在Linux系统下,打开串口是通过使用标准的文件打开函数操作的。

#include <fcntl.h>

/* 以读写的方式打开 */

int fd = open( "/dev/ttyUSB0",O_RDWR);  

设置串口

所有对串口的操作都是通过结构体 struct termios 和 几个函数实现的。

tcgetattr          //获取属性
tcsetattr          //设置属性
cfgetispeed       //得到输入速度
cfsetispeed        //设置输入速度
cfgetospeed        //得到输出速度
cfsetospedd        //设置输出速度
tcdrain             //等待所有输出都被传输
tcflow              //挂起传输或接收
tcflush             //刷清未决输入和输出
tcsendbreak        //送break字符
tcgetpgrp          //得到前台进程组ID
tcsetpgrp          //设置前台进程组ID
tcgetattr( 0,&oldstdio);  //获取默认的配置选项 存储到oldstdio结构体中
tcgetattr( fd,&oldstdio);  //获取当前配置选项 存储到oldstdio结构体中
tcsetattr( fd,TCSANOW,&oldstdio);  //TCSANOW 修改立即生效
cfgetispeed( &oldstdio);      //得到波特率
cfsetispeed(&oldstdio, B115200 )    //设置波特率为115200
即可使用read或open来操作串口的发送与接收。  

测试代码:
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>


int serial_send( int fd, char *Data );

int main()
{
    int fd;
    int num;
    struct termios oldstdio;


    fd = open("/dev/ttyUSB0", O_RDWR );
    if( -1==fd )
    {
        printf("cannot open /dev/ttyUSB0
");
        return -1;
    }
    tcgetattr( fd, &oldstdio);
    cfsetispeed(&oldstdio, B115200);
    tcsetattr( fd, TCSANOW, &oldstdio);
    tcflush( fd, TCIFLUSH );

    num = serial_send( fd,"Serial BAUND is default 
" );

    close(fd);
    return 0;
}

int serial_send( int fd, char *Data )
{
    int string_num;
    string_num = strlen(Data);
    return  write( fd,Data, string_num );
}

在没有数据读取的时候,执行read函数会发生阻塞,执行下面的程序,在串口接收端没有数据时,返回0,并不会发生阻塞。

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

const char *Serial_Dev = "/dev/ttyUSB0";

typedef struct {
    char R_flag;
    char W_flag;
    int  len;
    char Data[255];
}Serial;

typedef struct {
    int Forward;
    int left;
    int rotate;
    unsigned char Check;
    char Enter[3];
}Vehicle;


Vehicle Serial_Tx = {0,0,0,0,{"
"}};
Serial Serial_D = {0,0,0,{0}};
int S_fd;


int wait_flag = 0;

int serial_send( int fd, char *Data );
int set_opt(int fd,int nSpeed,int nBits,char nEvent,int nStop);

void * Pthread_Serial( void *arg )
{
    int n=0;
    int ret;
    struct termios oldstdio;
    char Rx_Data[100];
    char Tx_Data[50]={0};
    
    S_fd = open( Serial_Dev, O_RDWR|O_NOCTTY );
    if( -1==S_fd ) 
        pthread_exit(NULL);
    
    ret = set_opt(S_fd,115200,8,'N',1);
    if(ret == -1)
    {
         pthread_exit(NULL);
    }
    
    while(1)
    {        
        ret = read( S_fd, Rx_Data, 100);
        if( ret >0 )
        {
            Serial_D.len = ret;
            memset( Serial_D.Data, 0, Serial_D.len+3 );
            memcpy( Serial_D.Data, Rx_Data, Serial_D.len );        
            printf("%s",Serial_D.Data);
        }
        else
        {
            usleep(100000);
            sprintf( Tx_Data,"send %d
", n++ );
            serial_send( S_fd, Tx_Data );    
            //printf("send ok%d
",n++);    
        }
    }
    pthread_exit(NULL);
}

int main()
{
    pthread_t pthread_id;
    
    //Create a thread
    pthread_create( &pthread_id, NULL, &Pthread_Serial, NULL );
    usleep(1000);

    if( -1==S_fd )
    {
        printf("error: cannot open serial dev
");
        return -1;
    }   
    
    while(1)
    {
        usleep(1000);
        
    }
    
    return 0;
}

int serial_send( int fd, char *Data )
{
    int string_num;
    string_num = strlen(Data);
    return  write( S_fd,Data, string_num );
}

int set_opt(int fd,int nSpeed,int nBits,char nEvent,int nStop)
{
    struct termios newtio,oldtio;
    if(tcgetattr(fd,&oldtio)!=0)
    {
        perror("error:SetupSerial 3
");
        return -1;
    }
    bzero(&newtio,sizeof(newtio));
    //使能串口接收
    newtio.c_cflag |= CLOCAL | CREAD;
    newtio.c_cflag &= ~CSIZE;

    newtio.c_lflag &=~ICANON;//原始模式

    //newtio.c_lflag |=ICANON; //标准模式

    //设置串口数据位
    switch(nBits)
    {
        case 7:
            newtio.c_cflag |= CS7;
            break;
        case 8:
            newtio.c_cflag |=CS8;
            break;
    }
    //设置奇偶校验位
    switch(nEvent)

    {
        case 'O':
            newtio.c_cflag |= PARENB;
            newtio.c_cflag |= PARODD;
            newtio.c_iflag |= (INPCK | ISTRIP);
            break;
        case 'E':
            newtio.c_iflag |= (INPCK | ISTRIP);
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
            break;
        case 'N':
            newtio.c_cflag &=~PARENB;
            break;
    }
    //设置串口波特率
    switch(nSpeed)
    {
        case 2400:
            cfsetispeed(&newtio,B2400);
            cfsetospeed(&newtio,B2400);
            break;
        case 4800:
            cfsetispeed(&newtio,B4800);
            cfsetospeed(&newtio,B4800);
            break;
        case 9600:
            cfsetispeed(&newtio,B9600);
            cfsetospeed(&newtio,B9600);
            break;
        case 115200:
            cfsetispeed(&newtio,B115200);
            cfsetospeed(&newtio,B115200);
            break;
        case 460800:
            cfsetispeed(&newtio,B460800);
            cfsetospeed(&newtio,B460800);
            break;
        default:
            cfsetispeed(&newtio,B9600);
            cfsetospeed(&newtio,B9600);
            break;
    }
    //设置停止位
    if(nStop == 1)
        newtio.c_cflag &= ~CSTOPB;
    else if(nStop == 2)
        newtio.c_cflag |= CSTOPB;
    newtio.c_cc[VTIME] = 1;
    newtio.c_cc[VMIN] = 0;
    tcflush(fd,TCIFLUSH);

    if(tcsetattr(fd,TCSANOW,&newtio)!=0)
    {
        perror("com set error
");
        return -1;
    }
    return 0;
}

 可以使用select函数来判断有没有接收到数据。

int read_datas_tty(int fd,char *rcv_buf,int sec,int usec)
{
    int retval;
    unsigned char tempchar2;
    fd_set rfds;
    struct timeval tv;
    int ret,pos;


    tv.tv_sec = sec;//set the rcv wait time
    tv.tv_usec = usec;//100000us = 0.1s

    while(1)
    {
        FD_ZERO(&rfds);
        FD_SET(fd,&rfds);
        retval = select(fd+1,&rfds,NULL,NULL,&tv);
        if(retval ==-1)
        {
            printf("select error
");
            break;
        }
        else if(retval)
        {
            ret= read(fd,rcv_buf,1);
            tempchar2 = rcv_buf;
            printf("rcv_buf is %s
",rcv_buf);

        }
        else
        {
            break;
        }
    }
    return 1;
}

将上面的函数放到read前面调用即可。

下面是我用在小车上的代码:

#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include "serial.h"

const char *Serial_Dev = "/dev/ttyUSB0";

typedef struct {
    int Forward;
    int left;
    int rotate;
    unsigned char status;
    unsigned char Check;
    char Enter[3];
}Vehicle;

typedef struct {
    int fd;
    int sec;
    int usec;
    Vehicle* Veh;
}Uart;

Vehicle Motor = {0,0,0,0,0,{'
','
',''}};
Uart serial_usb={ -1,0,0,NULL};;

void * Pthread_Serial_Rx( void *arg  )
{
    int fd;
    int retval;
    struct timeval tv;
    fd_set rfds;
    Uart  *ser = (Uart *)arg;
    char Rx_data[100];    
    int len;
    int Num=0;
    
    fd = ser->fd;
    tv.tv_sec = ser->sec;
    tv.tv_usec = ser->usec;

    while(1)
    {
        FD_ZERO(&rfds);
        FD_SET( fd,&rfds );
        retval = select(fd+1,&rfds,NULL,NULL,&tv);
        if( retval == -1 )
        {
            printf("error
");
            break;
        }
        else if( retval)
        {
            len = read(fd,Rx_data,100);
        //    printf("read %d
",len);
            if( (len ==3 )&&( Rx_data[0] == 'S' ) )
            {
                if( Rx_data[1] == '1' )
                    ser->Veh->status = 1;
                else 
                    ser->Veh->status = 2;        
                Num=0;
            }
        }
        else
        {
            usleep(1000);
            Num++;
        }    

        if( Num>100)    
        {
            ser->Veh->status = 0;
            Num=120;
        }    
    }
    pthread_exit(NULL);

}

void * Pthread_Serial( void *arg )
{
    int n=0;
    int fd;
    pthread_t pthread_id;
    
    fd = open( Serial_Dev, O_RDWR|O_NOCTTY );
    if( -1==fd ) 
        pthread_exit(NULL);
    
    if( set_opt(fd,115200,8,'N',1)== -1)
    {
         pthread_exit(NULL);
    }
    serial_usb.fd = fd;
    serial_usb.sec = 0;
    serial_usb.usec = 1;
    serial_usb.Veh = &Motor;
    
    pthread_create( &pthread_id, NULL, &Pthread_Serial_Rx, ( void *)&serial_usb );

    while( 0==pthread_kill(pthread_id,0)  )
    {        
        if(Motor.status)
        {
            Motor.Forward = 0;
            Motor.left = 0;
            Motor.rotate = 0;
            Motor.Check = (unsigned char)(Motor.Forward + Motor.left +    Motor.rotate);
            write( fd, &Motor, 16 );
                //serial_send( fd, "this is ok
" );
        }
        
        usleep(5000);
    }

    printf("receive thread is quited
");
    pthread_exit(NULL);
}

int main()
{
    pthread_t pthread_id;
    
    //Create a thread
    pthread_create( &pthread_id, NULL, &Pthread_Serial, NULL );
    usleep(10000);
    
    if( 0!= pthread_kill(pthread_id,0))
    {
        printf("error: cannot open serial dev
");
        return -1;
    }    
    printf("%d
",sizeof(Vehicle));
    //serial_send( serial_usb.fd, "this is ok
" );
    
    while( 0==pthread_kill(pthread_id,0) )
    {
        usleep(500000);
        if( Motor.status )
            printf("The device is online %d
",Motor.status);
        else
            printf("The device is offline
");
    }
    printf("serial thread is quited
");
    return 0;
}

sd

原文地址:https://www.cnblogs.com/ynxf/p/6105072.html