PN532 NFC 读卡器的串口通讯实现

1、了解PN532串口通讯数据帧的含义

0000 FFLENLCSTFIPD0…PDnDCS00
  • 00:数据帧头部;
  • 00 FF:数据起始号;
  • LEN :数据包长度,包含TFI;
  • LCS :数据长度校验和,LEN+LCS = 0,等同于(LEN ^ 0xFF) + 1;(容易出错的地方)
  • TFI :命令; D4表示主机到PN532,D5表示PN532到主机;
  • PD0…PDn:数据;
  • DCS :数据校验和,sum=TFI+PD0+…+PDn = 0,等同于(sum & 0xFF ^ 0xFF) + 1;(容易出错的地方)
  • 00 :序列结尾;

2、数据通讯的使用

以下位操作IC卡为例
  • 常用指令参数
	// 初始化指令
    public static byte[] initCommand = new byte[] { 0x55,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,0xfd,0xd4,0x14,0x01,0x17,0x00 };
    // 读取的数据块 0扇区 第三块(IC卡有16个扇区,每个扇区4块数据,此数据块从0开始到63结束)
    public static byte dataPath = 0x02;
    // 寻卡指令
    public static byte[] searchCardCmd = new byte[] { 0x4A,0x02,0x00 };
    // 读卡指令
    public static byte[] readData1 = new byte[] { 0x40,0x01,0x30,dataPath };
    // 校验指令前缀(其中0xFF,0xFF,0xFF,0xFF,0xFF,0xFF 为密码)
    public static byte[] localCheckCmd = new byte[] { 0x40,0x01,0x60,dataPath,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
  • 开始串口通讯
	// 设置串口参数(选定设备连接的COM序号,)
	SerialPort serialPort = new SerialPort("COM4", 115200);
	serialPort.Open();
    // 发送 PN532 初始化指令(该指令为默认指令)
    sendToPN532("唤醒", serialPort, initCommand, false);
  • 循环监听串口读到的数据信息
while (true) {
	try {
		List<byte> checkResp = sendToPN532("寻卡", serialPort, searchCardCmd, true);
        int checkCmdLength = 10 + checkResp.Count;
        byte[] checkCmd = new byte[checkCmdLength];
        for (int i=0;i<localCheckCmd.Length;i++) {
        	checkCmd[i] = localCheckCmd[i];
        }
        for (int i=0;i<checkResp.Count;i++) {
			checkCmd[10+i] = checkResp[i];
        }
        // 验卡指令拼接完成后 发送
        sendToPN532("验证", serialPort, checkCmd, false);
        Console.Write("获取到卡号:");
        byte[] cardNo = new byte[checkResp.Count];
        for (int i=0;i<checkResp.Count;i++) {
        	cardNo[i] = checkResp[i];
        }
        Console.WriteLine(BitConverter.ToUInt32(cardNo, 0) );
        List<byte> readResp = sendToPN532("读", serialPort, readData1, true);
        Console.Write("读到的数据:");
        byte[] data = new byte[readResp.Count];
        for (int i=0;i<readResp.Count;i++) {
             data[i] = readResp[i];
             Console.Write($"{readResp[i]:X2} ");
        }
	} catch (Exception e) {
    	Console.WriteLine(e.Message);
        Thread.Sleep(1000);
    }
}
  • 代码的响应
寻卡 Write
00 00 FF 04 FC D4 4A 02 00 E0 00 
寻卡 Response: (93 9C C5 19 卡号16进制)
00 00 FF 00 FF 00 00 00 FF 0C F4 D5 4B 01 01 00 04 08 04 93 9C C5 19 C1 00 
验证 Write
00 00 FF 0F F1 D4 40 01 60 02 FF FF FF FF FF FF 93 9C C5 19 82 00
验证 Response: (41 00:代表成功)
00 00 FF 00 FF 00 00 00 FF 03 FD D5 41 00 EA 00 
卡号:432381075
读 Write:(40 01 30 02 中02 代表0扇区的第3块数据)
00 00 FF 05 FB D4 40 01 30 02 B9 00
读 Response: (41 00:代表成功,后面的32 30 32 33 30 38 33 30 00 00 00 00 D0 00 00 00 为数据块的数据)
00 00 FF 00 FF 00 00 00 FF 13 ED D5 41 00 32 30 32 33 30 38 33 30 00 00 00 00 D0 00 00 00 88 00 
读到的数据:32 30 32 33 30 38 33 30 00 00 00 00 D0 00 00 00 -> 20230830
// 指令发送与数据获取
public static List<byte> sendToPN532(String name, SerialPort serialPort, byte[] checkCmd, Boolean result){
    List<byte> commendReturn = new List<byte>();
    byte[] initCommand = RequestPackage(checkCmd);
    serialPort.Write(initCommand, 0, initCommand.Length);
    Console.WriteLine($"{name} Write");
    for (int i = 0; i < initCommand.Length; i++)
    {
        Console.Write($"{initCommand[i]:X2} ");
    }
    Console.WriteLine();
    Thread.Sleep(500);
    byte[] responseBuffer = new byte[512];
    int bytesRead3 = serialPort.Read(responseBuffer, 0, responseBuffer.Length);
    Console.WriteLine($"{name} Response: ");
    for (int i = 0; i < bytesRead3; i++) {
        if (result && responseBuffer[i]==0xD5) {
            // 指令响应成功
            if (responseBuffer[i+1]==0x41 && responseBuffer[i+2]==0x00) {
                // 获取数据
                for (int n=i;n<bytesRead3;n++) {
                    if (3+n < bytesRead3-2) {
                        commendReturn.Add(responseBuffer[3+n]);
                    }
                }
            } else if (responseBuffer[i+1]==0x4B) {
                // 获取卡号
                for (int n=i;n<bytesRead3;n++) {
                    if (8+n < bytesRead3-2) {
                        commendReturn.Add(responseBuffer[8+n]);
                    }
                }
            }
        }
        if (responseBuffer[i]==0xD5) {
            if (responseBuffer[i+1]==0x41 && responseBuffer[i+2]!=0x00) {
                throw new Exception("数据暂无法获取!");
            }
        }
        Console.Write($"{responseBuffer[i]:X2} ");
    }
    Console.WriteLine();
    return commendReturn;
}
// 拼接请求参数
public static byte[] RequestPackage(byte[] data){
        byte[] requestPackageData = new byte[8+data.Length];
        requestPackageData[0] = 0x00;
        requestPackageData[1] = 0x00;
        requestPackageData[2] = 0xFF;
        requestPackageData[3] = (byte)(data.Length+1);
        requestPackageData[4] = (byte)((requestPackageData[3] ^ 0xFF) + 1);
        requestPackageData[5] = 0xD4;
        // this.Tfi = 0xD4; 的10进制数
        int sum = 0xD4;
        for (int i=0;i<data.Length;i++) {
            requestPackageData[6+i] = data[i];
            sum += data[i];
        }
        requestPackageData[6+data.Length] = (byte)((sum & 0xFF ^ 0xFF) + 1);
        requestPackageData[7+data.Length] = 0x00;
        return requestPackageData;
}

完整代码如下

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Text;
using System.Threading;

namespace WebSocket {
    class Program
{
    // 初始化指令
    public static byte[] initCommand = new byte[] { 0x55,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,0xfd,0xd4,0x14,0x01,0x17,0x00 };
    // 读取的数据块 0扇区 第三块
    public static byte dataPath = 0x02;
    // 寻卡指令
    public static byte[] searchCardCmd = new byte[] { 0x4A,0x02,0x00 };
    // 读卡指令
    public static byte[] readData1 = new byte[] { 0x40,0x01,0x30,dataPath };
    // 校验指令前缀
    public static byte[] localCheckCmd = new byte[] { 0x40,0x01,0x60,dataPath,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};

    static void Main(string[] args)
    {
        // 设置串口参数
        SerialPort serialPort = new SerialPort("COM4", 115200);
        serialPort.Open();
        // 发送 PN532 初始化指令
        sendToPN532("唤醒", serialPort, initCommand, false);
        while (true) {
            try {
                List<byte> checkResp = sendToPN532("寻卡", serialPort, searchCardCmd, true);
                int checkCmdLength = 10 + checkResp.Count;
                byte[] checkCmd = new byte[checkCmdLength];
                for (int i=0;i<localCheckCmd.Length;i++) {
                    checkCmd[i] = localCheckCmd[i];
                }
                for (int i=0;i<checkResp.Count;i++) {
                    checkCmd[10+i] = checkResp[i];
                }
                sendToPN532("验证", serialPort, checkCmd, false);
                Console.Write("卡号:");
                byte[] cardNo = new byte[checkResp.Count];
                for (int i=0;i<checkResp.Count;i++) {
                    cardNo[i] = checkResp[i];
                }
                Console.WriteLine(BitConverter.ToUInt32(cardNo, 0) );
                List<byte> readResp = sendToPN532("读", serialPort, readData1, true);
                Console.Write("读到的数据:");
                byte[] data = new byte[readResp.Count];
                for (int i=0;i<readResp.Count;i++) {
                    data[i] = readResp[i];
                    Console.Write($"{readResp[i]:X2} ");
                }
                Console.WriteLine(System.Text.Encoding.UTF8.GetString(data) );
            } catch (Exception e) {
                Console.WriteLine(e.Message);
                Thread.Sleep(1000);
            }
        }
    }

    public static List<byte> sendToPN532(String name, SerialPort serialPort, byte[] checkCmd, Boolean result){
        List<byte> commendReturn = new List<byte>();
        byte[] initCommand = RequestPackage(checkCmd);
        serialPort.Write(initCommand, 0, initCommand.Length);
        Console.WriteLine($"{name} Write");
        for (int i = 0; i < initCommand.Length; i++)
        {
            Console.Write($"{initCommand[i]:X2} ");
        }
        Console.WriteLine();
        Thread.Sleep(500);
        byte[] responseBuffer = new byte[512];
        int bytesRead3 = serialPort.Read(responseBuffer, 0, responseBuffer.Length);
        Console.WriteLine($"{name} Response: ");
        for (int i = 0; i < bytesRead3; i++) {
            if (result && responseBuffer[i]==0xD5) {
                // 指令响应成功
                if (responseBuffer[i+1]==0x41 && responseBuffer[i+2]==0x00) {
                    // 获取数据
                    for (int n=i;n<bytesRead3;n++) {
                        if (3+n < bytesRead3-2) {
                            commendReturn.Add(responseBuffer[3+n]);
                        }
                    }
                } else if (responseBuffer[i+1]==0x4B) {
                    // 获取卡号
                    for (int n=i;n<bytesRead3;n++) {
                        if (8+n < bytesRead3-2) {
                            commendReturn.Add(responseBuffer[8+n]);
                        }
                    }
                }
            }
            if (responseBuffer[i]==0xD5) {
                if (responseBuffer[i+1]==0x41 && responseBuffer[i+2]!=0x00) {
                    throw new Exception("数据暂无法获取!");
                }
            }
            Console.Write($"{responseBuffer[i]:X2} ");
        }
        Console.WriteLine();
        return commendReturn;
    }

    public static byte[] RequestPackage(byte[] data){
            byte[] requestPackageData = new byte[8+data.Length];
            requestPackageData[0] = 0x00;
            requestPackageData[1] = 0x00;
            requestPackageData[2] = 0xFF;
            requestPackageData[3] = (byte)(data.Length+1);
            requestPackageData[4] = (byte)((requestPackageData[3] ^ 0xFF) + 1);
            requestPackageData[5] = 0xD4;
            // this.Tfi = 0xD4; 的10进制数
            int sum = 0xD4;
            for (int i=0;i<data.Length;i++) {
                requestPackageData[6+i] = data[i];
                sum += data[i];
            }
            requestPackageData[6+data.Length] = (byte)((sum & 0xFF ^ 0xFF) + 1);
            requestPackageData[7+data.Length] = 0x00;
            return requestPackageData;
    }
}
}
参考资料

指令描述
PN532 百度文库资料
PN532指令描述
PN532读写操作

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值