問題來源
儀表作為從機時,需要分析儀表的響應速度。但發現采用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"方式和是相同的。