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
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: PN532是一款广泛应用于NFC(Near Field Communication,近场通讯)技术的芯片。它具有高性能和可靠性,并且提供了丰富的功能和接口,方便开发人员进行硬件和软件设计。 PN532的中文手册详细介绍了芯片的功能和特性,包括工作原理,通信协议以及命令和应答的格式。手册还提供了详细的电气特性和引脚定义,以及射频接口和传输速率等参数。这些信息对于开发NFC应用非常重要,因为它们帮助开发人员了解如何使用PN532进行数据交换和通信。 手册还介绍了PN532的应用场景和使用方法。以支付系统为例,手册详细描述了如何使用PN532进行安全的NFC支付,包括读取卡片信息、验证支付数据和进行加密通信等过程。同时,手册还提供了相关的示例代码和开发工具,帮助开发人员快速上手并实现自己的应用。 此外,手册还包含了关于PN532的测试和调试信息,帮助开发人员排查故障和进行硬件调试。它提供了一些常见问题的解决方案,并介绍了常见错误的原因和解决方法。 总之,PN532的中文手册是一个非常有用的工具,它提供了全面而详细的文档和指导,帮助开发人员了解和使用这款芯片。无论是专业开发人员还是初学者,通过研究手册,他们可以更好地应用PN532,开发出高效和安全的NFC应用。 ### 回答2: PN532是一款NFC(Near Field Communication,近场通信)芯片,它是一种用于无线通信和数据传输的技术。PN532芯片采用先进的射频通信技术,能够实现智能手机与其他设备、标签或卡之间的近距离交互。它是一种非触摸式的通信技术,能够实现高速、低功耗、安全可靠的数据传输。 PN532 NFC中文手册是一本提供PN532芯片相关技术和应用的指南。这个手册包含了PN532芯片的介绍、功能、电气特性、通信协议、软件驱动、命令集以及应用示例等内容。它提供了详细的技术规格和操作指南,方便开发人员进行PN532芯片的集成和应用开发。 PN532 NFC中文手册的主要内容包括: 1. PN532芯片的概述:介绍了芯片的主要特征和应用场景,如门禁系统、支付系统、智能标签等。 2. 电气特性:包括工作电压、功耗、射频特性等详细参数,以及相关的注意事项和建议。 3. 通信协议和接口:介绍了PN532与外部设备或主机之间的通信接口和协议,如I2C、SPI和UART等。 4. 软件驱动和命令集:提供了针对PN532芯片的软件API和命令集,开发人员可以通过这些接口进行开发和控制。 5. 应用示例:包含了一些常见的应用案例,如读写NFC标签、NFC支付、NFC门禁等,方便开发人员进行参考和学习。 PN532 NFC中文手册对于开发人员来说是一本重要的参考资料,它能够帮助他们了解PN532芯片的功能和特性,掌握相关的开发技术和应用方法,从而更好地开发和应用NFC技术。读者可以通过阅读手册,了解PN532芯片的原理和工作方式,学习如何使用PN532芯片开发各种NFC应用。 ### 回答3: PN532是一种高性能近场通信NFC)芯片,广泛应用于各种NFC设备中。PN532 NFC中文手册是对PN532芯片的详细说明和操作指南的文档。 该手册包括PN532芯片的基本信息、功能特性、接口定义、通信协议等内容。它提供了广泛的指令集和命令格式,涵盖了PN532的各种功能,如读写NFC标签、进行P2P通信、支持各种NFC模式等等。 手册中会介绍PN532的各种寄存器和寄存器配置方法,以及相应的读写操作。此外,手册还会介绍PN532的工作模式和通信协议,包括ISO/IEC 14443 Type A和B、FeliCa、ISO/IEC 15693等等。 PN532 NFC中文手册还会介绍PN532与各种主控芯片(如Arduino、Raspberry Pi等)的连接方法和操作示例。它会详细说明PN532的软件开发接口(API)和相关的驱动程序,方便开发者快速集成PN532芯片到自己的应用中。 通过PN532 NFC中文手册,用户可以深入了解PN532芯片的原理和功能,掌握PN532的使用方法和开发技巧。用户可以根据手册提供的资料,结合自己的需求,开发出各种功能丰富的NFC应用。 总之,PN532 NFC中文手册是PN532芯片的一份详尽说明文档,它提供了全面的信息,帮助用户快速上手并开发出高性能的NFC应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值