问题来源
仪表作为从机时,需要分析仪表的响应速度。但发现采用linux或windows开发时发现串口响应较慢。在网上没有查到相关的资料,于是在手上的linux产品上进行试验。(iMAX6Q)
试验一 、采用linux文件读写方式测试响应速度
测试代码
#include “testfuncrun.h”
#ifdef D38_board
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <termios.h>
#include <linux/serial.h>
#define PortName “ttymxc2”
#endif
#include “SerialTool/SerialTool.h”
TestFuncRun::TestFuncRun()
{
m_toStop = false;
}
TestFuncRun::~TestFuncRun()
{
qDebug()<<"~TestFuncRun()";
m_toStop = true;
wait();
}
void TestFuncRun::stop()
{
m_toStop = true;
}
#ifdef D38_board
int rs485_enable(int fd,int enable)
{
struct serial_rs485 rs485conf;
int res;
res=ioctl(fd,TIOCGRS485,&rs485conf);
if(res<0){
printf(“rs485 config read fail \n”);
return res;
}
if(enable){
rs485conf.flags |=SER_RS485_ENABLED;
rs485conf.flags |=SER_RS485_RTS_AFTER_SEND;
}
rs485conf.delay_rts_before_send=0x0004;
res=ioctl(fd,TIOCSRS485,&rs485conf);
if(res<0){
printf("rs485 confing set fail\n");
}
return res;
}
int uartdev_init(int fd)
{
struct termios newtty,oldtty;
if(tcgetattr(fd,&oldtty)!=0){
printf(“tcgetattr fail \n”);
return -1;
}
bzero(&newtty,sizeof(newtty));
newtty.c_cflag|=(CLOCAL|CREAD);
newtty.c_cflag&=~CSIZE;
#if 0
switch(bit){
case 7:
newtty.c_cflag |=CS7;
break;
case 8:
newtty.c_cflag |=CS8;
break;
default:
newtty.c_cflag |=CS8;
}
#endif
newtty.c_cflag |=CS8;
newtty.c_cflag &=~PARENB;
cfsetispeed(&newtty,B19200);
cfsetospeed(&newtty,B19200);
newtty.c_cflag &=~CSTOPB;
// if(havecrtscts==1){
// newtty.c_cflag |=CRTSCTS;
// }
newtty.c_cc[VTIME] = 0;
newtty.c_cc[VMIN] = 0;
tcflush(fd ,TCIFLUSH);
if((tcsetattr( fd, TCSANOW,&newtty))!=0)
{
perror("com set error");
return -1;
}
return 0;
}
void TestFuncRun::run()
{
//QSerialPort serial;
int count=0;
bool serialOK = false;
int ret;
//serialOK = SerialTool::SerialOpen(&serial,m_PortName,“9600”,“8”,“无校验”,“1”);
// qDebug()<<“serialOK = “<<serialOK;
int gfd = open(”/dev/ttymxc2”,O_RDWR|O_NOCTTY);
ret = uartdev_init(gfd);
ret = rs485_enable(gfd,1);
if(ret != 0){
serialOK = false;;
}
else
{
serialOK = true;
}
qDebug()<<"serialOK = "<<serialOK;
char readbuff[11];
char cmpbuff[11];
char cmpbuff2[10]={'a','b','c','d','e','a','b','c','d','e'};
memset(cmpbuff,0,11);
while(m_toStop == false)
{
if(serialOK == false)
{
msleep(1000);
}
else
{
memset(readbuff,0,11);
//printf("uart readloop\n");
ret=read(gfd,readbuff,10);
readbuff[10]=0;
if(ret !=0)
{
// printf("read buff:%s\n",readbuff);
}
if(strcmp(readbuff,"abcdeabcde")!=0 &&strcmp(readbuff,cmpbuff)!=0){
printf("!!!!!!!!wrong val\n");
printf("read buff:%s\n",readbuff);
}
//usleep(10*1000);
if(strcmp(readbuff,"abcdeabcde")==0){
write(gfd,"recive abcde",12);
}
else
{
if(ret !=0)
{
write(gfd,"recive fail",12);
}
}
}
//qDebug()<<"count = "<<count;
count++;
if(count>999999)
{
count = 0;
}
}
if(serialOK == true)
{
close(gfd);
}
qDebug()<<"end of run()";
}
#else
void TestFuncRun::run()
{
int count=0;
while(m_toStop == false)
{
qDebug()<<"count = "<<count;
count++;
if(count>999999)
{
count = 0;
}
msleep(1000);
}
qDebug()<<"end of run()";
}
#endif
电脑发送内容:abcdeabcde(发送个数10)
波特率为115200时,响应时间为3ms
波特率为19200时,响应时间为20ms
波特率为9600时,响应时间为40ms
波特率为2400时,响应时间为160ms
电脑发送内容:abcdeabcdeabcdeabcde(发送个数20)
波特率为115200时,响应时间为3ms–
波特率为19200时,响应时间为20ms
电脑发送内容:abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde(发送个数100)
波特率为115200时,响应时间为3ms
波特率为9600时,响应时间为40ms
小结:linux串口就算收到数据,但反馈到应用层还需一定的时间,他的反映时间原理猜想如下:
当收到数据若n时间没有再收到数据,则向应用层反馈有数据收到。
时间n与接收字节数无关.而只与波特率有关.
注:偶尔也会收到一半就提前响应。比如电脑向仪表连续发送100个字节,结果仪表接收了70个字节就响应了有字节收到了。
第二项试验,验证Qt的串口函数是否有影响(serial.read(100))
void TestFuncRun::run()
{
QSerialPort serial;
int count=0;
bool serialOK = false;
int ret;
serialOK = SerialTool::SerialOpen(&serial,"/dev/ttymxc2",“9600”,“8”,“无校验”,“1”);
qDebug()<<"serialOK = "<<serialOK;
QByteArray readArr;
while(m_toStop == false)
{
if(serialOK == false)
{
msleep(1000);
}
else
{
//printf(“uart readloop\n”);
readArr.clear();
readArr = serial.read(100);
qApp->processEvents();
if(readArr.count() !=0)
{
serial.clearError();
serial.write(readArr);
}
}
//qDebug()<<"count = "<<count;
count++;
if(count>999999)
{
count = 0;
}
}
if(serialOK == true)
{
serial.close();
}
qDebug()<<"end of run()";
}
测试情况:
电脑发送内容:0123456789(发送个数10)
波特率为9600时,响应时间为40ms
电脑发送内容:0123456789。。(发送个数100)
波特率为9600时,响应时间为40ms
电脑发送内容:0123456789。。(发送个数100)
波特率为115200时,响应时间为3ms
1、同时没有出现过电脑连续发送100个字节,结果70个就反馈有数据的情况;
2、注:readArr = serial.read(100);虽说写了最大100个字节,而实际是全部接收,比如电脑连接发送200个字节给仪表,此时readArr就是接收了200个字节.
3、物理串口接收完毕至应用层响应时间完全同liunx 原始串口调用的响应时间
第三项试验,验证Qt的串口函数是否有影响(waitForReadyRead)
void TestFuncRun::run()
{
QSerialPort serial;
int count=0;
bool serialOK = false;
int ret;
serialOK = SerialTool::SerialOpen(&serial,"/dev/ttymxc2",“115200”,“8”,“无校验”,“1”);
qDebug()<<"serialOK = "<<serialOK;
QByteArray readArr;
while(m_toStop == false)
{
if(serialOK == false)
{
msleep(1000);
}
else
{
//printf(“uart readloop\n”);
readArr.clear();
if(serial.waitForReadyRead(200)) {
qApp->processEvents();
readArr = serial.readAll();
if(readArr.count() !=0)
{
serial.clearError();
serial.write(readArr);
}
}
}
//qDebug()<<"count = "<<count;
count++;
if(count>999999)
{
count = 0;
}
}
if(serialOK == true)
{
serial.close();
}
qDebug()<<"end of run()";
}
测试:
电脑发送内容:0123456789。。(发送个数100)
波特率为9600时,响应时间为40ms
电脑发送内容:0123456789。。(发送个数100)
波特率为115200时,响应时间为3ms
电脑发送内容:0123456789。。(发送个数200)
波特率为115200时,响应时间为3ms
1、同时没有出现过电脑连续发送100个字节,结果70个就反馈有数据的情况;
2、物理串口接收完毕至应用层响应时间完全同liunx 原始串口调用的响应时间
实验四、设置接收缓冲,看是否响应有无影响
void TestFuncRun::run()
{
QSerialPort serial;
int count=0;
bool serialOK = false;
int ret;
serialOK = SerialTool::SerialOpen(&serial,"/dev/ttymxc2",“9600”,“8”,“无校验”,“1”);
qDebug()<<"serialOK = "<<serialOK;
QByteArray readArr;
serial.setReadBufferSize(50);
while(m_toStop == false)
{
if(serialOK == false)
{
msleep(1000);
}
else
{
//printf(“uart readloop\n”);
readArr.clear();
if(serial.waitForReadyRead(200)) {
qApp->processEvents();
readArr = serial.readAll();
if(readArr.count() !=0)
{
serial.clearError();
serial.write(readArr);
}
}
}
//qDebug()<<"count = "<<count;
count++;
if(count>999999)
{
count = 0;
}
}
if(serialOK == true)
{
serial.close();
}
qDebug()<<"end of run()";
}
测试:
电脑发送内容:0123456789。。(发送个数10)
波特率为9600时,响应时间为40ms
电脑发送内容:0123456789。。(发送个数40)
波特率为9600时,响应时间为40ms
电脑发送内容:0123456789。。(发送个数50)
波特率为9600时,响应时间为40ms
电脑发送内容:0123456789。。(发送个数60)
波特率为9600时,响应时间为40ms,且60个数据都接收正常。
小结:(serial.setReadBufferSize(50);)设置接收缓存没有起到缩短串口响应的作用,当电脑向仪表连续发送60个字节时,仪表也是能正常接收,即说明这个设置缓存没有什么作用.
总结:
linux物理接收完数据(内核)至应用层响应(指示有数据接收)的时间只受波特率有关。(与接收缓存大小、调用Qt函数还是底层的文件方式都 没有关系)。------这一点不如单片机的实时性高。
注:以上结论属在imax6Q的测试结果.
1、经咨询供应商后的处理方法:通过更改驱动中寄存器的值,最多可以减少一半的时间,如原9600bps下由40ms变为20ms。同时供应商说liunx实时性差,很多设备采用外扩个单片机来解决这个问题。建议用集成了M4核的系统,由于项目时间问题,此问题暂时放下。
2、Qt5.6-linux串口采用调用时,存在内核串口已有数据,但应用层仍没有收到 数据的,导致两个指令包一起接收的情况。若采用Qt5.9-win则不存在这个问题。
解决方法是采用linux的“open”方式操作串口。缺点是:校验方式上没有搞定mark和space。由此可见Qt在串口处理上还需改进,另外延时问题上经测试。这个延时时间"open"方式和是相同的。