串口通信--树莓派、QT

整体介绍:这是一个用QT编写的,运行在树莓派上的小程序,当然运行在PC上也是可以的

一、树莓派准备工作

1、树莓派的蓝牙和GPIO口的UART不可兼得

树莓派3上用户目前无法正常是使用GPIO中的UART串口(GPIO14&GPIO15),,原因是树莓派CPU内部有两个串口,一个是硬件串口(官方称为PL011 UART),一个是迷你串口(官方成为mini-uart)。在树莓派2B/B+这些老版树莓派上,官方设计时都是将“硬件串口”分配给GPIO中的UART(GPIO14&GPIO15),因此可以独立调整串口的速率和模式。而树莓派3的设计上,官方在设计时将硬件串口分配给了新增的蓝牙模块上,而将一个没有时钟源,必须由内核提供时钟参考源的“迷你串口”分配给了GPIO的串口,这样以来由于内核的频率本身是变化的,就会导致“迷你串口”的速率不稳定,这样就出现了无法正常使用的情况。目前解决方法就是,关闭蓝牙对硬件串口的使用,将硬件串口重新恢复给GPIO的串口使用,也就意味着树莓派3的板载蓝牙和串口,现在成了鱼和熊掌,两者无法兼得。

解决办法:

查看下当前系统的串口信息
在这里插入图片描述
编辑/boot目录下的config.txt文件

sudo vim/boot/config.txt

末行添加

dtoverlay=pi3-miniuart-bt

注意:"pi3-miniuart-bt"是在文件夹/boot/overlays中可以找到的。如果没有,你可以下载一个"pi3-miniuart-bt-overlay"文件并将其拷贝至/boot/overlays文件夹中,并且将下面的语句更改为:dtoverlay=pi3-miniuart-bt-overlay即可。

再编辑cmdline.txt文件

sudo gdit /boot/cmdline.txt

修改以下内容:

dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes  rootwait

保存重启即可。
查看下重启后的串口信息
在这里插入图片描述
出现两个串口,表示操作成功

我们不再使用蓝牙,所以为了节省能耗,关掉蓝牙

sudo systemctl diable hciuart
sudo vim /lib/systemd/system/hciuart.service

将ttyAMA0改成ttyS0,如下图:
在这里插入图片描述
安装串口工具minicom

sudo apt-get install minicom

运行工具

sudo minicom -s

选择第三项
在这里插入图片描述
配置参数
在这里插入图片描述
先内部环回测试,短接GPIO14和GPIO15,在mincom时打开回显(Ctrl+A,再全部松开按Z,跳出配置界面按E)

输入一个在字符,先回显字符,然后自己又接收到字符,如下图测试成功
在这里插入图片描述
对接电脑的USB转TTL,树莓派RX对TTL的TX,树莓派TX对TTL的RX,测试成功,如下图:
在这里插入图片描述
电路原理图
在这里插入图片描述
至此,树莓派串口就配置好了

2、树莓派安装QT并配置模块

sudo apt-get update
sudo apt-upgrade(耗时,占空间)非必要步骤
sudo rpi-update

安装qt5-default

sudo apt-get install qt5-default

安装qtcreator

sudo apt-get install qtcreator

3、安装QtSerialPort模块

sudo apt-get install libqt5serialport5-dev libudev-dev

模块有没有安装成功在pro工程文件中添加QT += serialport如果没有报错就说明模块安装成功,可以进行一下一步开发了

二、代码编写

1、新建QT工程,并在工程文件中导入模块

QT       += serialport

在这里插入图片描述

2、导入包并创建串口对象

#include <QSerialPort>
QSerialPort *serialport;
serialport = new QSerialPort;

3、串口初始化代码

serialport->close();
    serialport->setPortName("ttyAMA0");
    serialport->setBaudRate(9600);
    serialport->setDataBits(QSerialPort::Data8);
    serialport->setParity(QSerialPort::NoParity);
    serialport->setStopBits(QSerialPort::OneStop);
    serialport->setFlowControl(QSerialPort::NoFlowControl);
    if(serialport->open(QIODevice::ReadWrite))
       qDebug()<<"Uart Init successful!";
    else
        qDebug()<<"Uart Init failed!";
    connect(serialport,SIGNAL(readyRead()),
            this,SLOT(uart_recv()));

4、串口发送代码

QString msg=ui->textEdit_write->toPlainText();
QByteArray array = msg.toUtf8();
serialport->write(array);

5、串口接收代码

QByteArray buf = serialPort->readAll();

由于串口每次是发送8个字节,由于分时操作系统的特性,如果一次发送的数据很大,上面那样操作会照成数据会分次接收,为了解决上面的问题,我们采用如下方案:

QByteArray buf = serialPort->readAll();
if(!buf.isEmpty())
{
     M2=buf;
     M1+=M2;
}
if(M1.contains("@",Qt::CaseSensitive) && M1.contains("#",Qt::CaseSensitive))
    {
        int s,q;
        s=M1.indexOf("@");q=M1.indexOf("#");
        QString M3=M1.mid(s+1,q-1);
        QString M4=M1.mid(s,q+1);
        M1="";M2="";
        QStringList List=M3.split(";");
        if(List.length()==11 && List[0]==QString::fromLocal8Bit("data"))
        {
             QByteArray tcp_data = M4.toUtf8();
             tcpsocket->write(tcp_data);
             QString D1,D2,D3,D4,D5,D61,D62,D63,D64;
             D1=List[2];D2=List[3];D3=List[4];D4=List[5];D5=List[6];D61=List[7];D62=List[8];D63=List[9];D64=List[10];
             ui->lineEdit_show_Heart_rate_2->setText(D1+" BPM");ui->lineEdit_show_Boold_oxygen_2->setText(D2+" %");ui->lineEdit_show_Alcoholic_2->setText(D3+" mg/mL");ui->lineEdit_show_Temperture_2->setText(D4+" ℃");
             ui->lineEdit_show_Boold_press_2->setText(D5+" mmHg");ui->lineEdit_show_Boold_fat_1->setText(D61+" mmol/L");ui->lineEdit_show_Boold_fat_2->setText(D62+" mmol/L");
             ui->lineEdit_show_Boold_fat_3->setText(D63+" mmol/L");ui->lineEdit_show_Boold_fat_4->setText(D64+" mmol/L");
             //draw plot
             CurrentData1=D1.toFloat(),CurrentData2=D2.toFloat(),CurrentData3=D3.toFloat();CurrentData4=D4.toFloat();
             CurrentData5=D5.toFloat(),CurrentData61=D61.toFloat();CurrentData62=D62.toFloat();CurrentData63=D63.toFloat();CurrentData64=D64.toFloat();
             ShowLine1(ui->Heart_rate);ShowLine2(ui->Boold_oxygen);ShowLine3(ui->Alcoholic_strength);
             ShowLine4(ui->Temperture);ShowLine5(ui->Boold_pressure);ShowLine6(ui->Boold_fat);
        }
    }

这里M1和M2是两个全局变量

原理如下:

因为只要检测到有数据发送过来,就会有信号产生,然后就会执行槽函数。所以我把接收到的数据先暂时追加在一个缓冲区里,就是M1,然后当我检测到M1中以及有我自定义的报文:@data;xxx;xxx;xxx#,我就将我的报文从缓存区M1中截取出来,然后将缓存区清空,这样操作的话,无论数据有多长,都可以接收到完整的数据了。

发布了48 篇原创文章 · 获赞 5 · 访问量 2637

猜你喜欢

转载自blog.csdn.net/Mr_robot_strange/article/details/104288684