本篇介绍了如何在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