linux串口传感器处理接收不完整,数据丢失问题分析

简介

因为当前项目需要在一个linux系统下进行串口传感器的收发工作,该串口传感器的收发使用的是字节流专有协议,按照每一个字节的十六进制编码来确定协议数据。按照以往的思路,串口就是最简单的外设的思想,本想着就是一个小case,但没想到在windows下测试的好好的传感器数据到linux系统上就完全变了样子。。
变化的数据

原因分析

经过一番查资料分析,原来linux的串口因为还有作为终端的功能,所以linux下的串口的设置会比windows要丰富不少。而为了保持我们的十六进制数据保持原样的发送过来,必须将linux下的串口设置为原始输入模式,保留串口数据中的所有控制字,避免linux系统对控制字等数据进行转义。
在linux中,控制串口的转义方法等各类控制结构在初始化串口时的结构体options中。在设置校验位,数据长度,停止位的时候,也是这个结构体在起作用,其结构如下:

struct termios {

tcflag_t  c_cflag/* 控制标志*/

tcflag_t  c_iflag;/* 输入标志*/

tcflag_t  c_oflag;/* 输出标志*/

tcflag_t  c_lflag;/* 本地标志*/

tcflag_t  c_cc[NCCS];/* 控制字符*/

};

想做到对这些结构体做到更深入的了解,可以参考另一篇文章:串口属性设置
而为了保持原始输入模式,我们需要控制的是输入标志和本地标志,将控制标志设置为屏蔽各种控制字,然后输入标志设置为屏蔽各种转义,最后控制字段如下:

	options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input LOCAL*/
	options.c_oflag  &= ~OPOST;   /*Output*/
	options.c_iflag &= ~(IXON | IXOFF | IXANY |BRKINT | ICRNL | ISTRIP );

在如此设置完成后,串口终于能够像pc一样正常的输出数据了!
附:完整的串口控制文件uart.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include<unistd.h>
#include <string.h>
#define UART_DEVICE     "/dev/ttyUSB0"
 
#define FALSE  -1
#define TRUE   0


#define REC_LEN 10
int dec; //设定为串口的设备描述符
FILE* fd;//串口设备的文件描述符

/**
*@brief  设置串口通信速率
*@param  fd     类型 int  打开串口的文件句柄
*@param  speed  类型 int  串口速度
*@return  void
*/
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, };
void set_speed(int fd, int speed){
  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 */ 
			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')   
		options.c_iflag |= INPCK; 
	tcflush(fd,TCIFLUSH);
	options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/   
	options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
	options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input LOCAL*/
	options.c_oflag  &= ~OPOST;   /*Output*/
	options.c_iflag &= ~(IXON | IXOFF | IXANY |BRKINT | ICRNL | ISTRIP );
	if (tcsetattr(fd,TCSANOW,&options) != 0)   
	{ 
		perror("SetupSerial 3");   
		return (FALSE);  
	} 
	return (TRUE);  
}

void print_hex(char* chr,int num)
{
	for(int i =0;i<num;i++)
	{
		printf("0x%02x ",*chr);
		chr++;
	}
	printf("\n");
}
int uart_init(void)
{
    printf("Try Open UART...\n");
    dec = open(UART_DEVICE, O_RDWR | O_NONBLOCK);
    if (dec < 0) {
        perror(UART_DEVICE);
        return -1;
    }
    printf("setting param...%d\n",B9600);
    set_speed(dec,9600);
	if (set_Parity(dec,8,1,'N') == FALSE)  {
		printf("Set Parity Error\n");
		return -2;
	}
    tcflush(dec, TCIOFLUSH);
	fd=fdopen(dec,"r+");
	if (fd < 0) {
        perror(UART_DEVICE);
        return -1;
    }
	return 0;
}
int uart_deinit(void)
{
	printf("Close uart..\n");
	close(dec);
	return 0;
}
///获取一次串口传感器数据,如果无数据返回-1 得到数据会返回0并传入数据
int uart_recdata(unsigned char* buf)
{
	int has_read=0;
	int res=0;
	int offset=0;//第一个有效数据帧头
	unsigned char rec_buf[REC_LEN*3]={0};//三倍是为了防止溢出
    tcflush(dec, TCIOFLUSH);//预清理一下缓存试试
	printf("%d",sizeof(rec_buf));
	while(1) {
		//res = fread(rec_buf+has_read,1,11-has_read,fd);
		res = read(dec,rec_buf+offset+has_read,REC_LEN-has_read);
		if(res<=0 && has_read==0)
		{
			return -1;
		}
		else if(res<=0)
		{
			continue;
		}else 
		{
			printf("waiting for data, has read %d\n",res);
			print_hex(rec_buf,11);
			if(has_read+res>=REC_LEN)//说明数据读取完成或溢出
			{
				memcpy(buf,rec_buf+offset,REC_LEN);
				printf("read success\n");
				return 0;
			}else{//读到了部分数据,等待继续拼接
				//拼接时判断数据头是否正确 不正确的话则丢掉部分数据
				has_read+=res;
				if(offset==0)
				{	
					while(rec_buf[offset]!=0x23 && offset<=sizeof(rec_buf))//数据头是0x23
					{
						offset++;
					}
					if(offset!=0)
					{
						has_read-=offset;
						printf("edit offset to %d\n",offset);
					}
				}
			}
		}
	}
}

main.c文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include<unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
    int res;
    unsigned char buf[15];
    if(!uart_init())printf("init success\n");
    while(1) {
        res = uart_recdata(buf);
        if(!res)
        {
            for(int i=0;i<15;i++)
            {
                printf("%02X ",buf[i]);
            }
            printf("\n");
            break;//Debug
        }
    }
 
    uart_deinit();
    return 0;
}
  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现传感器数据的实时接收处理,你可以采用以下步骤: 1. 选择合适的传感器:根据你的需求,选择适合的传感器类型。常见的传感器包括温度传感器、加速度传感器、光传感器等。确保选取的传感器能够提供你所需的数据。 2. 连接传感器:将传感器与计算机或嵌入式设备进行连接。这可以通过串口、USB、蓝牙、Wi-Fi等方式实现。根据传感器的接口和通信协议,选择相应的硬件设备和驱动程序。 3. 接收传感器数据:使用相应的编程语言(如Python)编写代码,以实时接收传感器数据。根据传感器的通信方式,可以使用串口库、USB库、蓝牙库等进行数据接收。确保代码能够持续监听传感器,并实时获取数据。 4. 数据处理分析:一旦接收传感器数据,你可以使用编程语言提供的库和工具对数据进行处理分析。例如,你可以使用NumPy、Pandas等库进行数据处理、统计和可视化。根据你的需求,可以应用滤波、特征提取、模式识别等算法。 5. 实时更新和反馈:根据你的应用场景,将处理后的数据实时更新到相关系统或展示界面。这可以通过网络通信、数据库连接、API调用等方式实现。确保数据能够及时反馈给用户或其他系统。 在实现传感器数据的实时接收处理过程中,需要考虑以下注意事项: - 选择合适的传感器和接口,确保能够满足你的需求。 - 了解传感器数据格式和通信协议,以便正确解析数据。 - 编写可靠的代码,确保数据的实时接收处理不会出现延迟或中断。 - 对于高频率的传感器数据,可能需要进行数据缓存、优化和并行处理,以提高性能。 - 考虑数据的安全性和隐私保护,特别是涉及到敏感信息或个人身份的情况。 以上是一般的指导步骤,具体的实现方式可能因传感器类型、编程语言和应用需求而有所不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值