嵌入式Linux主板Qt串口应用程序简介

 2014-4-25         

  串口和其他设备一样,在Linux系统中都是以设备文件的形式存在的,在英创公司提供的Linux主板配套光盘中,已经提供了基于C/C++ 的Linux标准串口操作例程,使用open函数来打开串口设备文件:'/dev/ttyS1',设置串口参数,创建一个单独的线程来对数据进行收发,使用read函数读取数据,write函数发送数据。并且英创公司将其封装成一个serial类,可以使用serial类方便的操作英创工控主板的串口资源。

 

  本文介绍如何在英创公司提供的Linux工控主板的串口例程Step2_serialtest的基础上,加入了Qt显示,实现在LCD显示屏上控制串口通讯。基本的思想是利用Qt的信号和插槽机制进行对象间的通讯,当串口接收的数据的时候,发出Signal,Qt 的显示事件循环中接收到这个信号,调用定义好的Slot显示串口的数据,如下图:

 

串口和Qt显示

 

  先使用Qt Creator创建一个工程,加入英创公司提供的Linux工控主板串口例程中的serial.h和serial.cpp文件,因为要在Step2_serialtest例程的基础上加入Qt显示单元,所以我们需要让英创公司封装好的serial类继承QObject类的属性,使QObject成为serial类的基类,然后在serial类中加入一个signal,让serial类和Qt显示通过信号和插槽的机制连接。但是对于串口的操作部分并不用做修改。

 

  QObject类是所有Qt对象的基类并且是Qt对象模型的中心。这个模型的中心特征就是一种用于无缝对象通讯的被叫做信号和槽的非常强大的机制。只有继承了 QObject 类的类,才具有信号和槽的能力。所以,为了使用信号和槽,必须继承 QObject。凡是 QObject 类(不管是直接子类还是间接子类),都应该在第一行代码写上 Q_OBJECT。不管是不是使用信号和槽,都应该添加这个宏。这个宏的展开将为我们的类提供信号和槽机制、国际化机制以及 Qt 提供的不基于 C++ RTTI 的反射能力。所以在serial.h中作如下修改:

 

  class CSerial: public QObject //继承QObject
  {
    Q_OBJECT
    signals:
    void readyRead(QString);
  };

 

  在serial.cpp中,串口数据接收线程检测到有数据可读时,添加发送信号的语句,提示显示对象有数据可以显示,并把数据发送给显示对象:

  emit pSer->readyRead(pSer->DatBuf);

 

  对串口程序的修改已经完成,在工程中加入Qt设计师界面,根据需要设计显示界面,本例程中的界面如下:

 

设计的界面

 

  可以看到,在界面中添加了三个按钮,分别为“打开串口”,“关闭串口”和“清除数据”,两个组合框,用来选择串口的端口号和比特率,一个文本浏览器用来显示串口接收的数据。在工程中加入窗口显示程序:widget.h和 widget.cpp,首先要对显示界面进行初始化,实现代码如下:

 

  Widget::Widget(QWidget *parent) :
  QWidget(parent),
  ui(new Ui::Widget)
  {
    ui->setupUi(this); //显示界面的初始化
    setActionsEnabled(true); //使能组件
    setComboBoxEnabled(true);
  }

 

  定义接收串口发送信号的插槽,在窗口的文本浏览器中显示串口接收的数据:

 

  void Widget::readMyCom(QString buf)
  {
    //将串口的数据显示在窗口的文本浏览器中
    ui->textBrowser->setText(ui->textBrowser->document()->toPlainText() + buf);
    QTextCursor cursor = ui->textBrowser->textCursor();
    cursor.movePosition(QTextCursor::End);
    ui->textBrowser->setTextCursor(cursor);
  }

 

  将串口发送的Signal和显示窗口类中Slot连接起来,以便响应:

  QObject::connect(myCom,SIGNAL(readyRead(QString)),this,SLOT(readMyCom(QString)),Qt::QueuedConnection);

 

  这里需要特别说明一点,由于串口例程会打开一个线程对数据进行处理,所以这里发送Signal的是串口处理数据的线程,而接收Signal是在显示的线程中,所以他们的连接方式需要采用Qt::QueuedConnection,这样使用跨线程的Signal和Slot连接,Signal发送以后,会等到控制权返回到接收者线程的事件循环后才调用槽,就是说槽在接收者的线程中被执行,如果采用其他方式连接,显示可能就会出现各种错误。

 

  如果在显示界面中添加了一些其他功能组件,比如按钮等,可以在这里编写组件的事件响应函数,实现想要的功能,在本例程中,添加了一些组合框和按钮,用来选择串口的端口号和波特率,并且打开或者关闭串口,部分实现代码如下:

 

  void Widget::on_openpushButton_clicked() //打开按钮
  {
    qint8 i;
    portno=ui->portNamecomboBox->currentText(); //读取端口号
    ba = portno.toLatin1();
    m_portno=ba.data(); //转换为字符串
    ba.clear();
    baudRate=ui->baudRatecomboBox->currentText(); //读取波特率
    m_baudRate=baudRate.toInt(); //转换为整形
    myCom=new CSerial;
    i=myCom ->OpenPort( m_portno, m_baudRate, '8', '1', 'N' ); //打开串口
    if(i<0)
    {
      QMessageBox::information(this, tr('打开失败'), tr('打开串口失败') , QMessageBox::Ok); //打开失败弹出提示框
      delete myCom;
      myCom = NULL;
      return;
    }
  }

 

  void Widget::on_closeopenpushButton_clicked() //关闭按钮
  {
    myCom->ClosePort(); //关闭串口
    delete myCom;
    myCom = NULL;
    setComboBoxEnabled(true);
    ui->closeopenpushButton->setEnabled(false);
  }

 

  最后在工程中添加main.cpp,在main函数中,主要是对字体进行设置,并且显示我们设计的窗口:

 

  int main( int argc,char* argv[] )
  {
    QApplication a(argc, argv);
    QFont font('simsun',11,QFont::Bold); //设置字体
    a.setFont(font);
    QTextCodec::setCodecForTr(QTextCodec::codecForName('utf8'));
    Widget w;
    w.showFullScreen(); //显示窗口
    return a.exec();
  }

 

  运行程序,例程的效果如下:

 

程序运行效果

 

  可以通过显示界面选择开启哪一路串口,设置波特率,显示接收到的数据。本例程只是简单的把Step2_serialtest和Qt的显示连接起来,将串口收到的数据显示在Qt的界面中,并且进行回发,客户的使用方法还是和以前的Step2_serialtest一样只是增加了显示界面。

 

  点此下载:示例源码