在这篇文章()中,实现了Linux环境下的串口读写操作,程序也运行成功了。但是再进一步测试时发现,如果开机之后直接如上文中所说,分别运行读程序和写程序,再用导体同时触碰串口的2、3针的话。此时将显示写入串口成功,但是读串口数据失败。

这个奇怪的问题当时之所以没有被发现出来,是因为在这之前,曾经打开过一次minicom。后来实验表明,如果打开过一次minicom,哪怕打开又关闭的话,再次运行上文中的串口读写程序就没有问题了。但是重启机器之后,错误又出现了:只要不运行minicom一下,程序读取总是会有问题。

为了查找错误究竟是在什么地方,分别在刚刚开机、运行过一次自己编写的串口程序、运行过一次minicom这三种情况下使用命令 stty -a < /dev/ttyS0查看了COM1的相关参数。然后主要根据自己的读取程序和minicom对串口的设置差异进行了相应的修改,现将读取程序的全部贴在下面。经过修改后,该程序运行之后的/dev/ttyS0的环境参数与直接运行minicom后/dev/ttyS0的环境参数完全相同。

注:程序中红色部分是与 文中的程序相比加入的主要内容。

/*********************************** read_serial ************************************/ 
 
 #include <stdio.h> 
 
 #include <sys/types.h> 
 
 #include <sys/stat.h> 
 
 #include <fcntl.h> 
 
 #include <termios.h> 
 
 #include <time.h> 
 
 #include <stdlib.h> 
 

 #define FALSE -1 
 
 #define TRUE   0 
 

 void set_speed(int, int); 
 
 int set_Parity(int,int,int,int); 
 
 int main() 
 
 { 
 
     int fd,flag,rd_num=0; 
 
     struct termios term; 
 
     struct timeval timeout; 
 
     speed_t baud_rate_i,baud_rate_o; 
 
     char recv_buf[20]; 
 
     fd=open("/dev/ttyS0",O_RDWR|O_NONBLOCK); 
 
     if(fd==-1) 
 
         printf("can not open the COM1!\n"); 
 
     else 
 
         printf("open COM1 ok!\n"); 
 

     flag=tcgetattr(fd,&term); 
 
     baud_rate_i=cfgetispeed(&term); 
 
     baud_rate_o=cfgetospeed(&term); 
 
     printf("设置之前的输入波特率是%d,输出波特率是%d\n",baud_rate_i,baud_rate_o); 
 

     set_speed(fd,1200); 
 

     flag=tcgetattr(fd,&term); 
 
     baud_rate_i=cfgetispeed(&term); 
 
     baud_rate_o=cfgetospeed(&term); 
 
     printf("设置之后的输入波特率是%d,输出波特率是%d\n",baud_rate_i,baud_rate_o); 
 

    if (set_Parity(fd,8,1,'N')== FALSE) 
 
     { 
 
         printf("Set Parity Error\n"); 
 
         exit(1); 
 
     } 
 
      
 
     int transfer_started=0; 
 
     int i=0; 
 
     while(1) 
 
     { 
 
         rd_num=read(fd,recv_buf,sizeof(recv_buf)); 
 
         timeout.tv_sec=0; 
 
         timeout.tv_usec=200000; 
 
         if(rd_num>0) 
 
         { 
 
             printf("%d(间隔%4.3fs):we can read \"%s\" from the COM1,total:%d characters.\n",++i,timeout.tv_sec+timeout.tv_usec*0.000001,recv_buf,rd_num); 
 
             transfer_started=1;  
 
         } 
 
         else 
 
             printf("%d(间隔%4.3fs):read fail! rd_num=%d。本次数据传输%s\n",++i,timeout.tv_sec+timeout.tv_usec*0.000001,rd_num,transfer_started==1?"已经结束":"尚未开始");  
 
              
 
 //        sleep(1);   粗糙定时 
 
         select(0,NULL,NULL,NULL,&timeout);/*精确定时*/ 
 
     } 
 
 } 
 

     int speed_arr[] = {B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300}; 
 
     int name_arr[] = {115200, 38400, 19200, 9600, 4800, 2400, 1200, 300}; 
 
     void set_speed(int fd, int speed){ 
 
     unsigned int   i;  
 
     int   status;  
 
     struct termios   Opt; 
 
     tcgetattr(fd, &Opt);  
 
     for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) {  
 
     if (speed == name_arr[i]) {      
 
         tcflush(fd, TCIOFLUSH);      
 
         cfsetispeed(&Opt, speed_arr[i]);  
 
         cfsetospeed(&Opt, speed_arr[i]);    
 
         status = tcsetattr(fd, TCSANOW, &Opt);  
 
     if (status != 0) {     
 
             perror("tcsetattr fd1");  
 
             return;      
 
         }     
 
         tcflush(fd,TCIOFLUSH);    
 
         }  
 
         } 
 
         } 
 

 /** 
 
 *@brief   设置串口数据位,停止位和效验位 
 
 *@param fd     类型 int 打开的串口文件句柄* 
 
 *@param databits 类型 int 数据位   取值 为 7 或者8* 
 
 *@param stopbits 类型 int 停止位   取值为 1 或者2* 
 
 *@param parity 类型 int 效验类型 取值为N,E,O,,S 
 
 */ 
 
 int set_Parity(int fd,int databits,int stopbits,int parity) 
 
 { 
 
     struct termios options; 
 
 if ( tcgetattr( fd,&options) != 0) 
 
 { 
 
     perror("SetupSerial 1"); 
 
     return(FALSE); 
 
 } 
 
 options.c_cflag &= ~CSIZE; 
 
 switch (databits) /*设置数据位数*/ 
 
 { 
 
     case 7: 
 
         options.c_cflag |= CS7; 
 
         break; 
 
     case 8: 
 
         options.c_cflag |= CS8; 
 
         break; 
 
     default: 
 
         fprintf(stderr,"Unsupported data size\n"); 
 
         return (FALSE); 
 
     } 
 
 switch (parity) 
 
     { 
 
     case 'n': 
 
     case 'N': 
 
 //        options.c_cflag &= ~PARENB;   /* Clear parity enable */ 
 
 //        options.c_iflag &= ~INPCK;     /* Enable parity checking */ 
 
         options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/ 
 
         options.c_oflag &= ~OPOST;   /*Output*/ 
 
         break; 
 
     case 'o': 
 
     case 'O': 
 
         options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/  
 
         options.c_iflag |= INPCK;             /* Disnable parity checking */ 
 
         break; 
 
     case 'e': 
 
     case 'E': 
 
         options.c_cflag |= PARENB;     /* Enable parity */ 
 
         options.c_cflag &= ~PARODD;   /* 转换为偶效验*/  
 
         options.c_iflag |= INPCK;       /* Disnable parity checking */ 
 
         break; 
 
     case 'S': 
 
     case 's': /*as no parity*/ 
 
         options.c_cflag &= ~PARENB; 
 
         options.c_cflag &= ~CSTOPB; 
 
         break; 
 
     default: 
 
         fprintf(stderr,"Unsupported parity\n"); 
 
         return (FALSE); 
 
         } 
 
 /* 设置停止位*/    
 
 switch (stopbits) 
 
     { 
 
     case 1: 
 
         options.c_cflag &= ~CSTOPB; 
 
         break; 
 
     case 2: 
 
         options.c_cflag |= CSTOPB; 
 
         break; 
 
     default: 
 
         fprintf(stderr,"Unsupported stop bits\n"); 
 
         return (FALSE); 
 
     } 
 
 /* Set input parity option */ 
 
 if ((parity != 'n')&&(parity != 'N')) 
 
         options.c_iflag |= INPCK; 
 

     options.c_cc[VTIME] = 5; // 0.5 seconds 
 
     options.c_cc[VMIN] = 1; 
 

    options.c_cflag &= ~HUPCL;
     options.c_iflag &= ~INPCK;
     options.c_iflag |= IGNBRK;
     options.c_iflag &= ~ICRNL;
     options.c_iflag &= ~IXON;
     options.c_lflag &= ~IEXTEN;
     options.c_lflag &= ~ECHOK;
     options.c_lflag &= ~ECHOCTL;
     options.c_lflag &= ~ECHOKE;
     options.c_oflag &= ~ONLCR; 
 
      
 
 tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */ 
 
 if (tcsetattr(fd,TCSANOW,&options) != 0) 
 
     { 
 
         perror("SetupSerial 3"); 
 
         return (FALSE); 
 
     } 
 
      
 
 return (TRUE); 
 
 }





这样的话,读程序就可以直接接收到串口过来的数据,而不需要先运行一次minicom了。