二、Jetson TX2下的串口通信 (Linux)------程序(采用SIGIO中断)

串口通信需要几个基本的条件:

硬件------串口,这个不用多说,一定都有的

软件------使用的相关的头文件

串口通信的相关的配置基本上博客都是有的,我在这里罗列一下:

它的主要动作是:

一、打开设备

二、设置波特率等等

三、读写函数

四、关闭

具体的可以参考下面这个:

打开串口及串口读写

https://blog.csdn.net/specialshoot/article/details/50707965

https://blog.csdn.net/specialshoot/article/details/50709257

串口主要涉及到termios结构体,这个很重要,可以参考:

下面这个的讲解:

termios的讲解

https://blog.csdn.net/querdaizhi/article/details/7436722

这个需要自己改一下。

但是很多都没有提到完整的接收和发送中断的问题(有的是用进程写的,对我来说有点难度),所以就希望写成中断的方式,希望写一个接收的中断。

具体的文件如下:

#include "serial.hpp"
//using the serial to transmit information
int my_fd;
//char my_buf[30]={"/dev/ttyUSB0"};
char my_buf[30]={"/dev/ttyTHS2"};
int my_nread=0;
int my_nByte=0;
u8 USART_TX_BUF[32];
/**
*@brief 设置串口通信速率
*@param fd 类型 int 打开串口的文件句柄
*@param speed 类型 int 串口速度
*@return void
*/
      int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
                          B38400, B19200, B9600, B4800, B2400, B1200, B300, };
      int name_arr[] = {  38400, 19200, 9600, 4800, 2400, 1200, 300, 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 fd");
            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");
       perror("SetupSerial 2");
       return(SERIAL_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 sizen"); return (SERIAL_FALSE);
     }
     switch (parity)
     {
     case 'n':
     case 'N':
     options.c_cflag &= ~PARENB; /* Clear parity enable */
     options.c_iflag &= ~INPCK; /* Enable parity checking */
     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 parityn");
     return (SERIAL_FALSE);
     }
     /* 设置停止位*/
     switch (stopbits)
     {
     case 1:
     options.c_cflag &= ~CSTOPB;
     break;
     case 2:
     options.c_cflag |= CSTOPB;
     break;
     default:
     fprintf(stderr,"Unsupported stop bitsn");
     return (SERIAL_FALSE);
     }
     /* Set input parity option */
     if (parity != 'n')
     options.c_iflag |= INPCK;
     tcflush(fd,TCIFLUSH);
     options.c_cc[VTIME] = 0; /* 设置超时 0  seconds*/
     options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
     if (tcsetattr(fd,TCSANOW,&options) != 0)
     {
      perror("SetupSerial 3");
     return (SERIAL_FALSE);
     }
     return (SERIAL_TRUE);
}
int OpenDev(char *Dev)
{

      int fd = open( Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY
      if (-1 == fd)
      {
             perror("Can not Open Serial Port");
             return -1;
       }
      else
       return fd;
}
u8 head[2]={'#',0x0d};

void USART_Send(int fd, u8 *buf,int length )
{
   
  write(fd,head,1);
   if(length>0)
   {
   write(fd,buf,length);
   }
   write(fd,&(head[1]),1);//這裏是0a0d,所以如果是'\n'的話,會多出來一個0a
}
void USART_Send_Dis(int mode)
{
   if(d==0)
  mode=MODE_ERROR;
   
   switch(mode)
   {case MODE_1:

    default: break;
    }
}
u8 Buff_Rec[16];

u8 Falg_Send=0;
u8 USART_RX_STA=0;        //接收标志位,前六位为接收个数,
u8 USART_RX_BUF[32];     //第七位为开始标志,第八位为接收完成标志
  
void USART_Rec(u8* Buff_Rec, int length)
{
      std::cout<<"receive-2"< 31)
		            USART_RX_STA = 0;	//超出接收范围
                }
            }
        }
	else if(temp=='#')
        {
	    u8 i;
        USART_RX_STA|=0x40;	
	    for(i = 0; i < 32; i++)			//数组清零
	    USART_RX_BUF[i] = 0x00;
	    }
     }	
	if((USART_RX_STA&0x80)!=0)				
	{
           
	    /*处理的部分,数据已经存在了USART_RX_BUF里面*/
       /*USART_RX_STA清零*/
		USART_RX_STA = 0;
       
	}
     
}

serial.hpp如下:

#ifndef __SERIAL_H
#define __SERIAL_H
#include  /*标准输入输出定义*/
#include  /*标准函数库定义*/
#include  /*Unix 标准函数定义*/
#include /**/
#include 
#include  /*文件控制定义*/
#include  /*PPSIX 终端控制定义*/
#include  /*错误号定义*/
#include 
#include 
#include 
typedef unsigned char u8;
typedef  short int s16;
typedef  unsigned short int u16;
//*struct termio
//{ unsigned short c_iflag; //输入模式标志 
//  unsigned short c_oflag; // 输出模式标志 
//  unsigned short c_cflag; //* 控制模式标志
//  unsigned short c_lflag; //* local mode flags 
//  unsigned char c_line; //* line discipline 
//  unsigned char c_cc[NCC]; //* control characters 
//};*/
#define SERIAL_FALSE -1
#define SERIAL_TRUE   0

//extern u8 Buff_Rec[16];
extern u8 Falg_Send;
//using the serial to transmit information
extern int my_fd;
extern char my_buf[30];//={"/dev/ttyUSB0"};
extern int my_nread;
extern int my_nByte;
extern u8 USART_TX_BUF[32];
extern u8 Buff_Rec[16];
extern float distance_x;
void set_speed(int fd, int speed);
int OpenDev(char *Dev);
int set_Parity(int fd,int databits,int stopbits,int parity);
void USART_Send(int fd, u8 *buf,int length );
void USART_Send_Dis(int mode);
void USART_Rec(u8* Buff_Rec, int length);

#endif

主函数里面调用:

main.cpp

#include "serial.hpp"
#include "stdio.h"
using namespace std;
int main (void )
{
 int main(int argc, char** argv)
 {

        //open the serial
        char *dev; 
        dev=my_buf;
        my_fd  = OpenDev(dev);
        //设置串口接收的中断
	     struct sigaction saio; /*definition of signal axtion */   
         /* install the signal handle before making the device asynchronous*/
         saio.sa_handler = signal_handler_IO;
         sigemptyset(&saio.sa_mask);
         //saio.sa_mask = 0; 必须用sigemptyset函数初始话act结构的sa_mask成员 

         saio.sa_flags = 0;
         saio.sa_restorer = NULL;
         sigaction(SIGIO,&saio,NULL);

         set_speed(my_fd,115200);
         if (set_Parity(my_fd,8,1,'s')  == SERIAL_FALSE)
         {
           printf("Set Parity Errorn");
           exit (0);
         }
         
         
         fcntl(my_fd,F_SETOWN,getpid());
         fcntl(my_fd,F_SETFL,FASYNC);
         char key = 0;
          while (key != 27)
	     {
           for(int i=0;i<2000;i++)
           {;}
              if(serial_receive==1)
           { 
                  int my_num=0;my_num= read(my_fd,Buff_Rec,5);
                  USART_Rec(Buff_Rec,my_num);
                  serial_receive=0;
		          for(int ii=0; ii < 16; ii++)
		          Buff_Rec[ii] = 0;
           }
            if(serial_receive==1)
           { 
                  int my_num=0;
                  my_num= read(my_fd,Buff_Rec,4);
                  USART_Rec(Buff_Rec,my_num);
                  serial_receive=0;
           }
           serial_send=0;
         }
 return 0;

}

/******************************************
 信号处理函数,设备wait_flag=FASLE
 ******************************************************/
void signal_handler_IO(int status)
{
    if(serial_send==1)
      {
           serial_send=0;
      }
    else
      {    serial_receive=1;
      }
 
}

我参考的链接是下面的:

https://blog.csdn.net/lin111000713/article/details/26083307

但是有一个问题是,我使用的是SIGIO中断,即我的发送和接收都会引起这个中断,这不是我希望的。

所以我在里面加了两个标志位,来加以区分,分别是serial_send和serial_receive

但是这个时候还是会出现问题,运行之后程序还是会卡死,发现是逻辑的问题,即每次只会发送一个字节,导致中断,然后接收,所以就将在serial.cpp里面的串口发送修改如下

void USART_Send(int fd, u8 *buf,int length )
{
   tcdrain(my_fd);
   write(fd,head,1);
   
   if(length>0)
   {
   write(fd,buf,length);
   tcdrain(my_fd);
   }
   write(fd,&(head[1]),1);
   tcdrain(my_fd);
}

即添加了tcdrain(int fd)函数,它的意义是,让调用程序一直等待,直到所有排队的输出都发送完毕。

这个时候就好了,其实这个是模拟单片机的做法,就是等待上一次发送的完成。

另外注意的是:

1、设备要修改为自己的文件名,一般是   /dev/ttyUSB0

2、我的发送和接收都是格式的,开头发一个'#', 结尾发一个'\n'  (接受的是开头'#', 结尾0x0a), 好像是默认将'0x0d' 转换为 ‘0x0a’了

3、其实还有其他的方式可以实现这个过程比如更改中断的类型,不使用SIGIO, 但是没有尝试。

4、这个是删减过的,可能会报错(主要是main部分被我删了),所以需要大家自己改一下。

5、自己接下来想尝试的,主要是阻塞模式,不知道是不是跟串口在中断里面接收卡死有关,然后是通过线程去写的(虽然表示不会)

如果没有问题的话,大家就不用往下看了,但是如果还是有问题,就请往下看,仅作参考

3-29 更新:

之后的程序还是出现了问题,主要是以下的问题:

一、我设置的波特率是115200, 但是serial.cpp里面并没有这个。(难受,当时复制的别人的代码,没有看清。)所以需要在数组里面添加。

  int speed_arr[] = {  B115200   B38400, B19200, B9600, B4800, B2400, B1200, B300,
                       B115200   B38400, B19200, B9600, B4800, B2400, B1200, B300, };
      int name_arr[] = { 115200, 38400, 19200, 9600, 4800, 2400, 1200, 300, 
                         115200, 38400, 19200, 9600, 4800, 2400, 1200, 300, };

二、发送有的数据,是可以接收到的,但是有的数据,是接受不到的,比如0x03.

 

后来发现,它是c_lflag下面的中断信号,termios是默认使用这些东西的,所以当发送0x03的时候,什么都没有收到,我们需要禁用这个数据的。

termois options

options.c_flag=~ISIG

 

4-15更新----------------------------------------------

三、上面问题解决以后,发现当运行一段时间以后,程序是会自动的卡死的。但是当我们打开minicom之后,保存设置然后退出,(Ctrl+A  Z  Q),就一切正常。

试了很多的方法都没有解决问题,后来查到有一篇博客上说的是如果出现丢包,那么是会出现这种情况的,需要使用非标准模式

也就是:

options.c_flag=~ICANON

在此使用串口,发现一切就正常了。

悲伤,几个问题折腾了好几天

4-29更新------------------------------------------------

四、上述三的问题是很短的时间内就卡死了,但是后来出现运行一二十分钟就卡死,。。。

后来参考这个博客,修改参数,发现又可以了。

总的思路,就是:

(一)运行程序,发现不行,

(二)打开串口调试助手minicom ,  然后保存设置 关闭, 运行程序,一切正常,

就证明两次(minicom  和我自己的程序)的串口参数设置是不一样的,查看两者差别,修改,就可以了。

查看命令是:

cat /proc/tty/driver/serial

其中的serial替换为自己的串口设备名,大致是这个命令,因为是之前用的,这个命令是写博客的时候现查的,所以没有实践过,有错误的话希望大家提示下。

不过思路就是这样。

你可能感兴趣的:(Linux)