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