编程语言
首页 > 编程语言> > Qt中的串口编程

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_NONBLOCK | O_NDELAY);”。

接下来,需要对打开的串口进行配置,其中的重要参数有:波特率、数据位数、停止位位数、奇偶校验、硬件流量控制等。在Linux中,这些配置项目由一个名为termios 结构体实现,结合前面的打开串口,可以一起写在一个串口初始化函数中,如下 。 

int MainWindow::UART_Init(void)
{
struct termios mycom;
fdUart = open("/dev/ttySAC0", O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
tcgetattr(fdUart, &mycom);
cfsetospeed(&mycom, B115200);          //波特率为115200
mycom.c_cflag &= ~(PARENB | CSTOPB | CSIZE);     //无奇偶校验、1位停止位
mycom.c_cflag |= (CS8 | CLOCAL | CREAD);     //8位数据位
mycom.c_cc[VMIN] = 0;
mycom.c_cc[VTIME] = 0;
tcflush(fdUart,TCIOFLUSH);
if (tcsetattr(fdUart, TCSANOW, &mycom) < 0)
{
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个字节数据及时发送出去。

当然,上面给出的只是一个最小框架,具体应用时,要根据实际的项目进行灵活修改。 

标签:mycom,Qt,fdUart,编程,QSocketNotifier,Linux,串口
来源: https://www.cnblogs.com/fxzq/p/14406501.html