本示例使用的发卡器:https://item.taobao.com/item.htm?spm=a21dvs.23580594.0.0.52de2c1bVIuGpf&ft=t&id=18645495882
一、确定已安装Qt Serial Port组件
二、在.pro项目文件声明引用Serialport组件
三、在.h头文件内引用Serialport组件
四、在.cpp程序中实列化serial,并绑定串口接收数据的槽函数
五、打开串口并设置波特率、数据位、校验位、停止位
void MainWindow::on_Butt_Open_clicked()
{if(ui->Butt_Open->text() == "打开串口"){//设置串口为下拉栏所选串口名serial->setPortName(ui->Com_Serial->currentText());//设置波特率switch (ui->Com_Baud->currentIndex()){case 0: serial->setBaudRate(QSerialPort::Baud4800); break;//4800case 1: serial->setBaudRate(QSerialPort::Baud9600); break;case 2: serial->setBaudRate(QSerialPort::Baud19200); break;case 3: serial->setBaudRate(QSerialPort::Baud38400); break;case 4: serial->setBaudRate(QSerialPort::Baud57600); break;case 5: serial->setBaudRate(QSerialPort::Baud115200); break;default: break;}//设置数据位数switch (ui->Com_Size->currentIndex()){case 0:serial->setDataBits(QSerialPort::Data5); break;//1位case 1:serial->setDataBits(QSerialPort::Data6); break;case 2:serial->setDataBits(QSerialPort::Data7); break;//1位case 3:serial->setDataBits(QSerialPort::Data8); break;default: break;}//设置校验位switch (ui->Com_Parity->currentIndex()){case 0:serial->setParity(QSerialPort::NoParity); break;//无校验位case 1:serial->setParity(QSerialPort::OddParity); break;case 2:serial->setParity(QSerialPort::EvenParity); break;case 3:serial->setParity(QSerialPort::MarkParity); break;default: break;}//设置停止位switch (ui->Com_Stop->currentIndex()){case 0:serial->setStopBits(QSerialPort::OneStop); break;//1位case 1:serial->setStopBits(QSerialPort::OneAndHalfStop); break;case 2:serial->setStopBits(QSerialPort::TwoStop); break;default: break;}//设置流控制//serial->setFlowControl(QSerialPort::NoFlowControl);//无流控制serial->setFlowControl(QSerialPort::SoftwareControl);//打开串口 同时判断是否成功bool info = serial->open(QIODevice::ReadWrite);if(info == true){qDebug()<<"success";//改变label颜色(指示灯)ui->label_16->setStyleSheet("background-color:rgb(0,255,0);border-radius:10px;");//关闭下拉栏设置使能ui->Com_Serial->setEnabled(false);ui->Com_Baud->setEnabled(false);ui->Com_Size->setEnabled(false);ui->Com_Parity->setEnabled(false);ui->Com_Stop->setEnabled(false);//改变按钮文本为“关闭串口”ui->Butt_Open->setText(tr("关闭串口"));ui->Label_status->setText("");}else{qDebug()<<"fail";QMessageBox::critical (this, "警告", "串口打开失败!", QMessageBox::Ok);}}else{ //关闭串口serial->clear();serial->close();//改变label颜色(指示灯)ui->label_16->setStyleSheet("background-color:rgb(255,0,0);border-radius:10px;");//恢复下拉栏设置使能ui->Com_Serial->setEnabled(true);ui->Com_Baud->setEnabled(true);ui->Com_Size->setEnabled(true);ui->Com_Parity->setEnabled(true);ui->Com_Stop->setEnabled(true);//改变按钮文本为“打开串口”ui->Butt_Open->setText(tr("打开串口"));ui->Label_status->setText("请先选择并打开与发卡器相连的串口!");}
}
六、向串口发送读M1卡扇区数据的指令
void MainWindow::on_Butt_ReadCard_clicked()
{unsigned char authkeybuf[6];QString authkeystr=ui->Text_key->toPlainText().trimmed();int checkbuflen=checkhexdata(authkeystr,authkeybuf);if (checkbuflen!=6){QMessageBox::critical(NULL, "提示", QString::asprintf("十六进制认证密钥输入错误,请输入 %d", 6)+ " 字节的16进制认证密钥!");ui->Text_key->setFocus();return;}if (serial->isOpen()) { //检查串口是否打开unsigned char databuff[16];databuff[0]=0x0e;databuff[1]=0x78;databuff[2]=0x17;databuff[3]=0x00; //表示读取任意卡databuff[4]=0x00;databuff[5]=0x00;databuff[6]=0x00;databuff[7]=ui->Com_Area->currentIndex(); //扇区号databuff[8]=ui->Com_Auth->currentIndex(); //认证方式for(int i=0;i<6;i++){databuff[9+i]=authkeybuf[i];} //认证密钥char crc= 0;for (int i=1;i<=14;i++){crc=crc xor databuff[i];}databuff[15]=crc;sendData.clear();sendData.append(reinterpret_cast<const char*>(databuff), sizeof(databuff));FuncCode=2;getdatapoint=0;serial->write(sendData);QString dispinf=ByteArrayToHexString(sendData);ui->Text_HexData->setPlainText(dispinf);listadditems(getsystime()+" Send:"+dispinf);}else{QMessageBox::critical (this, "警告", "请先打开与发卡器相连的串口!", QMessageBox::Ok);}
}
七、串口接收数据的槽函数返回读卡操作结果
void MainWindow::ReadData()//读取接收到的信息
{ QByteArray buf = serial->readAll();int buflen=buf.length();unsigned char databuff[buflen];std::copy(buf.begin(), buf.end(), databuff);QString str="";int crc=0;if (FuncCode>=0){ //读、写M1、Ntag卡指令,为防止数据分包上传,先将数据存入缓冲for (int i=0;i<buflen;i++){getdatabuff[getdatapoint]=databuff[i];getdatapoint++;}for (int i=0;i<getdatapoint;i++){ //校验缓冲区内数据crc,如果校验成功即认为数据全部接收完,开始分析接收数据str =str+QString::asprintf("%02X ", getdatabuff[i]);if (i>0){crc=crc xor getdatabuff[i];}}}else{ //其他串口调试指令,显示串口接收数据for (int i=0;i<buflen;i++){ //str =str+QString::asprintf("%02X ", databuff[i]);if (i>0){crc=crc xor databuff[i];}}if(crc!=0){listadditems(getsystime()+" Receive:"+str);}}if(crc==0){ //接收数据校验成功,数据有效getdatapoint=0;listadditems(getsystime()+" Receive:"+str);QString cardhao="";QString carddata="";switch(FuncCode){case 1: //只读卡号case 2:case 3:case 4:if(getdatabuff[0]==1){ //只返回一个字节的有效数据switch(getdatabuff[1]){case 1:str="密码认证成功,卡片序列号已知,但读取扇区内容失败!";break;case 2:str="第0块读出,但第1、2块没读出,仅扇区内容前16个字节的数据有效!";break;case 3:str="第0、1块读出,但第2块没读出,仅扇区内容前32个字节的数据有效!";break;case 8:str="未寻到卡!";break;case 9:str="两张以上卡片同时在感应区,发生冲突!";break;case 10:str="无法选择激活卡片!";break;case 11:str="密码装载失败,卡片序列号已知!";break;case 12:str="密码认证失败,卡片序列号已知!";break;}ui->Label_status->setText(str);}else{if(getdatabuff[0]==5){ //返回五个字节的有效数据str="读取卡号成功";cardhao=QString::asprintf("%02X%02X%02X%02X", getdatabuff[2], getdatabuff[3], getdatabuff[4], getdatabuff[5]);switch(getdatabuff[1]){case 0:if(FuncCode==3){str="写扇区数据成功";}else{if(FuncCode==4){str="修改扇区密钥成功";}}break;case 1:str="密码认证成功,卡片序列号已知,但读取扇区内容失败";break;case 2:str="第0块读出,但第1、2块没读出,仅扇区内容前16个字节的数据有效";break;case 3:str="第0、1块读出,但第2块没读出,仅扇区内容前32个字节的数据有效";break;case 8:str="未寻到卡!";break;case 9:str="两张以上卡片同时在感应区,发生冲突";break;case 10:str="无法选择激活卡片";break;case 11:str="密码装载失败,卡片序列号已知";break;case 12:str="密码认证失败,卡片序列号已知";break;}ui->Label_status->setText(str+",16进制卡号:"+cardhao);}else{if(getdatabuff[0]==53){ //返回五十三个字节的有效数据cardhao=QString::asprintf("%02X%02X%02X%02X", getdatabuff[2], getdatabuff[3], getdatabuff[4], getdatabuff[5]);for (int i=6;i<=getdatabuff[0];i++){carddata=carddata+QString::asprintf("%02X", getdatabuff[i])+" ";}ui->Text_Data->setPlainText(carddata);ui->Label_status->setText("读扇区数据成功,16进制卡号:"+cardhao);}}}break;case 5:break;case 6:if (getdatabuff[0]>7){cardhao="16进制卡号:"+QString::asprintf("%02X%02X%02X%02X%02X%02X%02X", getdatabuff[2], getdatabuff[3], getdatabuff[4], getdatabuff[5], getdatabuff[6], getdatabuff[7], getdatabuff[8]);}if(getdatabuff[1]==0){ui->Label_status->setText("写Ntag卡块数据成功,"+cardhao);}else{if(getdatabuff[1]==8){ui->Label_status->setText("未寻到卡!"); }else{if(getdatabuff[1]==9){ui->Label_status->setText("两张以上卡片同时在感应区,发生冲突!"); }else{if(getdatabuff[1]==12){ui->Label_status->setText("密码认证失败!"+cardhao); }else{if(getdatabuff[1]==13){ui->Label_status->setText("读卡失败,可能要带密码操作!"+cardhao); }else{ui->Label_status->setText("写Ntag卡失败,"+QString::asprintf("返回错误代码:%d",getdatabuff[1]));}}}}}break;case 7: //读Ntag卡返回状态if (getdatabuff[0]>7){cardhao="16进制卡号:"+QString::asprintf("%02X%02X%02X%02X%02X%02X%02X", getdatabuff[2], getdatabuff[3], getdatabuff[4], getdatabuff[5], getdatabuff[6], getdatabuff[7], getdatabuff[8]);}if(getdatabuff[1]==0){for (int i=11;i<=getdatabuff[0];i++){carddata=carddata+QString::asprintf("%02X", getdatabuff[i])+" ";}ui->Text_TagData->setPlainText(carddata);ui->Label_status->setText("读Ntag卡块数据成功,"+cardhao);}else{if(getdatabuff[1]==8){ui->Label_status->setText("未寻到卡!"); }else{if(getdatabuff[1]==9){ui->Label_status->setText("两张以上卡片同时在感应区,发生冲突!"); }else{if(getdatabuff[1]==12){ui->Label_status->setText("密码认证失败!"+cardhao); }else{if(getdatabuff[1]==13){ui->Label_status->setText("读卡失败,可能要带密码操作!"+cardhao); }else{ui->Label_status->setText("读Ntag卡失败,"+QString::asprintf("返回错误代码:%d",getdatabuff[1]));}}}}}break;case 8: //读Ntag卡号返回状态if(getdatabuff[0]==8 && getdatabuff[1]==0){cardhao=QString::asprintf("%02X%02X%02X%02X%02X%02X%02X", getdatabuff[2], getdatabuff[3], getdatabuff[4], getdatabuff[5], getdatabuff[6], getdatabuff[7], getdatabuff[8]);ui->Label_status->setText("读Ntag卡号成功,16进制卡号:"+cardhao);}else{if(getdatabuff[0]==1 && getdatabuff[1]==8){ui->Label_status->setText("未寻到卡!");}else{if(getdatabuff[0]==1 && getdatabuff[1]==9){ui->Label_status->setText("读Ntag卡号失败,感应区内的卡不是Ntag卡!");}}}break;case 9: //校验Ntag卡密钥if(getdatabuff[0]==3 && getdatabuff[1]==0){QString packhex=QString::asprintf("%02X%02X", getdatabuff[2], getdatabuff[3]);ui->Label_status->setText("Ntag卡密钥校验成功,PACK密钥确认码:"+packhex);}else{if(getdatabuff[0]==1 && getdatabuff[1]==12){ui->Label_status->setText("Ntag卡密钥校验失败,请先读取卡号、输入8位正确的认证密钥再校验试试!");}}break;case 10:break;}}
}