1、了解PN532串口通讯数据帧的含义
00 | 00 FF | LEN | LCS | TFI | PD0…PDn | DCS | 00 |
---|
- 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 };
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};
SerialPort serialPort = new SerialPort("COM4", 115200);
serialPort.Open();
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;
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 };
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();
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;
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读写操作