当前位置:首页 >> 信息与通信 >>

QT教程完美版


我们在 windows 下只需要使用其中的 6 个文件: qextserialbase.cpp 和 qextserialbase.h , qextserialport.cpp 和 qextserialport.h , win_qextserialport.cpp 和 win_qextserialport.h 如 果 在 Linux 下 只 需 将 win_qextserialpo

rt.cpp 和 win_qextserialport.h 换 为 posix_qextserialport.cpp 和 posix_qextserialport.h 即可。 下面我们将讲述详细编程过程,这里我们先给出完整的程序,然后再进行逐句分析。 1.打开 Qt Creator,新建 Qt4 Gui Application,工程名设置为 mycom,其他使用默认选项。 (注意:建立的工程路径不能有中文。 ) 2.将上面所说的 6 个文件复制到工程文件夹下,如下图。

3.在工程中添加这 6 个文件。 在 Qt Creator 中左侧的文件列表上, 鼠标右击工程文件夹, 在弹出的菜单中选择 Add Existing Files,添加已存在的文件。如下图:

选择工程文件夹里的那 6 个文件,进行添加。如下图。

添加好后文件列表如下图所示:

4.点击 mainwindow.ui,在窗口上加入一个 Text Browser,用来显示信息。如下图。

5.在 mainwindow.h 的相应位置添加头文件#include "win_qextserialport.h",添加对象声明 Win_QextSerialPort *myCom;,添加槽函数声明 void readMyCom();,添加完后,如下图。

6.在 mainwindow.cpp 的类的构造函数中添加如下语句。 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); struct PortSettings myComSetting {BAUD9600,DATA_8,PAR_NONE,STOP_1,FLOW_OFF,500}; //定义一个结构体,用来存放串口各个参数 myCom = new Win_QextSerialPort("com1",myComSetting,QextSerialBase::EventDriven); //定义串口对象,并传递参数,在构造函数里对其进行初始化 myCom ->open(QIODevice::ReadWrite); //以可读写方式打开串口 connect(myCom,SIGNAL(readyRead()),this,SLOT(readMyCom()));

=

//信号和槽函数关联,当串口缓冲区有数据时,进行读串口操作 } 在下面添加 readMyCom()函数的定义,添加如下代码。 void MainWindow::readMyCom() //读串口函数 { QByteArray temp = myCom->readAll(); //读取串口缓冲区的所有数据给临时变量 temp ui->textBrowser->insertPlainText(temp); //将串口的数据显示在窗口的文本浏览器中 } 添加完代码后如下图。

此时如果运行程序, 已经能实现读取串口数据的功能了。 我们将单片机采集的温度信息由串 口传给计算机,效果如下图。

这样最简单的串口通信程序就完成了。可以看到它只需要加入几行代码即可,非常简单。 在下一篇中我们将详细分析添加的每一条语句。

[mainwindow.cpp] #include <qlabel.h> #include <qpushbutton.h> #include <qvbox.h> #include <qapplication.h> #include <qlineedit.h> #include <qpixmap.h> #include <qpainter.h> #include <qimage.h> #include <qstring.h>

#include "mainwindow.h" #include "serialthread.h" void MainWindow::paintEvent( QPaintEvent * ) { QPainter paint( this ); paint.drawLine( 0,0,500,500 ); // draw line paint.drawPixmap(0,0,*pix); } void MainWindow::loadJPEGFile(){ if(!pix->load("testjpeg")){ //if(!pix->load("circle")){ setMsgText("Load failed"); return; } setMsgText("Load success!"); update(); } void MainWindow::setCounter(int no){ counter = no; } void MainWindow::serialOperate(){ a = new SerialThread(this); a->start(); a->wait(); } MainWindow::MainWindow(QWidget * parent , const char * name) :QMainWindow(parent, name) { counter = 0; QVBox *vbox; vbox = new QVBox(this); vbox->resize(300,150); //msg = new QLabel("SERIAL PROGRAMMING",vbox);

msg = new QLineEdit("SERIAL PROGRAMMING",vbox); msg->resize(300,50); pix = new QPixmap();

btn = new QPushButton(vbox); btn->setText("GO!"); QApplication::connect(btn,SIGNAL(clicked()),this,SLOT(serialOperate())); btn2LoadImg = new QPushButton(vbox); btn2LoadImg->setText("LOAD"); lab = new QLabel("before load jpeg",vbox); QApplication::connect(btn2LoadImg,SIGNAL(clicked()),this,SLOT(loadJPEGFile()) ); //btn->resize(100,75); //vbox->show(); }; void MainWindow::setMsgText(char* txt){ QString msgs(txt); QString count = QString::number(counter,10); msgs.append(count); const char *re = msgs.ascii (); //strcat(msgs,); msg->setText(re); };\

[my_define.h] #define BAUDRATE B115200 #define BLOCK_SIZE 200 #define DEVICE "/dev/ttyS0" #define WAIT_TIME 5 #define CHANGE_LINE 0x0a #define ACK_NUM 3 #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE -1 #endif

[serialthread.h] #ifndef SERIAL_THREAD_H #define SERIAL_THREAD_H #include <qthread.h> class MainWindow; class SerialThread: public QThread { public: SerialThread(MainWindow *parent); virtual void run(); private: MainWindow *parent; }; #endif

[serialthread.cpp] #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h> #include "my_define.h" #include "serialthread.h" #include "mainwindow.h"

int set_nc_mode(int fd) { struct termios options; if ( tcgetattr( fd,&options) != 0){ perror("SetupSerial 1"); return(FALSE); } /* get current port settings */ bzero(&options, sizeof(options)); options.c_cflag |= BAUDRATE | CS8 | CLOCAL | CREAD;

options.c_cflag &= ~CRTSCTS; options.c_iflag = IGNPAR; options.c_oflag &=~OPOST; // options.c_lflag = 0; options.c_cc[VTIME] = WAIT_TIME; options.c_cc[VMIN] = BLOCK_SIZE; /* blocking read until 5 chars received */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&options); return(TRUE); } int set_c_mode(int fd) { struct termios options; if ( tcgetattr( fd,&options) != 0){ perror("SetupSerial 1"); return(FALSE); } bzero(&options, sizeof(options)); tcflush(fd, TCIOFLUSH); cfsetispeed(&options, BAUDRATE); cfsetospeed(&options, BAUDRATE); options.c_cflag |=(CLOCAL|CREAD); options.c_cflag &= ~CRTSCTS; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_cflag &= ~PARENB; /* Clear parity enable,clear control mode flag */ options.c_iflag &= ~INPCK; /* Disable parity checking ,*/ options.c_cflag &= ~CSTOPB; options.c_iflag |= IGNBRK; options.c_lflag |= ICANON; options.c_lflag &= ~(ECHO | ECHOE | ISIG); options.c_oflag &= ~(OPOST); tcflush(fd, TCIOFLUSH); if (tcsetattr(fd,TCSANOW,&options) != 0){

perror("SetupSerial 3"); return (FALSE); } return(TRUE); } void send_ack(int fd) { char buf[]={'A','C','K',CHANGE_LINE}; write(fd,buf,sizeof(buf)); } void resend(int fd) { char buf[]={'R','S','D',CHANGE_LINE}; write(fd,buf,sizeof(buf)); } void delay(int i) { int j; for (;i>0;i--) for(j=0;j<65535;j++); }

//////////////////////////////////////////////////////////////////////////////////////////// SerialThread::SerialThread(MainWindow *parent){ this->parent = parent; }

void SerialThread::run() { int fd,c, res; int block_num,last_block; int i; char buf[BLOCK_SIZE]; char file_name[32]; FILE *fp; struct termios oldtio; block_num=last_block=0; fd = open(DEVICE, O_RDWR | O_NOCTTY ); parent->setCounter(fd);

parent->setMsgText("opend device fd::::::");

if (fd <0){ perror(DEVICE); parent->setMsgText("open device failed"); // exit(-1); } tcgetattr(fd,&oldtio); set_nc_mode(fd); printf("Changed to nc mode\n"); /* res=read(fd,( char *)file_name,32); parent->setCounter(res); parent->setMsgText("res is ::::::"); */ /*

if(res>0){ file_name[res-1]='\0'; printf("Received the file name:%s\n",file_name); } else printf("The received file name is error.\n"); fp=fopen(file_name,"wb"); if(fp==NULL){ printf("Can not creat file %s!\n",file_name); return; // exit(-1); } else { send_ack(fd); printf("The file %s is created.\nWaitting for the block num and last block size\n",file_name); } //set_nc_mode(fd); //printf("Changed to nc mode\n");

res=read(fd,buf,4); printf("res=%d\n",res); printf("Received the block num \n"); for(i=0,block_num=0;i<4;i++){ block_num=block_num*10+buf; printf("buf[%d]=%x\n",i,buf); } printf("The number of blocks is %d\n",block_num); send_ack(fd); res=read(fd,buf,3); printf("res=%d\n",res); printf("Received the last block size \n"); for(i=0,last_block=0;i<3;i++){ last_block=last_block*10+buf; printf("buf[%d]=%x\n",i,buf); } printf("The last block size is %d\n",last_block); send_ack(fd); printf("Starting receive blocks\n"); for(i=0;i<block_num;i++){ res=read(fd,buf,BLOCK_SIZE); if(res!=BLOCK_SIZE){ printf("res=%d,\t Request resend the %d block\n",res,i); i--; tcflush(fd, TCIOFLUSH); resend(fd); } else{ fwrite(buf,1,BLOCK_SIZE,fp); printf("Received the %d block, res=%d\n",i,res); printf("Send ack signal complete,waiting to read\n"); send_ack(fd); } } printf("start transporting the last block\n"); if(last_block>0){ send_ack(fd); res=read(fd,buf,last_block); printf("res=%d\n",res);

if(res!=last_block){ printf("Request resend the last block\n"); tcflush(fd, TCIOFLUSH); resend(fd); } else fwrite(buf,1,last_block,fp); } send_ack(fd); printf("The file transports end\n"); fclose(fp); printf("close the file\n"); tcsetattr(fd,TCSANOW,&oldtio); close(fd); printf("close the serial port\n"); */ }

上一篇文章中已经介绍了实现最简单的串口接收程序的编写,这篇将对程序内容进行分析。 1.首先应说明操作串口的流程。 步骤一:设置串口参数,如:波特率,数据位,奇偶校验,停止位,数据流控制等。 步骤二:选择串口,如 windows 下的串口 1 为“com1”,Linux 下为“ttyS0”等。 步骤三:读或写串口。 步骤四:关闭串口。 (我们上一个程序没有写串口和关闭串口的功能, 打开串口也是在构造函数里完成的, 因为 那只是为了用最简单的方法完成串口程序的编写。 在以后的文章里我们将会对它进行修改和 完善。 ) 2.下面我们将按照上面的操作串口的流程,讲解第一个程序的编写。 第一,我们在写程序之前,应该浏览一下那 6 个文件,大概看一下它们里面都是什么内容, 各个文件各个类之间有什么联系。在 win_qextserialport.cpp 文件中,我们看它的最后一个构 造函数,会发现,串口可以在这里进行初始化。

Win_QextSerialPort::Win_QextSerialPort(const QString & name, const PortSettings& settings, QextSerialBase::QueryMode mode) { Win_Handle=INVALID_HANDLE_VALUE; setPortName(name); setBaudRate(settings.BaudRate); setDataBits(settings.DataBits); setStopBits(settings.StopBits); setParity(settings.Parity);

setFlowControl(settings.FlowControl); setTimeout(settings.Timeout_Millisec); setQueryMode(mode); init(); } 它共有三个参数,其中第一个参数 const QString & name,应该是串口的名字,是 QString 类 型,我们可以用串口 1 即“com1”,不用过多说明。下面我们主要研究第二个和第三个参数。 第二,我们查看第二个参数的位置。 在 Qt Creator 的菜单中选择 Edit->Find/Replace->All projects,如下图。

在弹出的对话框中输入要查找的内容 PortSettings,如下图。

点击 Search 后,便能在下面显示出整个工程中所有 PortSettings 的位置。如下图。

我们点击第一条,可以看到在 qextserialbase.h 文件中有一个 struct PortSettings,如下图。

我们双击这一条,进入相应的文件。如下图。

struct PortSettings { BaudRateType BaudRate; DataBitsType DataBits; ParityType Parity; StopBitsType StopBits; FlowType FlowControl; long Timeout_Millisec; }; 可以看到在这个结构体里定义了串口初始化的各个参数,而对于 BaudRateType 等类型的定 义,我们在这个结构体的上面可以看到,它们是多个枚举变量。如下图。

这时我们便应该明白了,这个结构体便是实现串口参数设置的。

第三,定义串口参数。 BaudRateType BaudRate; 波特率设置,我们设置为 9600,即程序中用 BAUD9600; DataBitsType DataBits; 数据位设置,我们设置为 8 位数据位,即 DATA_8; ParityType Parity; 奇偶校验设置,我们设置为无校验,即 PAR_NONE; StopBitsType StopBits; 停止位设置,我们设置为 1 位停止位,即 STOP_1; FlowType FlowControl; 数据流控制设置,我们设置为无数据流控制,即 FLOW_OFF; long Timeout_Millisec; 延时设置,我们设置为延时 500ms,即 500; 这样便写出了程序中的那句: struct PortSettings myComSetting {BAUD9600,DATA_8,PAR_NONE,STOP_1,FLOW_OFF,500}; 我们定义了一个结构体变量 myComSetting,并对其进行了初始化。

=

第四,设置第三个参数。 我们先按上面的方法找到它的定义位置,在 qextserialbase.h 中,如下图。

可以看到查询模式也是枚举变量,有两个选项,我们选择第二个 EventDriven,事件驱动。 到这里,我们就可以定义 Win_QextSerialPort 类的变量了,就是那句: myCom = new Win_QextSerialPort("com1",myComSetting,QextSerialBase::EventDriven); 它完成了串口的选择和串口的初始化。

第五,写打开串口函数和读串口函数。 查看 win_qextserialport.h 文件,我们会发现 Win_QextSerialPort 类继承自 QextSerialBase 类。

查看 qextserialbase.h 文件,我们会发现 QextSerialBase 类继承自 QIODevice 类。

我们在 Qt 的帮助中查看 QIODevice 类,如下图。

其部分内容如下图。可以看到其中有 enum OpenModeFlag { NotOpen, ReadOnly, WriteOnly, ReadWrite, ..., Unbuffered },virtual bool open ( OpenMode mode ),QByteArray readAll ()等内 容。

而下面的信号函数中有 void readyRead ();它可以查看串口是否有新的数据传来。

所以,我们可以用这个类里的这些函数操作串口。

如程序中的语句: myCom ->open(QIODevice::ReadWrite); //我们调用了其中的 open 函数,用 ReadWrite 可读写的方式进行打开串口; connect(myCom,SIGNAL(readyRead()),this,SLOT(readMyCom())); //我们关联信号 readyRead(),和自己写的槽函数 readMyCom(),当串口有数据传来时进行读 串口操作。 void MainWindow::readMyCom() //自己写的读串口函数 { QByteArray temp = myCom->readAll(); //我们调用 readAll()函数,读取串口中所有数据,在上面可以看到其返回值是 QByteArray 类 型。 ui->textBrowser->insertPlainText(temp); //调用 insertPlainText()函数,是窗口上的文本浏览器中连续输出数据,而不是每次写数据前 都清除以前的 //数据,可以在 Qt 的帮助里查看这个函数的说明 }

这样我们便写完了所有的语句,最后只需要在 mainwindow.h 文件中加入相应的头文件,对 象声明,函数声明即可。 这里需要说明的是我们一定要学会查看文件和使用帮助文档,将我们不懂得知识一点一

点搞明白。

编写串口程序心得 最近一段时间, 需要完成项目中关于 Linux 下使用串口的一个部分, 现在开帖记录过程点滴。 项目的要求是这样的, 应用程序主要完成数据采集和发送功能, Qt 原来采用的是把 ARM 板 的串口设置城网口,然后通过拨号上网,通过 socket 编程实现数据的传输的。后来发现稳 定性不高,于是换了一个第三方公司生产的 DTU,希望直接往串口传输数据。 一开始在 google 中海搜关键字“Qt 串口编程”,得出的结论是: 一、Qt 自己的类中没有关于串口的类,不过有人做了一个第三方的类:qextserialport。可以 在 如 下 地 址 下 载 到 : ftp://ftp.trolltech.com/contrib/qextserialport.tar.gz 或 者 http://qextserialport.sourceforge.net 二、关于 qextserialport,下载下来的文件中会自带比较详细的 HTML 文档介绍,不过都是 英文哦!而且有版本对应,目前的认识是 0.8 或 0.9 是可以用于 qt3 的,之后的使用于 qt4。 三 、 以 下 文 章 是 讲 qextserial 的 编 译 的 , 不 过 好 像 用 处 不 大 。 http://www.cnblogs.com/leaway/archive/2008/03/13/1104562.html 四、也可以不用这个类,直接调用 linux 的系统函数。Linux 中“万物皆文件”,所以串口也 不例外。只要利用 open()函数打开设备,用 read()和 write()函数读写串口,用 close()关闭 即可。另外,对于串口需要设置一些参数。 五、继续往下搜,一篇号称“Linux 下串口编程 Bible”的文章《Serial Programming Guide for POSIX Operating Systems》浮出水面,不过照样是英文的。以下网址可以在线阅读或下载。 http://www.easysw.com/~mike/serial/serial.html http: //digilander.libero.it/robang/rubrica/serial.htm http: //digilander.libero.it/robang/rubrica/serial.htm 粗 粗 读 过 的 确 感 觉 不 错 。 许 多 中 文 版 本 大 都 是 部 分 翻 译 : http://www.ibm.com/developerworks/cn/linux/l-serials/index.html 六、 《Linux Serial Programming HOW-TO》也是另外一篇必读的文章,地址为 http://fanqiang.chinaunix.net/a4/b7/20010502/110712.html http://www.vanemery.com/Linux/Serial/serial-console.html 七、http://www.et2.tu-harburg.de/Mitarbeiter/Bauhan/software/serportE.html 据说是一个用 Qt 写的和串口通讯的应用。没有测试过。 八、http://www.oklinux.cn/html/Basic/jyjq/20070522/25995.html 中是串口通信学习笔记,其中 的参考文献可以看出基本的几个经典文章。 九、http://blog.csdn.net/autofei/archive/2005/12/07/545836.aspx 串口编程的个人心得。 十、 http://www.xxlinux.com/linux/article/development/soft/20071029/11228.html 串口编程分析。 2009 年 2 月 16 日 9:43:27 开始试着在 Linux 下编译下载的例子。 [root@localhost qextserialport-0.9.0]# ls Changes qextserialbase.cpp qextserialport.pro html qextserialbase.h win_qextserialport.cpp posix_qextserialport.cpp qextserialport.cpp win_qextserialport.h posix_qextserialport.h qextserialport.h [root@localhost qextserialport-0.9.0]# vi qextserialport.pro

[root@localhost qextserialport-0.9.0]# qmake -o Makefile qextserialport.pro [root@localhost qextserialport-0.9.0]# vi make [root@localhost qextserialport-0.9.0]# vi Makefile [root@localhost qextserialport-0.9.0]# make g++ -c -pipe -Wall -W -g -D_REENTRANT -fPIC -D_TTY_POSIX_ -DQT_THREAD_SUPPORT -I/usr/local/qt-x11-free-3.3.6/mkspecs/linux-g++ -I. -I/usr/include/freetype2 -I../../include -Imoc/ -o obj/qextserialbase.o qextserialbase.cpp g++ -c -pipe -Wall -W -g -D_REENTRANT -fPIC -D_TTY_POSIX_ -DQT_THREAD_SUPPORT -I/usr/local/qt-x11-free-3.3.6/mkspecs/linux-g++ -I. -I/usr/include/freetype2 -I../../include -Imoc/ -o obj/qextserialport.o qextserialport.cpp g++ -c -pipe -Wall -W -g -D_REENTRANT -fPIC -D_TTY_POSIX_ -DQT_THREAD_SUPPORT -I/usr/local/qt-x11-free-3.3.6/mkspecs/linux-g++ -I. -I/usr/include/freetype2 -I../../include -Imoc/ -o obj/posix_qextserialport.o posix_qextserialport.cpp test -d lib/ || mkdir -p lib/ rm -f libqextserialport.so.1.0.0 libqextserialport.so libqextserialport.so.1 libqextserialport.so.1.0 g++ -Wl,-rpath,/usr/local/qt-x11-free-3.3.6/lib -shared -Wl,-soname,libqextserialport.so.1 -Wl,-rpath,/usr/local/qt-x11-free-3.3.6/zhf_work/qextserialport-0.9.0/lib -o libqextserialport.so.1.0.0 obj/qextserialbase.o obj/qextserialport.o obj/posix_qextserialport.o -L/usr/local/qt-x11-free-3.3.6/lib -lqt-mt -lpthread ln -s libqextserialport.so.1.0.0 libqextserialport.so ln -s libqextserialport.so.1.0.0 libqextserialport.so.1 ln -s libqextserialport.so.1.0.0 libqextserialport.so.1.0 rm -f lib/libqextserialport.so.1.0.0 rm -f lib/libqextserialport.so rm -f lib/libqextserialport.so.1 rm -f lib/libqextserialport.so.1.0 mv -f libqextserialport.so.1.0.0 libqextserialport.so libqextserialport.so.1 libqextserialport.so.1.0 lib/ 一下子轻易通过编译,还真的是不敢相信! 不过后来发现这个根本没用, 因为我需要做到是把这个现成的类添加到我的工程之中, 而不 是要用它的.o 文件。编译通过只说明现在下载的版本是没有语法错误的哈哈。

2009 年 2 月 16 日 19:46:21 搞了两天,几近崩溃,好在晚饭前终于曙光降临。我用 Posix_qextserialport 类实现了串口的 写数据,看到数据接收到的一刻,突然感觉所有的事情都不再困难。而之前所有的努力也因 为这一刻的到来而充满了意义。 必须清醒认识到,万里长征只走了一小步。之后需要实现的问题有: 一、做到向串口写数据和读数据,最后做个小界面,完善一下,直观一点。 二、与原有程序结合,实现原来的预想功能。 三、顺便研究调用第三方类和直接使用 linux 函数两种方式,总结成文。 2009 年 2 月 16 日 21:15:03

阅读网上的《为 Qt 扩展 QextSerialPort 类》有感 无论是 Win 还是 Lin,都是下载源码包,然后放到源码目录 src 下,然后 qmake make,即可 得到对应的库文件。在 Win 下是 qextserialport.dll,qextserialport.prl,libqextserialport.a, 需要将第一个复制到 Qt 安装目录下的 bin 目录下,后面两个放在 Qt 安装目录的 lib 下。 而在 Lin 下会产生好几个文件,具体候补 然后需要在 Qt 的 include 目录下拷贝有 qextserialport.h 等头文件,Qt4 好像有 QtGui 目录, 这个还没有研究过。 1.1 后 的 版 本 有 example , 可 以 拷 贝 到 硬 盘 中 , Qt4 好 像 里 面 的 头 文 件 包 含 可 以 用 QextSerialPort。 然后是 qmake -project 得到 pro 文件爱你,打开工程,加入 LIBS+= -l qextserialport 如果是 win 加入 DEFINES+= _TTY_WIN_QWT_DLL QT_DLL 如果是 Lin,加入 DEFINES+= _TTY_POSIX_ 然后 qmake Make 正常会顺利,如果遇到问题再根据提示解决。

2009 年 2 月 17 日 继续努力。今天定下的两条指导思想: 1、要研究 qextserialport 的文档,因为它是为 Qt 编写的类,集成了 Qt 文档丰富的优点,而 且风格是与 Qt 的其他类是完全类似的。 2、要研究 1.1 版本的例子,虽然 0.9 版本没有例子,但是版本的延续是一脉相承的。所以看 后续版本的例子也有一些启发。 开始试着按照例子,写了一个小小程序,仅包含 openport(),putch(),getch()和 flush 等几个动 作,不过居然成功了,这给我巨大信心,感觉自己一定是可以做出来的。 看得差不多了我就构思如何换掉原来程序通信模式而运用读写串口来完成通信。仔细一想, 其实并不怎么难的。 面向对象真的好处体现出来了!功能需要变化时候只需要改变相关部分的类而不用动其他 的,甚至接口的改动都很少。我修改了 client 类和接口的参数以及一些小细节。然后就去试 验了。 没想到不行,连串口都打不开! 这个成了一个大问题,程序方面不应该有太大问题啊,因为主要部分都是执行成功过的。那 只能是怀疑文件系统问题,因为我一直没有搞明白的问题是为什么一个 COM2 可以当作网 口来用! (文件系统中一直没找到相关的设置) 。后来用了小梁的文件系统,试验了他的另一 种方法对串口操作的程序,结果成功了。 然后我尝试打开串口 1,结果可以啊! 调换了好多次之后,莫名其妙串口 2 也可以了,可是原因还没有找到。 剩下的一个问题是数据的读取,读取是没有什么问题,问题是不知道以什么方式去读。是中 断?中断需要触发啊,可是数据的来临有什么信号呢,不知道啊。只好用查询,查询不可能 用 while(1),否则程序啥也别干,就只能干这个了,显然不行。后来采用了一个定时器来查 询,隔一段时间就过来巡逻一次。

回想起来,今天大部分时间耗在了文件系统的更换上。有一点感悟是:做过的东西一定要备 份,否则很惨,时间一长,你除了知道你做过之外就什么也不记得了! 2009 年 2 月 18 日 20:44:16 今天主要被两个个问题困扰! 一个是乱码问题。 原以为昨天解决了大部分问题今天就可以正常接收了, 没想到接收到的全是乱码。 后来想来 想去觉得只有串口的波特率设置是可能造成影响的。可是我的发送端和接收端全是一样是 9600 啊。无奈,看到梁哥那个串口调试工具是 115200,DNW 也是,我就把波特率全部设 置成了 115200。 果然可以了, 而且另外一个发现是: 只能是这一种, 设置成其他的, 一样都不行, 高于 115200 也不行,真不知道波特率由什么决定的啦。不过通信中真是太重要了,一不匹配就什么也得 不到。 第二个问题是:当我传输的文本较大时(其实没有多大,只有 2000 多 bytes),服务器就只 能接收到前面一部分了,原来以为它分了多次也应该能够收到,但是就是没有! 我想过了各种可能,并尝试了各种小试验验证,逐一排查。 试验一:利用电脑的串口向 DTU 发送较长数据,发现接收正常,反过来也是正常的。 这下我认定 DTU 肯定没有问题,出问题的肯定只有自己发送端的程序了。我查看了 qestserialport 的源代码,发现里面并没有给发送的大小设置限制。 试验二:在发送端采用拆包发送机制,一次发送不超过 1024,结果发现现象还是跟原来一 样没变化。 最后没办法,把 qextserialport 的父类,爷爷类,太爷爷类都翻出来看。发现其祖上居然是 Qt 中的 QIODevice,这东西真是太好了,因为 QSocket 也是其同脉子孙,既然如此,可以 用原来程序的思路。这样,我用出了最后一招(GOD,这招要是不管用,我真的没招了!: ) QString->QTextStream->QIODevice(Qtxtserialport) 哈哈,可以了! 从 13 号下午拿到模块,到现在已经过了五天,除了情人节没有工作之外总共用了四天多时 间来解决这个问题。想想自己真的菜鸟,要换了牛人一下子功夫不久搞定了。 不管那么多,牛人还不是从小牛仔长大的嘛!从零开始,日积月累,终会成功。 在问题的解决过程中,我体会到了几点: 1、要善于在网上查资料,不管中文英文,来者不拒都有有选择地仔细阅读。互联网会带给 你很多! 2、在程序方面自己要提高,可以多模仿外国的程序,规范写法。 3、问题引导式的学习更有成效。平时没有什么事情可以给自己出一个问题,然后尝试找到 解决办法。随后进行测试,郁闷,刚想说肯定没问题,结果一看出问题了!弹出了对话框, 不过解决一下应该可以。DTU 还是很不错,很稳定。

首先,加入了“打开串口” 首先,加入了“打开串口”,“关闭串口”“传送数据”三个按钮,加入了一 关闭串口”“传送数据”三个按钮, ”“传送数据 Edit。它们的命名如下: 个行编辑框 Line Edit。它们的命名如下: “打开串口”按钮命名为:openMyComBtn

“关闭串口”按钮命名为:closeMyComBtn “传送数据”按钮命名为:sendMsgBtn 要传送数据的行编辑框命名为:sendMsgLineEdit 界面如下图。

第二步, 在 打开串口”按钮上右击, 选择 Go to slot 选项, 选项, 第二步, “打开串口”按钮上右击, 然后选择 clicked() 选项,进入它的单击事件槽函数中, 选项,进入它的单击事件槽函数中,将上个程序中在构造函数里写的语句全部 剪切到这里。然后加入几句按钮的状态设置语句。如下: 剪切到这里。然后加入几句按钮的状态设置语句。如下: void MainWindow::on_openMyComBtn_clicked() { struct PortSettings myComSetting = {BAUD9600,DATA_8,PAR_NONE,STOP_1,FLOW_OFF,500}; //定义一个结构体,用来存放串口各个参数 myCom = new Win_QextSerialPort("com1",myComSetting,QextSerialBase::EventDriven); //定义串口对象,并传递参数,在构造函数里对其进行初始化 myCom ->open(QIODevice::ReadWrite); //以可读写方式打开串口 connect(myCom,SIGNAL(readyRead()),this,SLOT(readMyCom())); //信号和槽函数关联,当串口缓冲区有数据时,进行读串口操作 // ui->openMyComBtn->setEnabled(false); //打开串口后“打开串口”按钮不可 用 ui->closeMyComBtn->setEnabled(true); //打开串口后“关闭串口”按钮可用 ui->sendMsgBtn->setEnabled(true); //打开串口后“发送数据”按钮可用 }

然后在构造函数里也添加几句按钮初始状态设置语句,如下: 然后在构造函数里也添加几句按钮初始状态设置语句,如下: MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)

{ ui->setupUi(this); ui->closeMyComBtn->setEnabled(false); //开始“关闭串口”按钮不可用 ui->sendMsgBtn->setEnabled(false); //开始“发送数据”按钮不可用 } 更改后程序如下图所示:

这时运行程序,效果如下:

第三步,按上面的方法进入“关闭串口”按钮和“发送数据” 第三步,按上面的方法进入“关闭串口”按钮和“发送数据”按钮的单击事件 的槽函数,更改如下。 的槽函数,更改如下。 void MainWindow::on_closeMyComBtn_clicked() //关闭串口槽函数

{ myCom->close(); //关闭串口,该函数在 win_qextserialport.cpp 文件中 定义 ui->openMyComBtn->setEnabled(true); //关闭串口后“打开串口”按钮可用 ui->closeMyComBtn->setEnabled(false); //关闭串口后“关闭串口”按钮不 可用 ui->sendMsgBtn->setEnabled(false); //关闭串口后“发送数据”按钮不可用 }

void MainWindow::on_sendMsgBtn_clicked() //发送数据槽函数 { myCom->write(ui->sendMsgLineEdit->text().toAscii()); //以 ASCII 码形式将行编辑框中的数据写入串口

}

最终效果如下: (将数据 x 发送给单片机,单片机返回 you send message is : x)

在下一篇文章中将对程序更进一步的完善 在下一篇文章中将对程序更进一步的完善。 本文一开始先讲解对程序的改进,在文章最后将要讲解一些重要问题 重要问题。 重要问题 第一, Box,它们的名称及条目如下: 第一,在窗口中加入一些组合框 Combo Box,它们的名称及条目如下: 串口:portNameComboBox,条目为:COM1,COM2 波特率:baudRateComboBox,条目为:9600,115200 数据位:dataBitsComboBox,条目为:8,7 校验位:parityComboBox,条目为:无,奇,偶 停止位:stopBitsComboBox,条目为:1,2 (注:在窗口上的 Combo Box 上双击,在弹出的对话框上按“+”号,可添加条 目。) 改好的窗口如下所示:

第二,更改“ 开串口”按钮的单击事件槽函数。 第二,更改“打开串口”按钮的单击事件槽函数。 void MainWindow::on_openMyComBtn_clicked()

{ QString portName = ui->portNameComboBox->currentText(); //获取串口名 myCom = new Win_QextSerialPort(portName,QextSerialBase::EventDriven); //定义串口对象,并传递参数,在构造函数里对其进行初始化 myCom ->open(QIODevice::ReadWrite); //打开串口

if(ui->baudRateComboBox->currentText()==tr("9600")) //根据组合框内容 对串口进行设置 myCom->setBaudRate(BAUD9600); else if(ui->baudRateComboBox->currentText()==tr("115200")) myCom->setBaudRate(BAUD115200);

if(ui->dataBitsComboBox->currentText()==tr("8")) myCom->setDataBits(DATA_8); else if(ui->dataBitsComboBox->currentText()==tr("7")) myCom->setDataBits(DATA_7);

if(ui->parityComboBox->currentText()==tr("无")) myCom->setParity(PAR_NONE); else if(ui->parityComboBox->currentText()==tr("奇")) myCom->setParity(PAR_ODD); else if(ui->parityComboBox->currentText()==tr("偶")) myCom->setParity(PAR_EVEN);

if(ui->stopBitsComboBox->currentText()==tr("1")) myCom->setStopBits(STOP_1); else if(ui->stopBitsComboBox->currentText()==tr("2")) myCom->setStopBits(STOP_2);

myCom->setFlowControl(FLOW_OFF); myCom->setTimeout(500);

connect(myCom,SIGNAL(readyRead()),this,SLOT(readMyCom())); //信号和槽函数关联,当串口缓冲区有数据时,进行读串口操作

ui->openMyComBtn->setEnabled(false); //打开串口后“打开串口”按钮不可 用

ui->closeMyComBtn->setEnabled(true); //打开串口后“关闭串口”按钮可用 ui->sendMsgBtn->setEnabled(true); //打开串口后“发送数据”按钮可用

ui->baudRateComboBox->setEnabled(false); //设置各个组合框不可用 ui->dataBitsComboBox->setEnabled(false); ui->parityComboBox->setEnabled(false); ui->stopBitsComboBox->setEnabled(false); ui->portNameComboBox->setEnabled(false); } 这里我们先获取串口的名称,然后调用另一个构造函数对 myCom 进行定义,这个 构造函数里没有串口的设置参数。然后打开串口。然后获取串口的设置数据,用 setBaudRate();等一系列函数进行串口的设置,这些函数都在 win_qextserialport.cpp 文件中定义,如下图。

对于这几个函数应该很好理解,这里不再解释。在最后我们对添加的那几个组合 框进行了不可用设置,使其在串口打开的情况下不能选择。 程序如下:

第三,更改“关闭串口”按钮单击事件的槽函数。 第三,更改“关闭串口”按钮单击事件的槽函数。 void MainWindow::on_closeMyComBtn_clicked() { myCom->close(); ui->openMyComBtn->setEnabled(true); //关闭串口后“打开串口”按钮可用 ui->closeMyComBtn->setEnabled(false); //关闭串口后“关闭串口”按钮不 可用 ui->sendMsgBtn->setEnabled(false); //关闭串口后“发送数据”按钮不可用

ui->baudRateComboBox->setEnabled(true); //设置各个组合框可用 ui->dataBitsComboBox->setEnabled(true); ui->parityComboBox->setEnabled(true); ui->stopBitsComboBox->setEnabled(true); ui->portNameComboBox->setEnabled(true); } 这里只是加入了一些使组合框在“关闭串口”按钮按下后变为可用的语句。 程序如下:

第四, 文件。 第四,更改 main.cpp 文件。 #include <QtGui/QApplication> #include <QTextCodec> //加入头文件 #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QTextCodec::setCodecForTr(QTextCodec::codecForLocale()); //使程序可处理中文 MainWindow w; w.show(); return a.exec(); } 因为上面的语句中用到了中文,为了能使程序识别中文,我们需要在主函数中加 入这些语句。 程序如下:

第五,运行程序。 第五,运行程序。 打开后程序界面如下。

正常发送 1 后如下。

设置为“奇校验”后,发送完 1 的效果如下图。

到这里,整个程序就完全写完了。

重要问题说明:
(下面所说的第一个程序是指第一篇文章中写的那个程序, 第二个程序是指第三 篇文章中那个程序,第三个程序是指本篇文章中所写的程序。) 问题一: 问题一:第一个程序中 struct PortSettings myComSetting = {BAUD9600,DATA_8,PAR_NONE,STOP_1,FLOW_OFF,500}; myCom = new Win_QextSerialPort("com1",myComSetting,QextSerialBase::EventDriven); 这两句代码如果换为下面一行: myCom = new Win_QextSerialPort("com1",QextSerialBase::EventDriven); 你再运行一下程序,是不是还能用?那我们的串口设置的结构体 myComSetting 没有用吗?你可以把上面的结构体里的波特率由 9600 改为 115200,如果这个结 构体有用, 那么程序不可能再接收到数据, 不过, 你再运行一下程序, 是这样吗? 如此看来,我们对串口进行的设置果真没用,那默认的串口设置是什么 呢?我们先看下一个问题。

问题二:我们同时打开第三个程序和第二个程序。 (注意:两个程序的串口不能 问题二:我们同时打开第三个程序和第二个程序。 同时打开,所以打开一 个程序的串口时要将另一个程序的串口关闭。) 我们先在第三个程序上按默认设置打开串口,发送数据 1。然后关闭串口,在第 二个程序上打开串口,发送数据 1。可以看到两个程序上接受到的信息都正确。 如下图。

我们关闭第二个程序上的串口, 再将第三个程序上设置为奇校验, 然后打开串口, 发送数据 1,可以看到其收到的数据显示乱码。这时我们关闭第三个程序上的串 口,打开第二个程序上的串口,发送数据 1,你会惊奇地发现,它收到的信息也 是乱码。如下图。

这到底是怎么回事呢?我们也可以去网上下载其他的串口助手进行实验, 也可以 改变波特率进行实验。由所有的结果得出的结论只能是:我们用那个结构体作为 参数传过去后,并没有对串口进行设置,而程序运行使用的串口设置是系统以前 保留的设置。那么,为什么会这样呢?我们看下面的一个问题。 问题三: 问题三:我们将第三个程序中的那行代码 myCom ->open(QIODevice::ReadWrite); 放到设置串口的语句之后, connect(myCom,SIGNAL(readyRead()),this,SLOT(readMyCom())); 这句之前,然后再运行程序。你会发现程序的串口设置功能已经不起作用了。 其实,上面的三个问题是一个问题,它的结论就是,写串口程序时,要 先打开串口再对它进行设置,不然设置就不会起作用。所以,这里应该说明,第 一个和第二个程序都是不太正确的,正确的方法应该是像第三个程序一样,先定 义 Win_QextSerialPort 类对象,然后打开串口,再用那几个函数对串口进行设 置。

到这里整篇文章就结束了。对于其中的一些问题也只是我个人的观点, 到这里整篇文章就结束了。对于其中的一些问题也只是我个人的观点,由于水 平有限,所以理解上可能会有偏差,或者错误,请广大网友批评指正。 平有限,所以理解上可能会有偏差,或者错误,请广大网友批评指正。我写这 写出串口通信程序, 篇文章的目的只是想让 Qt 初学者能更轻松的用 Qt 写出串口通信程序,及掌握 写程序时的一些技巧。如果你从我的文章中学到了一点知识, Qt 写程序时的一些技巧。如果你从我的文章中学到了一点知识,那么我的这篇 文章就算是没有白写。 文章就算是没有白写。 最后,如果你喜欢我的写作风格,并且初学 Qt,可以在我的空间查看 Qt Creator 系列教程,希望能对你的入门有所帮助。


相关文章:
QT实验教程完美版
QT实验教程完美版_信息与通信_工程科技_专业资料。QT,单片机,嵌入式,数据采集,智能小车,串口Qt 核心剖析 寻找 QObject 的源代码 核心剖析: 本来打算把《Qt 学习之...
QT入门教程教程-详解版
QT入门教程教程-详解版_信息与通信_工程科技_专业资料。内含一些Qt的小实验,并有详解,带你打开Qt大门...今日推荐 180份文档 CET四六...
qt creator实战教程
qt creator实战教程c 一、Qt Creator 的安装和 hello world 程序的编写(原创) 1.首先到 Qt 的官方网站上下载 Qt Creator,这里我们下载 windows 版的。 下载...
QT一个月自学教程
QT一个月自学教程_计算机软件及应用_IT/计算机_专业资料 暂无评价|0人阅读|0次下载|举报文档 QT一个月自学教程_计算机软件及应用_IT/计算机_专业资料。QT一个月...
Qt入门教程--第01章 Qt Creator的安装和最简单程序的编写
Qt入门教程--第01章 Qt Creator的安装和最简单程序的编写_IT/计算机_专业资料。Qt入门教程, 用最简单的实例介绍Qt Creator的安装和Qt程序的编写方法第...
Qt简明教程
Qt 简明教程 1、Qt 的安装 、首先去 Qt 的官方网站(http://qt.nokia.com/downloads)下载你所需要的安装包,官方提供 了 Windows、Linux、Mac OS 等平台的安装...
QT 基础学习教程
QT 基础学习教程_计算机软件及应用_IT/计算机_专业资料。估计这是我见过的最基础的QT教程了.坑的.帮助文档都讲了.QT 基础学习教程 本教程主要是帮助初学者尽快的...
QT教程参考
QT教程参考_计算机软件及应用_IT/计算机_专业资料。QT问题解决有一次因为在 chakanbl::chakanbl(QWidget *parent) : QDialog(parent), ui(new Ui::chakanbl) 中...
Qt Designer快速入门
我发现很多 Qt教程都是用代码来编写界面程序,有没有图形化的方法呢!答案就 是 Qt Designer 打个比方,用代码写界面就像是你用记事本一行行的敲 HTML 代码,...
一步一步做QT界面设计
17 网络 QT 教程:http://www.qtopia.org.cn/doc/qiliang.net/qt/ 第一章 界面设计器第1节 界面设计效果 在这一张我们介绍用 QT 的设计器来设计一个程序...
更多相关标签:
黑苹果安装教程完美版 | qt教程 | qt5 教程 | qt视频教程 | qt creator 教程 | qt安装教程 | qt开发教程 | qt designer教程 |