Qt中的串口编程

  • 格式:pdf
  • 大小:143.23 KB
  • 文档页数:2

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

#include

#include

本例给出的只是⼀个最⼩框架,在具体应⽤时,还要根据实际的项⽬进⾏适当的修改。