Qt中的串口编程
串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口(Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信,大大降低了成本,特别适用于远距离通信,但传送速度较慢。根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。串行接口按电气标准及协议来分包括RS-232-C、RS-422、RS485等。
异步串行是指UART(Universal Asynchronous Receiver/Transmitter),通用异步接收/发送。UART包含TTL电平的串口和RS232电平的串口。 TTL电平是3.3V的,而RS232是负逻辑电平,它定义+5~+12V为低电平,而-12~-5V为高电平。UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输,其中各位的意义如下:
起始位:先发出一个逻辑”0”的信号,表示传输字符的开始。
数据位:紧接在起始位之后,数据位的个数可以是4、5、6、7、8等,构成一个字符,从最低位开始传送,靠时钟定位。
奇偶校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。
停止位:它是一个字符数据的结束标志,可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
空闲位:处于逻辑“1”状态,表示当前线路上没有数据传送。
此外,在异步通信中还有一个重要的参数,即波特率,它是衡量数据传送速率的指标,表示每秒钟传送的符号数(symbol)。收发双方的波特率必须保持一致,才能保证数据的正常通信。
在通信程序中,一般来说如何获取收到的信息是一个问题。因为接收是被动的,即不知道对方什么时候会发送信息来,所以需要一直对接收端进行侦听,看是否有信息到来,这样就需要CPU反复地进行查询,会消耗过多的资源。所以,在硬件层面一般会使用中断方式来进行接收侦听,而不会采用查询方式。同样,在基于操作系统的编程中,对于接收也有查询和事件两种方式。查询方式同样需要反复地对接收进行读取,看是否收到了信息,同样会消耗过多的系统资源,一般会把它放在一个线程中来进行,以保证系统资源不会被过度地消耗掉。另一种是事件方式,即当接收端收到信息时,会采用事件的方式来通知主程序,以对收到的信息进行相应地处理。事件方式有点类似硬件上的中断方式,效率较高。
在基于Qt的串口编程中,有多种实现的方式。比如可以使用Qt自己带的串口模块(如QSerialPort),也可以使用其他第三方的控件(如qextserialport等),还可以使用操作系统中直接编写的串口程序。各种方式都有自己的特点,但对于Qt自带的QSerialPort模块,虽然兼容性较好,但目前只有Qt5.0以上的版本才支持该模块。对于第三方控件qextserialport,虽然在Qt4.0的版本就可以使用,但目前对于Linux系统而言,仅支持查询方式,而不支持事件方式,所以在Linux中一般只能把它放到一个新建的线程中去运行。对于在操作系统中直接编程处理串口通信的方式,需要一定的程序开发能力,但适用性更广泛一些,执行的效率也要高一些。
在Qt4.0及以上的版本中,新增加了一个名为QSocketNotifier的模块,它用来监听系统文件的操作,把操作转换为Qt事件进入系统的消息循环队列,并调用预先设置的事件接受函数来处理事件。QSocketNotifier一共设置了三类事件:read、write、exception,具体如下表所示。
在使用QSocketNotifier来处理串口时,只需要设置为Read属性即可。每个QSocketNotifie对象监听一个事件,在使用open方法打开串口并设置好属性后,就可以使用Qt的类 QSocketNotifier来监听串口是否有数据可读,它是事件驱动的,配合Qt的信号/槽机制,当有数据可读时,QSocketNotifier就会发射ativated信号,只需要创建一个槽连接到该信号即可,然后在槽函数中处理串口读到的数据。这样一来,读取串口的任务就不用新开辟线程来处理了,这也是Qt官方给出的建议。
在Linux系统中,一切设备都可以按文件来处理,这一机制也正好符合QSocketNotifie监听系统文件变化的机制。因此,在Linux系统中使用QSocketNotifie来处理串口接收数据,可谓相得益彰。因此,下面讨论的就是在Linux系统中,基于Qt4.8.7的版本,结合Linux的串口模块编程,然后在Qt中使用QSocketNotifie来实现串口信息的读取。下面给出具体步骤。
首先,在Linux系统中,所有的设备文件都位于/dev目录下,以tty为前缀的文件,是终端设备。以tty加大写S开头的即为串口设备文件,如ttyS0即为串口0(即COM0)。但在嵌入式Linux中,串口是以ttySAC为前缀的,如ttySAC0即代表串口0。
其次,在Linux系统中,要对文件进行相应地操作,必须先打开它,对串口设备文件也不例外,所以需要调用open函数来打开对应的串口端口。另外在打开的同时,还需要设置打开文件的合适属性(如可读可写、不将设备分配为控制终端、无延时模式等)。 比如,可执行语句“open("/dev/ttySAC0", O_RDWR | O_NOCTTY | O_NDELAY);”。
接下来,需要对打开的串口进行配置,其中的重要参数有:波特率、数据位数、停止位位数、奇偶校验、硬件流量控制等。在Linux中,这些配置项目由一个名为termios 结构体实现,结合前面的打开串口,可以一起写在一个串口初始化函数中,如下 。
int MainWindow::UART_Init(void)
{
struct termios opt;
fdUart = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NDELAY);
tcgetattr(fdUart, &opt); //获取当前打开的串口参数到termios型结构体mycom中
opt.c_cflag &= ~(PARENB | CSTOPB | CSIZE | OPOST);//无奇偶校验、1位停止位、原始模式
opt.c_cflag |= (CS8 | CLOCAL | CREAD); //8位数据位
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600); //波特率为9600
opt.c_cc[VMIN] = 0;
opt.c_cc[VTIME] = 0;
tcflush(fdUart,TCIOFLUSH); //清除输出输入缓冲
if (tcsetattr(fdUart, TCSANOW, &opt) < 0) //使用mycom中的参数设置当前已打开的串口,不等数据传输完毕就立即改变
{
return -1;
}
return fdUart;
}
接下来需要在Qt的主程序中,加入串口初始化,创建一个QSocketNotifier类的对象并将其实例化,建立信号与槽的连接函数,如下。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
UART_Init();
QSocketNotifier *com0_notifier;
com0_notifier = new QSocketNotifier(fdUart, QSocketNotifier::Read, this);
connect(com0_notifier, SIGNAL(activated(int)), this, SLOT(SerialIncoming(void)));
}
最后,还要写一个槽函数来读取串口数据,如下。
void MainWindow::SerialIncoming(void)
{
char buff[8];
read(fdUart,buff,8);
}
在上述程序中,文件描述符fdUart应定义为全局变量。程序运行后,只要串口接收到数据,就会触发SerialIncoming函数,然后把收到的数据存入buff数组中,以供后续处理之用。当然,若要向串口发送数据,直接写fdUart即可,比如执行“write(fdUart,buff,8);”即可把buff中的8个字节数据及时发送出去。
为了让上述程序能够正常运行,还需要包含下面的三个头文件。
#include <QSocketNotifier>
#include <fcntl.h>
#include <termios.h>
本例给出的只是一个最小框架,在具体应用时,还要根据实际的项目进行适当的修改。
Qt中的串口编程的更多相关文章
- Qt中的串口编程之三
QtSerialPort 今天我们来介绍一下QtSerialPort模块的源代码,学习一下该可移植的串口编程库是怎么实现的. 首先,我们下载好了源代码之后,使用QtCreator打开整个工程,可以看到 ...
- Qt中的串口编程之一
QtSerialPort 简介 功能介绍 SerialPort SerialPortInfo 源代码 编译和安装 配置编译环境 Perl只是在Qt5的时候才需要Qt4的情况下可以不配置 使用如下推荐步 ...
- QT中的SOCKET编程(QT-2.3.2)
转自:http://mylovejsj.blog.163.com/blog/static/38673975200892010842865/ QT中的SOCKET编程 2008-10-07 23:13 ...
- QT中的SOCKET编程
转自:http://mylovejsj.blog.163.com/blog/static/38673975200892010842865/ QT中的SOCKET编程 2008-10-07 23:13 ...
- Qt中的多线程编程
http://www.ibm.com/developerworks/cn/linux/l-qt-mthrd/ Qt 作为一种基于 C++ 的跨平台 GUI 系统,能够提供给用户构造图形用户界面的强大功 ...
- <QT障碍之路>qt中使用串口类接收数据不完整
问题:当用QT中的serial->readAll()的时候,不会把全部的数据一次性都读取出来,而是阶段性的.原因是因为当串口有信号时候,readyRead()信号就会被抛出,那么一帧完整的数据帧 ...
- 5.关于QT中的网络编程,QTcpSocket,QUdpSocket
1 新建一个项目:TCPServer.pro A 修改TCPServer.pro,注意:如果是想使用网络库,需要加上network SOURCES += \ TcpServer.cpp \ T ...
- qt中的udp编程
UDP QUdpSocket ---> upd socket 1.创建 QUdpSocket *p = new QUdpSocket(); 2.最先接收数据一方 调用bind-> ip/p ...
- qt中的tcp编程
server .server.h #define DIALOG_H #include <QDialog> #include <QTcpServer> #include < ...
- Qt 多线程和网络编程学习
一,Qt多线程类学习 QThread类,开始一个新的线程就是开始执行重新实现QThread::run(),run()是默认现实调用exec(),QThread::start()开始线程的执行,run( ...
随机推荐
- golang 中mgo update报错: The dollar ($) prefixed field '$inc' in '$inc' is not valid for storage.
_, err := c.Find(bson.M{"index": index}).Apply(mgo.Change{ Upsert: true, ReturnNew: false, ...
- Linux磁盘与文件系统
Linux磁盘与文件系统 我们使用过windows,相信大家对磁盘的概念都有所了解,像c盘d盘e盘,对吧,磁盘的作用是什么呢,作为整个系统的载体,磁盘承担了对系统中所有数据和文件存储的任务,并且可以保 ...
- datax缺少clickhouse reader插件
背景:想要把click house的数据源同步到clickhouse,发现Datax没有clickhousereader组件. 1.把clickhousewriter/libs下的所有jar包复制到r ...
- CPU性能测试-coremark
测试CPU性能指标会用到 Benchmarks常见的为 Dhrystone 和 CoreMark. CoreMark是一个综合基准,用于测量嵌入式系统中使用的中央处理器(CPU)的性能.它是在2009 ...
- Qt ui_xxx.h no file or directory
今天是2023年1.19,22号就过年了,先祝大家新年快乐! 首先经过这几天的研究,出现这个问题,提示其实已经很明显了,就是没找到文件,那么为什么没找到文件呢?基本上就是编译的时候没有找到相应的文件, ...
- 【Linux】常用
查看端口占用 yum install lsof lsof -i:8080:查看8080端口占用 lsof abc.txt:显示开启文件abc.txt的进程 lsof -c abc:显示abc进程现在打 ...
- [Swift] 在 OC 工程中,创建和使用 Swift
1.我们创建了一个 Objective-C 的工程,叫做 playGround. 2.首先,我们需要在 工程的 Build Settings,找到 如中所示的项目,并将 Defines Module ...
- Window10上CLion极简配置教程
Window10上CLion极简配置教程 1. 首先,下载安装CLion 去JB公司官网下载CLion,https://www.jetbrains.com/clion/download/#sectio ...
- Delphi中Stringlist的自定义排序(将函数地址做为参数)
近日,在编制一个程序过程,因为数据量较小,就使用了stringlist来暂存数据.在使用过程中,遇到了一个问题.Stringlist字符串列表的默认排序方法是按ASCII码的方式进行排序,如3,10, ...
- css可继承与不可继承的属性
一.可继承性的属性 字体相关的:font-size/font-family/font-weight/font-style/font-variant/font-stretch 文本相关的:color/t ...