开发环境是vs2012,qt5.2.0版本。通过串口通信与步进电机控制器进行交互。步进电机连接两个电机驱动器,步进电机控制器控制两个电机运动,正反转,回零,运行到一定位置,控制输入输出停等基本操作。为了调整镜头和相机的距离,从而调整相机的放大倍率。两个电机只能分时运动,两个命令直接需要加延时,全部停除外。
遇到两个问题,一个是串口开始写数据返回为-1的问题,另一个是由于YL1和YL2输入端功能不单一:导致回零停时或是控制停时,步进电机控制器没有返回数据(用串口调试助手不存在这个问题,我也不清楚原因),没法保证命令是否完成,于是把输入端改成YL3-YL13其中的任何两个就可以。
再说写串口返回为-1的问题,主要以前没有做过串口通信,对过程不是很了解,对qt封装的类构造函数了解不足。其实,qt封装的代码还是调用windows底层的东西。首先,我来申明一件事:网上给的代码有的是有问题的。因为,我是这么做的,初次向串口写数据返回为-1。然后我查阅很多资料,都没有找到问题所在,后来受到一句话启发:串口必须先打开,然后在对他配置才是有效地。然后我仔细研究了源代码,主要是Qt封装的是有一点问题。其实,我们可以迈过这个问题,就是构造函数使用问题。
Win_QextSerialPort();
Win_QextSerialPort(Win_QextSerialPort const& s);
Win_QextSerialPort(const QString & name, QextSerialBase::QueryMode mode = QextSerialBase::Polling);
Win_QextSerialPort(const PortSettings& settings, QextSerialBase::QueryMode mode = QextSerialBase::Polling);
Win_QextSerialPort(const QString & name, const PortSettings& settings, QextSerialBase::QueryMode mode = QextSerialBase::Polling);
这五个构造函数第三个比较常用,我开始写得是用的第5个,然后并没又再次对PortSettings进行设置,所以写数据会返回为-1.
来看看端口的open函数:
bool Win_QextSerialPort::open(OpenMode mode) { unsigned long confSize = sizeof(COMMCONFIG); Win_CommConfig.dwSize = confSize; DWORD dwFlagsAndAttributes = 0; if (queryMode() == QextSerialBase::EventDriven) dwFlagsAndAttributes += FILE_FLAG_OVERLAPPED; LOCK_MUTEX(); if (mode == QIODevice::NotOpen) return isOpen(); if (!isOpen()) { /*open the port*/ Win_Handle=CreateFileA(port.toAscii(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL); if (Win_Handle!=INVALID_HANDLE_VALUE) { /*configure port settings*/ GetCommConfig(Win_Handle, &Win_CommConfig, &confSize); GetCommState(Win_Handle, &(Win_CommConfig.dcb)); /*set up parameters*/ Win_CommConfig.dcb.fBinary=TRUE; Win_CommConfig.dcb.fInX=FALSE; Win_CommConfig.dcb.fOutX=FALSE; Win_CommConfig.dcb.fAbortOnError=FALSE; Win_CommConfig.dcb.fNull=FALSE; setBaudRate(Settings.BaudRate); setDataBits(Settings.DataBits); setStopBits(Settings.StopBits); setParity(Settings.Parity); setFlowControl(Settings.FlowControl); setTimeout(Settings.Timeout_Millisec); SetCommConfig(Win_Handle, &Win_CommConfig, sizeof(COMMCONFIG)); //init event driven approach if (queryMode() == QextSerialBase::EventDriven) { Win_CommTimeouts.ReadIntervalTimeout = MAXDWORD; Win_CommTimeouts.ReadTotalTimeoutMultiplier = 0; Win_CommTimeouts.ReadTotalTimeoutConstant = 0; Win_CommTimeouts.WriteTotalTimeoutMultiplier = 0; Win_CommTimeouts.WriteTotalTimeoutConstant = 0; SetCommTimeouts(Win_Handle, &Win_CommTimeouts); if (!SetCommMask( Win_Handle, EV_TXEMPTY | EV_RXCHAR | EV_DSR)) { qWarning("Failed to set Comm Mask. Error code: %ld", GetLastError()); UNLOCK_MUTEX(); return false; } overlapThread->start(); } QIODevice::open(mode); } } else { UNLOCK_MUTEX(); return false; } UNLOCK_MUTEX(); return isOpen(); }
而其中的:
setBaudRate(Settings.BaudRate);
setDataBits(Settings.DataBits);
setStopBits(Settings.StopBits);
setParity(Settings.Parity);
setFlowControl(Settings.FlowControl);
setTimeout(Settings.Timeout_Millisec);
这些函数只有在端口打开的时候才会设置有效的,具体自己看代码。 再看构造函数里面,if (!isOpen()) {}不打开又进行设置,所以说打开函数并不会PortSettings设置成功,所以在打开之后我们要单独对PortSettings进行设置。
文件下载地址:http://sourceforge.net/projects/qextserialport/files/
正确的写法是,以事件驱动的为例:
myCom = new Win_QextSerialPort(“COM1″,QextSerialBase::EventDriven);
//定义串口对象,指定串口名和查询模式,这里使用事件驱动EventDriven
myCom ->open(QIODevice::ReadWrite);
//以读写方式打开串口
myCom->setBaudRate(BAUD9600);
//波特率设置,我们设置为9600
myCom->setDataBits(DATA_8);
//数据位设置,我们设置为8位数据位
myCom->setParity(PAR_NONE);
//奇偶校验设置,我们设置为无校验
myCom->setStopBits(STOP_1);
//停止位设置,我们设置为1位停止位
myCom->setFlowControl(FLOW_OFF);
//数据流控制设置,我们设置为无数据流控制
myCom->setTimeout(500);
//延时设置,我们设置为延时500ms,这个在Windows下好像不起作用
connect(myCom,SIGNAL(readyRead()),this,SLOT(readMyCom()));
//信号和槽函数关联,当串口缓冲区有数据时,进行读串口操作