设为首页收藏本站
查看: 887|回复: 2

【扩展模块】基于QT5的PN532_NFC读卡

[复制链接]

7

主题

16

回帖

95

积分

注册会员

积分
95
lugl 发表于 2024-5-1 22:27:21 | 显示全部楼层 |阅读模式
【开发环境】

1、ubuntu18.04
2、瑞米派SDK
3、QT5.12
【开发步骤】
一、根据瑞米派的原理图,与PN535_NFC的原理图,它们是兼容树霉派的标准接口。根据瑞米派的原理图,我这里确定ttySC4为与PN535_NFC的UART接口。
二、为了实现串口通信,我这里顺便用QT实现了一个串口助手的工程。基实现如下:
1、打开QT设计,新建一个工程,工程中配置GCC与G++为瑞米派的SDK指定的目录,如下图示:
微信截图_20240501220859.png 微信截图_20240501220833.png

2、配置qmake路径如下:
微信截图_20240501221118.png

3、最后配置kit如下:
微信截图_20240501221219.png

4、同时由于黙认的镜像包没有拷贝libQt5Serialport.so的这个库文件,
复制SDK的目录/opt/poky/3.1.20/sysroots/aarch64-poky-linux/usr/lib64$下面,他是有libQt5SerialPort.so.5这个文件的,所以直接复制到开发板:


5、设计界面如下:
微信截图_20240501221713.png

上面是一个串口助手,可以实现串口的调试。
6、主要代码如下在.pro中添加serial的引用
  1. QT       += core gui serialport
复制代码
7、在mainwindow.h中添加相关头文件的引用,并使用设计器生成槽函数,添加一个接收的函数,其代码如下:
  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H

  3. #include <QMainWindow>
  4. #include <QtSerialPort>
  5. #include <QString>
  6. #include <QSerialPortInfo>
  7. #include <QMessageBox>
  8. #include <QTimer>
  9. #include <QPainter>

  10. QT_BEGIN_NAMESPACE
  11. namespace Ui { class MainWindow; }
  12. QT_END_NAMESPACE

  13. class MainWindow : public QMainWindow
  14. {
  15.     Q_OBJECT

  16. public:
  17.     MainWindow(QWidget *parent = nullptr);
  18.     ~MainWindow();

  19.     QSerialPort *serialPort; //定义串口指针
  20. private slots:
  21.      /*手动连接槽函数*/

  22.     void manual_serialPortReadyRead();
  23.     /*以下为mainwindow.ui文件中点击“转到槽”自动生成的函数*/
  24.     void on_openBt_clicked();

  25.     void on_btnSerialCheck_clicked();

  26.     void on_sendBt_clicked();

  27.     void on_btnClearSend_clicked();

  28.     void on_clearBt_clicked();

  29.     void on_chkTimeSend_stateChanged(int arg1);

  30.     void on_bntReadIC_clicked();

  31. private:
  32.     Ui::MainWindow *ui;

  33.     // 发送、接收字节计数
  34.     long sendNum, recvNum;
  35.     QLabel *lblSendNum;
  36.     QLabel *lblRecvNum;
  37.     QLabel *lblPortState;
  38.     void setNumOnLable(QLabel *lbl, QString strS, long num);

  39.     // 定时发送-定时器
  40.      QTimer *timSend;
  41. };
  42. #endif // MAINWINDOW_H
复制代码
8、在mainwindow.cpp代码如下:
  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include "QSerialPort"
  4. #include "QSerialPortInfo"
  5. #include "QMessageBox"
  6. #include "QDateTime"
  7. #include "QTimer"

  8. MainWindow::MainWindow(QWidget *parent)
  9.     : QMainWindow(parent)
  10.     , ui(new Ui::MainWindow)
  11. {
  12.     ui->setupUi(this);

  13.     QStringList serialNamePort;

  14.     serialPort = new QSerialPort(this);
  15.     connect(serialPort, SIGNAL(readyRead()), this,SLOT(manual_serialPortReadyRead()));/*手动连接槽函数*/
  16.     ui->serailCb->clear();
  17.     foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){
  18.         ui->serailCb->addItem(info.portName());
  19.     }

  20.     sendNum = 0;
  21.     recvNum = 0;

  22.     QStatusBar *sBar = statusBar();

  23.     lblSendNum = new QLabel(this);
  24.     lblRecvNum = new QLabel(this);
  25.     lblPortState = new QLabel(this);
  26.     lblPortState->setText("Conneted");

  27.     lblPortState->setStyleSheet("color:red");

  28.     lblSendNum->setMinimumSize(100,20);
  29.     lblRecvNum->setMinimumSize(100,20);
  30.     lblPortState->setMinimumSize(550,20);
  31.     setNumOnLable(lblSendNum,"S:",sendNum);
  32.     setNumOnLable(lblRecvNum,"R:",recvNum);

  33.     sBar->addPermanentWidget(lblPortState);
  34.     sBar->addPermanentWidget(lblSendNum);
  35.     sBar->addPermanentWidget(lblRecvNum);

  36.     // 定时发送-定时器
  37.     timSend = new QTimer;
  38.     timSend->setInterval(1000);// 设置默认定时时长1000ms
  39.     connect(timSend, &QTimer::timeout, this, [=](){
  40.         on_sendBt_clicked();
  41.     });

  42. }

  43. /*手动实现接收数据函数*/
  44. void MainWindow::manual_serialPortReadyRead()
  45. {
  46.     QByteArray recBuf = serialPort->readAll();;
  47.     QString str_rev;

  48.     // 接收字节计数
  49.     recvNum += recBuf.size();
  50.     // 状态栏显示计数值
  51.     setNumOnLable(lblRecvNum, "R: ", recvNum);
  52.     if(recvNum == 19){
  53.         ui->lblICID->setText(QString(recBuf.toHex().toUpper().mid(10,8)));
  54.     }
  55.     if(ui->chk_rev_hex->checkState() == false){
  56.         if(ui->chk_rev_time->checkState() == Qt::Checked){
  57.             QDateTime nowtime = QDateTime::currentDateTime();
  58.             str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";
  59.             str_rev += QString(recBuf).append("\r\n");
  60.         }
  61.         else{
  62.             // 在当前位置插入文本,不会发生换行。如果没有移动光标到文件结尾,会导致文件超出当前界面显示范围,界面也不会向下滚动。
  63.             //ui->recvEdit->appendPlainText(buf);

  64.             if(ui->chk_rev_line->checkState() == Qt::Checked){
  65.                 str_rev = QString(recBuf).append("\r\n");
  66.             }
  67.             else
  68.             {
  69.                 str_rev = QString(recBuf);
  70.             }
  71.         }
  72.     }else{

  73.         // 16进制显示,并转换为大写
  74.         QString str1 = recBuf.toHex().toUpper();//.data();
  75.         // 添加空格
  76.         QString str2;
  77.         for(int i = 0; i<str1.length (); i+=1)
  78.         {
  79.             str2 += str1.mid (i,1);
  80.             str2 += " ";
  81.         }
  82.         if(ui->chk_rev_time->checkState() == Qt::Checked)
  83.         {
  84.             QDateTime nowtime = QDateTime::currentDateTime();
  85.             str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";
  86.             str_rev += str2.append("\r\n");
  87.         }
  88.         else
  89.         {
  90.             if(ui->chk_rev_line->checkState() == Qt::Checked)
  91.                 str_rev += str2.append("\r\n");
  92.             else
  93.                 str_rev = str2;

  94.         }
  95.     }
  96.     ui->recvEdit->insertPlainText(str_rev);
  97.     ui->recvEdit->moveCursor(QTextCursor::End);

  98. }


  99. void MainWindow::setNumOnLable(QLabel *lbl, QString strS, long num)
  100. {
  101.     QString strN;
  102.     strN.sprintf("%ld", num);
  103.     QString str = strS + strN;
  104.     lbl->setText(str);
  105. }
  106. MainWindow::~MainWindow()
  107. {
  108.     delete ui;
  109. }


  110. void MainWindow::on_openBt_clicked()
  111. {
  112.      /*串口初始化*/
  113.     QSerialPort::BaudRate baudRate;
  114.     QSerialPort::DataBits dataBits;
  115.     QSerialPort::StopBits stopBits;
  116.     QSerialPort::Parity checkBits;

  117.     // 获取串口波特率
  118.     if(ui->baundrateCb->currentText()=="2400")
  119.             baudRate=QSerialPort::Baud2400;
  120.         else if(ui->baundrateCb->currentText()=="4800")
  121.             baudRate=QSerialPort::Baud4800;
  122.         else if(ui->baundrateCb->currentText()=="9600")
  123.             baudRate=QSerialPort::Baud9600;
  124.         else if(ui->baundrateCb->currentText()=="19200")
  125.             baudRate=QSerialPort::Baud19200;
  126.         else if(ui->baundrateCb->currentText()=="38400")
  127.             baudRate=QSerialPort::Baud38400;
  128.         else if(ui->baundrateCb->currentText()=="57600")
  129.             baudRate=QSerialPort::Baud57600;
  130.         else if(ui->baundrateCb->currentText()=="115200")
  131.             baudRate=QSerialPort::Baud115200;
  132.     // 获取串口数据位
  133.         if(ui->databitCb->currentText()=="5")
  134.             dataBits=QSerialPort::Data5;
  135.         else if(ui->databitCb->currentText()=="6")
  136.             dataBits=QSerialPort::Data6;
  137.         else if(ui->databitCb->currentText()=="7")
  138.             dataBits=QSerialPort::Data7;
  139.         else if(ui->databitCb->currentText()=="8")
  140.             dataBits=QSerialPort::Data8;

  141.         // 获取串口停止位
  142.         if(ui->stopbitCb->currentText()=="1")
  143.             stopBits=QSerialPort::OneStop;
  144.         else if(ui->stopbitCb->currentText()=="1.5")
  145.             stopBits=QSerialPort::OneAndHalfStop;
  146.         else if(ui->stopbitCb->currentText()=="2")
  147.             stopBits=QSerialPort::TwoStop;

  148.         // 获取串口奇偶校验位
  149.         if(ui->checkbitCb->currentText() == "none"){
  150.             checkBits = QSerialPort::NoParity;
  151.         }else if(ui->checkbitCb->currentText() == "奇校验"){
  152.             checkBits = QSerialPort::OddParity;
  153.         }else if(ui->checkbitCb->currentText() == "偶校验"){
  154.             checkBits = QSerialPort::EvenParity;
  155.         }else{

  156.         }
  157.         // 初始化串口属性,设置 端口号、波特率、数据位、停止位、奇偶校验位数

  158.         serialPort->setPortName(ui->serailCb->currentText());
  159.         serialPort->setBaudRate(baudRate);
  160.         serialPort->setDataBits(dataBits);
  161.         serialPort->setStopBits(stopBits);
  162.         serialPort->setParity(checkBits);

  163.         // 根据初始化好的串口属性,打开串口
  164.         // 如果打开成功,反转打开按钮显示和功能。打开失败,无变化,并且弹出错误对话框。
  165.         if(ui->openBt->text() == "打开串口"){
  166.             if(serialPort->open(QIODevice::ReadWrite) == true){
  167.                 ui->openBt->setText("关闭串口");
  168.                 // 让端口号下拉框不可选,避免误操作(选择功能不可用,控件背景为灰色)
  169.                 ui->serailCb->setEnabled(false);
  170.             }else{
  171.                 QMessageBox::critical(this,"错误提示", "串口打开失败!!!\r\n该串口可能被占用\r\n请选择正确的串口" );
  172.             }
  173.             //statusBar 状态栏显示端口状态
  174.             QString sm = "%1 OPEND, %2, 8, NONE, 1";
  175.             QString status = sm.arg(serialPort->portName()).arg(serialPort->baudRate());
  176.             lblPortState->setText(status);
  177.             lblPortState->setStyleSheet("color:green");

  178.         }else{
  179.             serialPort->close();
  180.             ui->openBt->setText("打开串口");
  181.             ui->serailCb->setEnabled(true);
  182.             QString sm = "%1 ClOSED";
  183.             QString status = sm.arg(serialPort->portName());
  184.             lblPortState->setText(status);
  185.             lblPortState->setStyleSheet("color:red");
  186.         }
  187. }

  188. void MainWindow::on_btnSerialCheck_clicked()
  189. {
  190.     ui->serailCb->clear();

  191.     foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
  192.         ui->serailCb->addItem(info.portName());
  193.     }

  194. }

  195. void MainWindow::on_sendBt_clicked()
  196. {
  197.     QByteArray array;

  198.     if(ui->chk_send_hex->checkState() == Qt::Checked){
  199.         array = QByteArray::fromHex(ui->sendEdit->toPlainText().toUtf8().data());
  200.     }else{
  201.         array = ui->sendEdit->toPlainText().toLocal8Bit().data();
  202.     }

  203.     if(ui->chk_send_line->checkState() == Qt::Checked){
  204.         array.append("\r\n");
  205.     }

  206.     int a = serialPort->write(array);
  207.     if(a>0){
  208.         sendNum += a;
  209.         setNumOnLable(lblSendNum,"S:", sendNum);
  210.     }
  211. }

  212. void MainWindow::on_btnClearSend_clicked()
  213. {
  214.     ui->sendEdit->clear();

  215.     sendNum = 0;
  216.     setNumOnLable(lblSendNum,"S:",sendNum);
  217. }

  218. void MainWindow::on_clearBt_clicked()
  219. {
  220.     ui->recvEdit->clear();
  221.     sendNum = 0;
  222.     recvNum = 0;

  223.     setNumOnLable(lblSendNum,"S:",sendNum);
  224.     setNumOnLable(lblRecvNum, "R:", recvNum);
  225. }

  226. void MainWindow::on_chkTimeSend_stateChanged(int arg1)
  227. {
  228.     if(arg1 == 0){
  229.         timSend->stop();
  230.         ui->txtSendMs->setEnabled(true);
  231.     }else{
  232.         if(ui->txtSendMs->text().toInt() >= 10){
  233.             timSend->start(ui->txtSendMs->text().toInt());
  234.             ui->txtSendMs->setEnabled(false);
  235.         }else{
  236.             ui->chkTimeSend->setCheckState(Qt::Unchecked);
  237.             QMessageBox::critical(this, "错误提示", "定时发送的最小间隔为 10ms\r\n请确保输入的值 >=10");
  238.         }
  239.     }

  240. }

  241. void MainWindow::on_bntReadIC_clicked()
  242. {
  243.     QByteArray array;
  244.     QString sendstry = "00 00 FF 04 FC D4 4A 01 00 E1 00";
  245.     array = QByteArray::fromHex(sendstry.toUtf8().data());
  246.     serialPort->write(array);
  247. }
复制代码
其中on_bntReadIC_clicked,实现了发送获取发送此帧用于探测Mifare Classic 卡的命令。发送以后把IC卡帖进天线,就可以在卡号那里读出IC的卡号:
下载.png

【总结】
这次米尔提供的PN532——NFC模块为标准树莓派的接口,他可以通过跳钱来使用SPI、I2C、UART来与NFC卡实现通信功能。同时瑞米派也提供了非常好的QT开发SDK。使得驱动非常之容易。接我会偿试使用刷卡实现远程门禁的认证的例程。

回复

使用道具 举报

0

主题

25

回帖

120

积分

注册会员

积分
120
米尔小超人 发表于 2024-5-6 08:32:25 | 显示全部楼层
请问你有什么问题吗?是哪方面的,硬件还是软件?  一般这种读卡模块是IIC或者SPI接口,接线正常的话,可以直接使用linux的接口编程
回复 支持 反对

使用道具 举报

7

主题

16

回帖

95

积分

注册会员

积分
95
 楼主| lugl 发表于 2024-5-6 10:21:22 | 显示全部楼层
米尔小超人 发表于 2024-5-6 08:32
请问你有什么问题吗?是哪方面的,硬件还是软件?  一般这种读卡模块是IIC或者SPI接口,接线正常的话,可以 ...

我发了帖子,怎么只剩一下个空标题,是论坛的问题吗?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录

本版积分规则

Archiver|手机版|小黑屋|米尔科技论坛   

GMT+8, 2024-12-26 21:16 , Processed in 0.095697 second(s), 22 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表