串行接口简称串口,也称串行通信接口或串行通讯接口(通常指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
本例给出的只是一个最小框架,在具体应用时,还要根据实际的项目进行适当的修改。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章