本文用的是广泛通用的yafeilinux所做的QT串口第三方类。没有看过这个教程的可以先移步。这个教程所提及的串口初始化以及字符串发送我在此就略去不提了。
现在我们从yafeilinux教程中
myCom->write(ui->lineEdit->text().toAscii());
这句串口发送语句展开来讲。
可以看到,Write函数这个参数ui->lineEdit->text().toAscii()实际返回了一个QByteArray值。那么,这个基于QIODevice类的write函数除了能够接受QByteArray作为传入参数,还有其他的重载形式没有呢?来看看QIOdevice.h中对write函数怎么定义的:
qint64 write(const char *data, qint64 len);
qint64 write(const char *data);
inline qint64 write(const QByteArray &data)
{ return write(data.constData(), data.size()); }
可以看到该函数接受一个char型指针作为输入,第二个可选参数为发送数据的长度。
另外还有一个内联函数接受一个QByteArray类的指针,而它只是调用了QByteArray类的constData函数,将这个QByteArray的首地址作为一个const char*返回。
好了,到这里我们对这个Write()函数有了个大概的印象了。现在把它放在一边,我们先来看看在发送数据前对数据的处理方式。
我这里的无线模块使用的是微网高通(北京)公司的WiMi-net,无线模块都大同小异,包括使用特别广泛的zigbee无线模块,它们收发数据都遵循一个特定的数据格式,包括一个统一的帧头(WiMi-net是0xAA);几个命令字(用来确定该帧具体的作用是对模块进行操作还是收发数据等等);一个iAmount存放该帧有效数据的字节数;十六位的CRC校验字;以及自定义的数据位等等。
在前一篇博文中以及介绍了WiMi-net添加CRC校验码的代码,这个Add_Crc接收一个unsigned char[]作为形参输入,为什么不使用char型数组输入的原因是在之后参与计算的过程中char型字符所带的符号位会造成最终的CRC返回结果出错。
那么来看下数据的转换以及发送代码吧~:
QString str;
QList<QString> SerialSendlist;
SerialSendlist << ui->IDLine->text()<<","
<<ui->XLine->text()<<","
<<ui->YLine->text()<<","
<<ui->HLine->text()<<","
<<ui->FLine->text()<<","
<<ui->BLine->text()<<","
<<ui->ALine->text()<<","
<<ui->RPLine->text()<<","
<<ui->RLLine->text();
unsigned char Test[64] = {0XAA, 0X1D, 0XFF, 0XFF, 0X03, 0X01};//帧头
for(int i = 0; i < 17; i++){ //QString转char*
str.append(SerialSendlist.at(i));
}
for(int i = 0; i < str.length(); i++){
Test[i+9] = *qPrintable(str.mid(i,1));
}
Test[6] = str.length();
if(Add_Crc(Test)){
myCom->write((char*)Test);
}
首先我们使用一个QList<QString>取出了我们需要发送的QString类型的数据,然后存放在一个QString中,并以","隔开。
之后我们定义一个unsigned char型数组,里面预先写入了固定的同步字符,以广播形式发送数据等操作字符。
然后我们利用循环以及宏函数qPrintable将str中的每个字符以其十六进制存入Test[]中。Test[6]中存放的是数据长度。
写串口数据,最简单的一句,myCom->write((char*)Test);却花了我最多的功夫。。。
因为当时天真的认为unsigned char*强制转换到char*的会丢失数据,所以引入了一个QByteArray类的中间变量。
发送代码类似
if(Add_Crc(Test)){
for(int i = 0; i < str.length() + 0X09; i++){
SendStr.append(Test[i]);
}
myCom->write(SendStr);
但是这样做会有一个特别奇怪的问题,即,这段代码在PC机上向外发送数据没有问题,而开发板的linux端发出来的数据会将这些十六进制数当做字符串转成ASCii码再发一遍。。。
Written data下面是PC机发出的数据,这种帧是可以被无线模块识别并发送出去的。而Read data下面的数据则是开发板发出来的数据,它在正常帧的数据前面加了一长串,就是将这帧十六进制当做字符串转成的ASCii码。
这个问题我在贴吧里面提出并进行了讨论(详见http://tieba.baidu.com/p/2719228495)但没有得到一个理想的答案,如果有知道这个缘由的朋友还望指点一二~鄙人感激不尽~
之后偶然看到一篇帖子中关于unsigned char与char转换的讨论,才想到,当作为ASCII码发送送时,unsigned char 与char 是没有区别的。他们都占一个字节,而我们并不需要去在意0XAA是-86还是170。
因此才有了这一行:myCom->write((char*)Test);(注意,如果Test[]中有0x00,系统会认为它是“/0”后面的数据会被截断。)
然后通过开发板与PC机通信发现Linux端发送的数据已经正常了。我满怀信心地将串口线插到WiMi-net上,结果什么数据都收不到!!这是什么情况?我又迷糊了。程序没有问题,数据没有问题,那问题一定出在硬件上了!!
在搜索到这篇博文的时候(http://www.cnblogs.com/qmlm8844/archive/2011/08/30/2159299.html),我恍然大悟了(再次感谢伟大的博客)。
RS232串口是全双工通信的,接收与发送数据可以同时进行,所以接收和发送有各自的数据线。从表1可以看到,2是接收线,3是发送线。
表1 RS232接口定义(9芯)
针脚 |
定义 |
符号 |
1 |
载波检测 |
DCD |
2 |
接收数据 |
RXD |
3 |
发送数据 |
TXD |
4 |
数据终端准备好 |
DTR |
5 |
信号地 |
SG |
6 |
数据准备好 |
DSR |
7 |
请求发送 |
RTS |
8 |
清除发送 |
CTS |
9 |
振铃提示 |
RI |
由于开发板和Wimi-net上的串口都是母头的,所以要用公-公串口线。而一般的公-公串口线都是1-1,2-2.。。针针对应的,那么,开发板数据从3脚出来,却发送到Wimi-net的发送脚(3脚)去了!!这要是能通信成功就奇了怪了列。。。。
再换了2X3的交叉串口线后。终于全系统调试成功,过程虽不算艰辛,但确实不断波折,记录在此,希望能给有相关疑问的朋友一个思路就好。
好了,今天的记录就先到这里吧~
作者:cloud_castle