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\r\n");
        return -1;
    }
    tcgetattr( fd, &oldstdio);
    cfsetispeed(&oldstdio, B115200);
    tcsetattr( fd, TCSANOW, &oldstdio);
    tcflush( fd, TCIFLUSH );

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

    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,{"\r\n"}};
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\r\n", n++ );
            serial_send( S_fd, Tx_Data );    
            //printf("send ok%d\r\n",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\r\n");
        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\n");
        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\n");
        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\r\n");
            break;
        }
        else if(retval)
        {
            ret= read(fd,rcv_buf,1);
            tempchar2 = rcv_buf;
            printf("rcv_buf is %s\n",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,{'\r','\n','\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\r\n");
            break;
        }
        else if( retval)
        {
            len = read(fd,Rx_data,100);
        //    printf("read %d\r\n",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\r\n" );
        }
        
        usleep(5000);
    }

    printf("receive thread is quited\r\n");
    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\r\n");
        return -1;
    }    
    printf("%d\r\n",sizeof(Vehicle));
    //serial_send( serial_usb.fd, "this is ok\r\n" );
    
    while( 0==pthread_kill(pthread_id,0) )
    {
        usleep(500000);
        if( Motor.status )
            printf("The device is online %d\r\n",Motor.status);
        else
            printf("The device is offline\r\n");
    }
    printf("serial thread is quited\r\n");
    return 0;
}

sd

转载于:https://www.cnblogs.com/ynxf/p/6105072.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux系统下串口编程指的是在Linux操作系统中使用程序来控制串口设备,通常可以用于与串口设备进行通信。 要在Linux系统中进行串口编程,需要使用特定的API(应用程序编程接口)。常用的API包括POSIX的termios库和Linux专有的serial API。 使用termios库的步骤如下: 1. 使用open函数打开串口设备文件,例如"/dev/ttyS0"。 2. 使用tcgetattr函数获取串口的属性。 3. 使用修改串口属性的函数(如cfsetispeed和cfsetospeed)设置串口的输入/输出波特率。 4. 使用tcsetattr函数将修改后的属性设置回串口。 5. 使用read和write函数进行串口的数据读写。 6. 使用close函数关闭串口。 使用serial API的步骤如下: 1. 使用open函数打开串口设备文件,例如"/dev/ttyS0"。 2. 使用ioctl函数设置串口的属性,包括波特率、数据位、停止位和校验方式等。 3. 使用read和write函数进行串口的数据读写。 4. 使用close函数关闭串口。 在编写串口程序时,还需要注意其他问题,如确保串口设备文件有足够的权限访问、处理串口通信中 ### 回答2: Linux串口编程是通过使用系统调用函数来实现的。在Linux系统中,串口设备可以被看作是一种特殊的文件,通过打开和操作该文件,我们可以进行串口通信操作。 在Linux中,常用的串口编程函数是open、close、read、write和ioctl等。我们首先需要使用open函数来打开串口设备文件,例如/dev/ttyS0。打开成功后,我们可以使用read函数从串口读取数据,使用write函数向串口写入数据,使用close函数关闭串口设备。 除了基本的读写操作,我们还可以使用ioctl函数来设置和获取串口的属性。通过设置串口的波特率、数据位、校验位、停止位等参数,我们可以实现不同的串口通信需求。一般情况下,我们需要使用termios结构体来保存和配置串口属性,然后通过ioctl函数来设置。 另外,Linux下还提供了一种轮询和一种中断的方式来进行串口通信。轮询方式是通过不断地查询串口是否有数据到达,然后进行读取。中断方式是通过设置串口的中断响应函数,当有数据到达时,会触发中断并执行相应的处理函数。 总之,Linux串口编程是一种通过打开、读写和ioctl等系统调用函数来操作串口设备文件的方法。通过设置串口属性,我们可以实现不同的串口通信需求。 ### 回答3: Linux下的串口编程是指在Linux操作系统下对串行端口(串口)进行数据输入输出的一种编程技术。串口编程通常用于连接外部设备与计算机之间进行数据交互。 在Linux下,串口设备被视作特殊的文件,通过打开和读写这些特殊文件来对串口进行操作。常用的串口编程接口有标准C库的系统调用,如open()、read()、write()等,也可以使用Linux提供的串口库函数,如termios库函数。 串口编程的基本流程如下: 1. 打开串口设备文件,使用open()函数,得到串口文件描述符。 2. 配置串口参数,使用termios库函数,设置波特率、数据位、停止位、奇偶校验等参数。 3. 读取或写入串口数据,使用read()或write()函数进行数据交互。 4. 关闭串口设备,使用close()函数关闭串口文件描述符。 在进行串口编程时,需要注意以下几点: - 波特率、数据位、停止位、奇偶校验等参数需要与外设保持一致。 - 需要使用合适的访问权限打开串口设备文件。 - 需要适当处理读写时可能发生的错误和异常。 - 可以使用非阻塞模式进行读写,以提高程序的响应性。 通过Linux下的串口编程,可以实现与各种外设的通信,如传感器、控制器、无线模块等。它被广泛应用于嵌入式系统、物联网设备、机器人等领域,具有较高的灵活性和扩展性。同时,针对不同需求,还可以使用一些开源的串口通信库,如pyserial、libserial等,简化串口编程的过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值