串口连接:
要写上位机,必不可少的就是串口的监听,接收到的一定格式的数据,然后读取、写入数据库。本次课程设计的用到的发送格式为"ID=xxx;T=xx.x;H=xx;"接收数据最麻烦就是接收的不完整的数据,导致读取数据库失败,所以发送格式就显得比较重要。只要学会串口的连接,写个串口调试助手也是非常的简单,本次课程设计我就是在程序中嵌入了Qt写的串口调试助手:
串口连接:
新建一个类为serialPort,包含一个QSerialPort的实例serial,头文件和代码:
#include<QtSerialPort/QSerialPort>
serial是QSerialPort的一个实例化
if("开启串口"==ui->openorclose->text())
{
serial.setPortName(ui->port->currentText()); //设置端口
//设置波特率
switch (ui->setBaudRate->currentIndex()) {
case 0:
serial.setBaudRate(QSerialPort::Baud115200);
break;
case 1:
serial.setBaudRate(QSerialPort::Baud19200);
break;
case 2:
serial.setBaudRate(QSerialPort::Baud9600);
break;
case 3:
serial.setBaudRate(QSerialPort::Baud4800);
break;
default:
break;
}
//设置校验位
switch (ui->setParity->currentIndex()) {
case 0:
serial.setParity(QSerialPort::NoParity);
break;
case 1:
serial.setParity(QSerialPort::OddParity);
break;
case 2:
serial.setParity(QSerialPort::EvenParity);
break;
default:
break;
}
//设置数据位数
switch (ui->bit->currentIndex()) {
case 0:
serial.setDataBits(QSerialPort::Data8);
break;
case 1:
serial.setDataBits(QSerialPort::Data7);
break;
case 2:
serial.setDataBits(QSerialPort::Data6);
break;
default:
break;
}
//设置停止位
switch (ui->stop->currentIndex()) {
case 0:
serial.setStopBits(QSerialPort::OneStop);
break;
case 1:
serial.setStopBits(QSerialPort::TwoStop);
break;
default:
break;
}
if(serial.open(QIODevice::ReadWrite)) //打开串口
{
serial.setFlowControl(QSerialPort::NoFlowControl); //设置无流控制
ui->openorclose->setText("关闭串口");
QMessageBox *tip=new QMessageBox;
tip->setAttribute(Qt::WA_DeleteOnClose);
tip->question(this,"提示","串口开启成功",QMessageBox::Ok);
}
else
{
QMessageBox *tip=new QMessageBox;
tip->setAttribute(Qt::WA_DeleteOnClose);
tip->question(this,"警告","串口打开失败",QMessageBox::Ok);
return;
}
}
else //关闭串口
{
serial.clear();
serial.close();
ui->openorclose->setText("开启串口");
}
接收数据:
可以接收数据时,QSerialPort的实例会产生一个readyRead信号,所以我们需要处理这个信号,才能知道什么时候开始接收数据。
处理QSerialPort实例的信号:
connect(&serial,&QSerialPort::readyRead,this,[=](){
RecvData();
});//RecvData是this对象的一个成员函数
RecvData函数实体:
需要在头文件包含#include<QByteArray>
void serialPort::RecvData() //接收数据
{
static QByteArray buf;
buf+=serial.readAll();
if(buf.startsWith("\"")&&buf.endsWith("\"")) //判断数据是否完整
{
QString str;
//str.prepend(buf); //转为Qstring
str=QString::fromLocal8Bit(buf); //utf-8
ui->recv_data->clear();//这是一个textEdit
ui->recv_data->append(str);
}
else //不完整数据结束
return;
buf.clear();
}
startsWith()是判断字节数组中的开始是否是其参数,是返回true,endsWith()同理。我是判断数据的开头和结束是否和格式一致,一致的则读取,若不一致就抛弃,这仅是适用于该次课程设计,若要通用的串口助手,可以考虑判断结束符"\n\r"是否存在。fromLocal8Bit()可以支持中文的转换,不会出现乱码。
发送数据:
QSerialPort的实例有一个write()方法是用于发送数据的。
void serialPort::SendData() //发送数据
{
serial.write(ui->send_data->text().toLocal8Bit()); //转化为utf-8编码
}
自动发送:
Qt有一个QTimer的类,这是一个定时器,当时间溢出时,会产生超时信号,处理这个信号就可以达到自动发送功能。
实例化一个QTimer:
QTimer Timer; //定时器
在serialPort的构造函数中绑定事件:
connect(&Timer,QTimer::timeout,this,SendData); //绑定定时器的超时事件
当然,只有用户选择自动发送时,我们才自动发送。在ui设计器中添加一个复选框CheckBox和一个时间周期输入框LineEdit,当用户勾选时,再自动发送。所以我们需要判断CheckBox的状态,通过他的状态改变再做出相应操作:
//绑定复选框事件
connect(ui->checkBox,&QCheckBox::stateChanged,this,[=](){
if(ui->checkBox->isChecked())
{
std::string time_str=ui->time->text().toStdString();//获取输入时间
const char *time=time_str.c_str(); //转为char*
while(*time!='\0') //判断输入的时间周期是否为合法的
{
if(*time>='0'&&*time<='9')
time++;
else
{
[=](){
QMessageBox *tip=new QMessageBox;
tip->setAttribute(Qt::WA_DeleteOnClose);
tip->question(this,"错误","非法时间",QMessageBox::Ok);
}();
return;
}
}
ui->send->setEnabled(false); //停止功能
Timer.start(ui->time->text().toInt()); //开启定时器
}
else
{
ui->send->setEnabled(true); //恢复功能
Timer.stop();
}
});
至此,一个简单的串口调试助手就可以完成了,建立虚拟串口进行测试:
接收数据:
发送格式为:"xxxxxx"
发送数据: