QT线程编程-串口收发

下面讲述原理,也可以下载完整实验源码: 源码下载

前言

QT没有提供串口类,使用的是linux提供的函数。
linux下设备和文件使用一样,用open函数打开串口,设置等都提供了函数的,需要自己组合一下,封装成你要的函数。

1.QT串口发送数据

要使用串口,需先打开串口,封装的函数如下:

int open_port(constchar* dev_path)
{
int fd;

//open uart
    fd = open(dev_path, O_RDWR|O_NOCTTY|O_NDELAY);
if(fd <0)
{
        perror("open serial port");
return-1;
}
if(fcntl(fd, F_SETFL,0)<0)//设置为阻塞模式,后面启动的线程会阻塞,串口有数据才读
        perror("fcntl F_SETFL\n");
/*if(isatty(STDIN_FILENO) == 0)//再次验证是否为终端设备,我用的刷卡器,屏蔽了这个,不然会失败
    {
        perror("standard inpput is not a terminal device");
    }*/

return fd;
}


接下来需要设置串口的波特率、校验、结束位等,封装的函数如下:

int set_com_config(int fd,int baud_rate,int data_bits,char parity,int stop_bits)
{
struct termios new_cfg, old_cfg;
int speed;
int returnvalue;

    returnvalue = tcgetattr(fd,&old_cfg);//save current uart configure
if(returnvalue <0)
{
        perror("tcgetattr:tcsetattr()1");
return-1;
}

    new_cfg = old_cfg;//直接获取串口的所有数据到新配置,省去了一些参数的手动设置
    cfmakeraw(&new_cfg);//config as orignal mode: recevice byte one by one
    new_cfg.c_cflag &=~CSIZE;//clear c_cflag

switch(baud_rate)
{
case2400:
        speed = B2400;break;
case4800:
        speed = B4800;break;
case9600:
        speed = B9600;break;
case19200:
        speed = B19200;break;
case38400:
        speed = B38400;break;
default:
case115200:
        speed = B115200;break;
}
    cfsetispeed(&new_cfg, speed);//set in baud_rate
    cfsetospeed(&new_cfg, speed);//set out baud_rate;

switch(data_bits)//set data bits
{
case7:
        new_cfg.c_cflag |= CS7;break;
default:
case8:
        new_cfg.c_cflag |= CS8;break;
}

switch(parity)//set parity
{
default:
case's':
case'S':
{
        new_cfg.c_cflag &=~PARENB;
        new_cfg.c_cflag &=~INPCK;
}break;

case'o':
case'O':
{
        new_cfg.c_cflag |=(PARODD | PARENB);
        new_cfg.c_cflag |= INPCK;
}break;

case'e':
case'E':
{
        new_cfg.c_cflag |= PARENB;
        new_cfg.c_cflag &=~PARODD;
        new_cfg.c_cflag |= INPCK;
}break;

case'n':
case'N':
{
        new_cfg.c_cflag &=~PARENB;
        new_cfg.c_cflag &=~CSTOPB;
}break;

}

switch(stop_bits)//set stop bits
{
default:
case1:
        new_cfg.c_cflag &=~CSTOPB;break;
case2:
        new_cfg.c_cflag |= CSTOPB;break;
}

//set wait time and min recevice byte
    new_cfg.c_cc[VTIME]=0;
    new_cfg.c_cc[VMIN]=1;

    tcflush(fd, TCIFLUSH);//清除数据

    returnvalue = tcsetattr(fd, TCSANOW,&new_cfg);//激活配置
if(returnvalue <0)
{
        perror("tcsetattr:tcsetattr()2");
return-1;
}

return0;
}



发送数据:

write(fd_com, buff, strlen(buff));//发送

2.QT线程编程

接收数据需要用一个线程,比较方便。
新建一个类,继承于QThread, 实现其中的 run 虚函数就大功告成,   用的时候创建该类的实例,调用它的 start 方法即可。但是 run 函数使用时有一点需要注意,即在其中不能创建任何 gui 线程(诸如新建一个 QWidget 或者 QDialog )。如果要想通过新建的线程实现一个 gui 的功能,那么就需要通过使用线程间的通信来实现。
自己实现run函数,start()函数不用自己写了哈。

.h文件如下:

#ifndef RECEIVETHREAD_H
#define RECEIVETHREAD_H

#include<QThread>

classReceiveThread:publicQThread
{
    Q_OBJECT
public:
explicitReceiveThread(QObject*parent =0);
void run();
void enStopFlag();
void enBaudChanged_flag();

int fd;
int stop_flag;
char buff[100];
int baudChanged_flag;

signals:
void sendbuff(QString);

public slots:

};

#endif// RECEIVETHREAD_H


run函数如下:

voidReceiveThread::run()
{
    qDebug()<<"Receive thread started";

int count_receive=0, c=0;
QString data;

    memset(buff,0,100);
    data.clear();

while(stop_flag)
{
        c = read(fd, buff,100);

if(baudChanged_flag)
{
            data.clear();
            memset(buff,0,100);
            count_receive =0;
            baudChanged_flag =0;
continue;
}

if(c >=0)
            data += buff;
elsecontinue;

        memset(buff,0,100);
        count_receive += c;
if(count_receive <14)
continue;

        data = data.mid(1,10);
        emit sendbuff(data);

        data.clear();
        count_receive =0;
        memset(buff,0,100);
}

    qDebug()<<"Receive thread exited";
}


在mainwindow里面申请一个线程对象,thread->start(),线程就开始监听串口了。

3.QT串口接收数据

上面的线程开始监听了,只要串口设置对了,就可以读串口的数据了,上面程序是收到14个才发送给我的程序用。
我用来读卡的,所以有一些处理。

4.ARM串口收发方便调试的接法

ID读卡器波特率9600,无校验码,停止位1,出口只有TXD和RXD和GND。

读卡器用PC的USB供电
将ARM的GND和PC的GND连接,全部共地;
将读卡器的TXD接到ARM的RXD,读卡器将读到的卡号发给ARM;
将ARM的TXD接到PC的RXD,ARM将你设置的内容发给PC;
这样就可以在ARM上显示读取的卡号,在PC上显示ARM给出的调试信息,主要是同时验证程序的收发是否正确。

下载完整实验源码: 源码下载

接法如图:


 

 

5.线程的结束

有时候需要结束线程,但是如果设置为读串口数据为阻塞模式,则不能立即结束线程,要换个方法,如下:
kill线程  某线程 ->terminate();

kill线程参考资料: http://qimo601.iteye.com/blog/1287279 

以下内容引用自参考资料:(begin到end)

<begin> 以上代码简单,没有必要做过多的讲解,但是其中的“thread->terminate(); ”有必要讲解下,terminate()函数的调用便不会立刻终止线程,因为线程的何时终止取决于系统的调度策略,所在在之后又调用了wait()函数是线程阻塞等待直到退出或者超时。 最后加以一点就是在.pro文件中加入一行代码才能成功运行: CONFIG+=thread <end>



版权声明:本文为lovenessless原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。