在嵌入式Linux系统的UI设计中,比较常见的是使用Qt库来实现。而在Qt中进行程序设计时,也经常会用到串口(UART)通信。现在基于Qt5.1以上的版本中,集成有串口模块(如QSerialPort),或者使用第三方开发的串口模块控件(如qextserialport等)。但无论采用哪种方式,在Linux系统下对于串口的数据接收都只能使用查询(Polling)的方式来实现,而在Windows系统下就可以使用效率较高的所谓事件驱动(EventDriven)方式。查询方式需要CPU反复对串口进行读取,看是否有发送来的可读数据,因此会消耗大量的CPU资源,一般的做法是把串口查询放到一个新建的线程中,以获得较高的效率。而对于事件方式则不同,只要串口接收到数据,就会以事件的方式通知CPU去执行相关的操作,在没有接收到数据时CPU可以做其他事情,所以效率较高,使用起来也很方便。

其实有Qt的官方文档中,并不推荐使用线程的方式来处理,因此给出了其替代的多种方案,其中之一就是使用QSocketNotifier的方式。在Qt4.0及以上的版本中,新增加了一个名为QSocketNotifier的模块,它用来监听系统文件的操作,把操作转换为Qt事件进入系统的消息循环队列,并调用预先设置的事件接受函数来处理事件。这就为Linux下的Qt串口通信提供了另外的解决方案。下面就来讨论一下,结合Qt中的QSocketNotifier模块,如何实现一个通用的、基于事件驱动的串口通信程序。

QSocketNotifier一共设置了三类事件:read、write、exception,具体如下表所示。

在使用QSocketNotifier来处理串口时,只需要设置为Read属性即可。每个QSocketNotifie对象监听一个事件,在使用open方法打开串口并设置好属性后,就可以使用Qt的类 QSocketNotifier来监听串口是否有数据可读,它是事件驱动的,配合Qt的信号/槽机制,当有数据可读时,QSocketNotifier就会发射ativated信号,只需要创建一个槽连接到该信号即可,然后在槽函数中处理串口读到的数据。这样一来,读取串口的任务就不用新开辟线程来处理了,这就是Qt官方给出的建议。

主程序代码如下。

#include "mainwindow.h"
#include "ui_mainwindow.h" #include <QSocketNotifier>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
int fdUart;
int len=0,count=0;
char read_data[100];
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
readTimer = new QTimer(this); //新建一个定时器,用于接收超时控制
connect(readTimer,SIGNAL(timeout()),this,SLOT(readMyCom())); //定时器连接槽函数
UART_Init(); //串口初始化
QSocketNotifier *m_notifier;
m_notifier = new QSocketNotifier(fdUart, QSocketNotifier::Read, this);//新建一个QSocketNotifier对象,用于侦测串口是否有数据可读取
connect(m_notifier, SIGNAL(activated(int)), this, SLOT(remoteDataIncoming(void)));//QSocketNotifier对象连接槽函数
} MainWindow::~MainWindow()
{
delete ui;
} void MainWindow::remoteDataIncoming(void) //串口接收槽函数
{
char buff[8]={0,0,0,0,0,0,0,0}; //定义8字节的缓冲区
int lenth=0; //定义接收长度
lenth = read(fdUart,buff,8); //读取串口8字节到缓冲区buff中并返回实际长度
if(lenth>0 && lenth<8) //本次只接收到小于8个字节的数据
{
if(count == 0) //本帧数据小于8字节
{
len = lenth;
for(int i=0;i<lenth;i++)
read_data[i] = buff[i]; //把长度和内容赋值给全局变量
}
else //上次已经接收到至少8字节数据,本次为最后的小于8字节数据
{
readTimer->stop(); //关闭定时器
len = len + lenth;
for(int i=0;i<lenth;i++)
read_data[i+8*count] = buff[i]; }
readMyCom(); //只要接收数据少于8字节,即完成接收
}
else if(lenth>0) //接收了8个字节,本帧数据很可能不止8字节
{
readTimer->stop(); //关闭定时器
len = len + lenth;
for(int i=0;i<8;i++)
read_data[i+8*count] = buff[i];
count++; //count为接收到8字节数据的次数
readTimer->start(70); //设置定时为70ms
}
} void MainWindow::readMyCom(void) //定时超时槽函数
{
readTimer->stop(); //关闭定时器
ReadCom(len, read_data); //调用串口接收服务函数并把参数(实际长度及其内容)传递过去
for(int i=0;i<len;i++) //清空接收内容
read_data[i] = 0;
len = 0; //长度归零
count = 0; //次数归零
} void MainWindow::ReadCom(int leng, char data[]) //串口接收服务函数
{
ui->label->setText(QString::number(leng));
ui->label_2->setText(data);
} int MainWindow::UART_Init(void)
{
fdUart = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NDELAY);//打开串口,配置为可读可写、不分配为控制终端、无延时
printf("fcntl=%d\n",fcntl(fdUart,F_SETFL,0)); //让串口进入阻塞状态
printf("isatty=%d\n",isatty(STDIN_FILENO)); //确定是否为一个终端设备
Setup_Serial(fdUart, 9600, 8, 'N', 1); //进行串口相关配置
return fdUart;
} int MainWindow::Setup_Serial(int fd,int nSpeed, int nBits, char nEvent, int nStop)//串口配置函数
{
struct termios newtio,oldtio;
if (tcgetattr(fd,&oldtio) != 0)
{
perror("Setup Serial save error!");
return -1;
}
bzero(&newtio, sizeof(newtio));
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
switch(nBits)
{
case 5:
newtio.c_cflag |= CS5;
break;
case 6:
newtio.c_cflag |= CS6;
break;
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
switch(nEvent)
{
case 'O':
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E':
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case 'N':
newtio.c_cflag &= ~PARENB;
break;
}
switch(nSpeed)
{
case 1200:
cfsetispeed(&newtio, B1200);
cfsetospeed(&newtio, B1200);
break;
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 19200:
cfsetispeed(&newtio, B19200);
cfsetospeed(&newtio, B19200);
break;
case 38400:
cfsetispeed(&newtio, B38400);
cfsetospeed(&newtio, B38400);
break;
case 57600:
cfsetispeed(&newtio, B57600);
cfsetospeed(&newtio, B57600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
if(nStop == 1)
newtio.c_cflag &= ~CSTOPB;
else if (nStop == 2)
newtio.c_cflag |= CSTOPB;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("Setup Serial error!");
return -1;
}
printf("Setup Serial complete!\n");
return 0;
} void MainWindow::WriteCom(int leng, char data[])
{
write(fdUart, data, leng);
}

头文件内容如下。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H #include <QMainWindow>
#include <QTimer> namespace Ui {
class MainWindow;
} class MainWindow : public QMainWindow
{
Q_OBJECT public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow(); private:
Ui::MainWindow *ui;
QTimer *readTimer;
int UART_Init(void);
int Setup_Serial(int fd,int nSpeed, int nBits, char nEvent, int nStop);
void ReadCom(int leng, char data[]);
void WriteCom(int leng, char data[]); private slots:
void remoteDataIncoming(void);
void readMyCom(void);
}; #endif // MAINWINDOW_H

经过实验,上述代码在S3C2416+Linux3.6.6+Qt4.8.7的系统中,波特率从1200到115200,字符数量从1个到100个,都能够实现正常通信,并没有发生数据丢失的情况。

嵌入式Qt中实现串口读取的事件驱动方法的更多相关文章

  1. [转]Qt中定时器使用的两种方法

    Qt中定时器的使用有两种方法,一种是使用QObject类提供的定时器,还有一种就是使用QTimer类. 其精确度一般依赖于操作系统和硬件,但一般支持20ms.下面将分别介绍两种方法来使用定时器. 方法 ...

  2. <QT障碍之路>qt中使用串口类接收数据不完整

    问题:当用QT中的serial->readAll()的时候,不会把全部的数据一次性都读取出来,而是阶段性的.原因是因为当串口有信号时候,readyRead()信号就会被抛出,那么一帧完整的数据帧 ...

  3. labview初始学习过程中遇到串口读取框红蓝色交替闪烁的处理

           labview工程的程序框图VISA串口读取框红蓝交替闪烁,前面板接收数据错乱,或者是接受不了,这是你不小心设置了断点.

  4. QT中几个函数的使用方法

    一.把字符串转换成整形demo1:QString str = "FF";bool ok;int hex = str.toInt(&ok, 16); // hex == 25 ...

  5. Qt中中文字符 一劳永逸的解决方法

    QT中中文字符问题,有没有一劳永逸的解决方法? 目前遇到有以下问题 1.字符串中有中文时,编译提示"常量中含有换行符" 2.在控制台窗口输出中文时无法正常显示,中文全部显示为? 目 ...

  6. Qt中QMenu的菜单关闭处理方法

    Qt中qmenu的实现三四千行... 当初有个特殊的需求, 要求菜单的周边带几个像素的阴影, 琢磨了半天, 用QMenu做不来, 就干脆自己用窗口写一个 然而怎么让菜单消失却非常麻烦 1. 点击菜单项 ...

  7. spring应用中多次读取http post方法中的流(附源码)

    一.问题简述 先说下为啥有这个需求,在基于spring的web应用中,一般会在controller层获取http方法body中的数据. 方式1: 比如http请求的content-type为appli ...

  8. Qt中的串口编程之一

    QtSerialPort 简介 功能介绍 SerialPort SerialPortInfo 源代码 编译和安装 配置编译环境 Perl只是在Qt5的时候才需要Qt4的情况下可以不配置 使用如下推荐步 ...

  9. Qt中的串口编程之三

    QtSerialPort 今天我们来介绍一下QtSerialPort模块的源代码,学习一下该可移植的串口编程库是怎么实现的. 首先,我们下载好了源代码之后,使用QtCreator打开整个工程,可以看到 ...

  10. Qt中让Qwidget置顶的方法

    一般来是说窗体置顶和取消只要        setWindowFlags(Qt::WindowStaysOnTopHint);        setWindowFlags(Qt::Widget); 要 ...

随机推荐

  1. 档案系统区块链集成 leveldb.net集成

    leveldb.net工作原理:leveldb为键值对数据库,具有增加,删除,查询功能,利用加密链式结构存储和查询数据. 区块(block):在区块链技术中,数据以电子记录的形式被永久储存下来,存放这 ...

  2. js正则匹配多行文本

    原文:https://lwebapp.com/zh/post/regular-expression-to-match-multiple-lines-of-text 需求 最近有小伙伴提了个需求,想用正 ...

  3. go环境 依赖管理 基本命令

    Go安装 Go官网下载地址:https://golang.org/dl/ Go官方镜像站(推荐):https://golang.google.cn/dl/ Windows 选择Windows版本下载安 ...

  4. (jmeter笔记) websocket接口测试

    1.在进程选择WebSocket Sampler 2.Websocket Sampler 界面 webserver Server Name or IP:输入连接的websocket服务器ip Port ...

  5. CompletableFuture使用方法的详细说明

    异步执行一个任务时,我们一般是使用自定义的线程池Executor去创建执行的.如果不需要有返回值, 任务实现Runnable接口:如果需要有返回值,任务实现Callable接口,调用Executor的 ...

  6. 一、100ASK_IMX6ULL嵌入式裸板学习_LED实验(知识点补充二)

    MUX是什么?   MUX(multiplexer数据选择器):   在多路数据传送过程中,能够根据需要将其中任意一路选出来的电路,叫做数据选择器,也称多路选择器或多路开关.   多路转换器的作用主要 ...

  7. Go--求数组奇偶数之和

    package main //申明main包 import "fmt" // 导入fmt标准库 func main() { arr := [...]int{01, 11, 22, ...

  8. selenium+python的网站爬虫

    爬取网站听起来就是程序员的标配,之前一直没有时间学一下,最近有空学习一下顺便记录一下 爬取网站实际上就是利用计算机模拟人的操作来对网站的前端进行访问,而各大浏览器也给计算机提供了访问的接口,也就是浏览 ...

  9. 了解了一下Cookie

    昨天做接口测试被Cookie折腾得云里雾里的,今天下午有时间特意了解了一下. 一:Edge浏览器查看Cookie的路径:设置->Cookie和网站权限 二:一个cookies包含以下信息:(1) ...

  10. 记录POI导入时单元格下拉框两种实现方式(excel数据有效性)

    如果下拉选项字符少于225 使用方式1 public static HSSFSheet setHSSFValidation(HSSFSheet sheet, String[] textlist, in ...