当前位置: 代码迷 >> 综合 >> 二、Jetson TX2下的串口通信 (Linux)------程序(采用SIGIO中断)
  详细解决方案

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

热度:76   发布时间:2023-11-23 19:20:00.0

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

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

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

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

它的主要动作是:

一、打开设备

二、设置波特率等等

三、读写函数

四、关闭

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

打开串口及串口读写

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_NDELAYif (-1 == fd){perror("Can not Open Serial Port");return -1;}elsereturn 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"<<std::endl;u8 num=0;for(num=0;num<(length);num++){std::cout<<"receive-3"<<std::endl;u8 temp=Buff_Rec[num]; if((USART_RX_STA & 0x40) != 0)		//接收已经开始{std::cout<<(int)temp<<std::endl;if((USART_RX_STA & 0x80) == 0)	//接收未完成{std::cout << (short)USART_RX_STA<<std::endl;if(temp == 0x0a)USART_RX_STA |= 0x80;	//接收完成了else{USART_RX_BUF[USART_RX_STA & 0X3F] = temp;USART_RX_STA++;if((USART_RX_STA & 0X3F) > 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 <stdio.h> /*标准输入输出定义*/
#include <stdlib.h> /*标准函数库定义*/
#include <unistd.h> /*Unix 标准函数定义*/
#include <sys/types.h>/**/
#include <sys/stat.h>
#include <fcntl.h> /*文件控制定义*/
#include <termios.h> /*PPSIX 终端控制定义*/
#include <errno.h> /*错误号定义*/
#include <iostream>
#include <signal.h>
#include <iostream>
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 serialchar *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替换为自己的串口设备名,大致是这个命令,因为是之前用的,这个命令是写博客的时候现查的,所以没有实践过,有错误的话希望大家提示下。

不过思路就是这样。