用C# 根据 JSC100 V5.0读写器通讯协议 编写读卡器API
JSC100 V5.0读写器通讯协议
一、通讯格式
波特率:9600 8 1 N
数据包长度L(1byte) 命令字C(1byte) 数据包D(L-1bytes)
通讯方向:
-> 下位机(读卡器)送给上位机(PC)
<- 上位机(PC)送给下位机(读卡器)
二、单步读卡命令说明(以下数据全为16进制数据)
1) 启动(风鸣器响)
<- 02 0B 0F (02为长度,0B为命令字,测试凤鸣器,0F凤鸣器响的时间)
-> 01 00 (01为长度,00为测试成功)
2) 寻卡(返回卡的类型)
<- 02 02 26 (02为命令字,26为RegMfOutSelect)
-> 03 00 04 00 (00为命令成功代码,04表示Mifare One卡)
3) 防冲突(返回卡的系列号)
<- 01 03 (03为命令字)
-> 05 00 52 00 75 7A (52 00 75 7A为卡号CardSerialNo)
4) 选择
<- 01 04 (04为命令字)
-> 03 00 80 00
5) 终止(使卡休眠)
<- 01 01 (01为命令字)
-> 01 00
6) 密码下载(新卡的密码为6个字节的FF)
<- 09 06 60 01 FF FF FF FF FF FF (06为命令字,60为密码A(61为密码B),01为扇区号,12个F为密码)
-> 01 00
7) 数据读
<- 02 02 52 (02为命令字,52为PICC_REQALL)
-> 03 00 04 00 (04为RegFIFOLength)
<- 01 03 (03为命令字)
-> 05 00 52 00 75 7A (52 00 75 7A为卡号)
<- 01 04 (04为命令字)
-> 03 00 08 00
<- 04 05 60 01 04 (校验密码,05为命令字,60为密码A(61为密码B),01为扇区1,04为RegFIFOLength)
-> 01 00
<- 02 08 04 (08为命令字读命令,04为块号)
-> 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (16个00为数据)
<- 02 08 05 (08为命令字,05为块号)
-> 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (16个00为数据)
<- 02 08 06 (08为命令字,06为块号)
-> 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (16个00为数据)
<- 02 08 07 (08为命令字,07为块号)
-> 11 00 00 00 00 00 00 00 ff 07 80 69 ff ff ff ff ff ff (第一个00为返回代码,后面6个00为密码A[不可现],ff 07 80 69为控制位,后面6个ff为密码B[可现])
8) 数据写
<- 12 09 04 12 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (09为命令字写命令,04为块号,12开始的16个字节为要写的数据)
-> 01 00
<- 12 09 05 45 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (09为命令字,05为块号,45开始的16个字节为要写的数据)
-> 01 00
<- 12 09 06 78 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (09为命令字,06为块号,78开始的16个字节为要写的数据)
-> 01 00
<- 12 09 07 11 11 11 11 11 11 ff 07 80 69 11 11 11 11 11 11 (09为命令字,07为块号,把密码A和密码B都修改成1)[可看成改密码的命令]
-> 01 00
9) 块值操作(初始化)
<- 12 09 04 11 11 11 11 EE EE EE EE 11 11 11 11 04 FB 04 FB (09为命令字块值初始化,04为块号,11开始的16个字节为要写的数据)
-> 01 00
10) 块值操作(读出)
<- 02 08 04 (08为命令字块值读出,04为块号)
-> 11 00 11 11 11 11 EE EE EE EE 11 11 11 11 04 FB 04 FB (11后面的16个自己是读出来的数据)
11) 块值操作(加值)
<- 08 0A C1 04 22 22 22 22 04 (0A为命令字块值加值,C1为PICC_INCREMENT,04为块号,4个字节的22是要加值的数据,04为块号)
-> 01 00
12) 块值操作(减值)
<- 08 0A C0 04 11 11 11 11 04 (0A为命令字,C1为PICC_DECREMENT为块号,4个字节的11是要减值的数据,04为块号)
-> 01 00
13) 修改密码
<- 02 02 26 (02为命令字,26为RegMfOutSelect)
-> 03 00 04 00
<- 01 03 (03为命令字)
-> 05 00 52 00 75 7A (52 00 75 7A为卡号)
<- 01 04 (04为命令字)
-> 03 00 80 00
<- 04 05 60 01 04 (05为命令字,60为密码A(61为密码B),01为扇区号,04为RegFIFOLength)
-> 01 00
<- 12 09 07 33 33 33 33 33 33 ff 07 80 69 33 33 33 33 33 33 (09为命令字,07为块号,33后面的12个字节为新密码)
-> 01 00
14) 自动寻卡
<- 02 0E 26 (0E为命令字,26为RegMfOutSelect)
-> 03 00 04 00 (00为命令成功代码,04表示Mifare One卡)
15) 进入软件掉电模式(内部电流消耗模块包括晶振在内关闭)
<- 01 10 (0C为命令字)
-> 01 00
16) 退出软件掉电模式
<- 01 11 (0C为命令字)
-> 01 00
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
namespace Card.Utility
{
/// <summary>
/// 摘要:读写卡操作基础类
/// </summary>
public class CardManagerUtil
{
private static SerialPort _serialPort;
#region CardManagerUtil 成员
/// <summary>
/// 初始化
/// </summary>
/// <param name="port">端口号</param>
/// <param name="baud">波特率</param>
/// <returns></returns>
public static int rf_init(int port, long baud)
{
string portName = GetPortName(port);
if (_serialPort != null)
{
if (_serialPort.IsOpen)
_serialPort.Close();
}
_serialPort = new SerialPort(portName, Convert.ToInt32(baud));
_serialPort.Parity = Parity.None;
_serialPort.StopBits = StopBits.One;
_serialPort.DataBits = 8;
_serialPort.WriteTimeout = 1;
try
{
if (_serialPort.IsOpen)
_serialPort.Close();
_serialPort.Open();
return 100;
}
catch
{
return -1;
}
}
/// <summary>
/// 重置读卡器
/// </summary>
/// <param name="icdev">设备号</param>
/// <param name="_Msec"></param>
/// <returns></returns>
public static int rf_reset(int icdev, int _Msec)
{
try
{
if (icdev != 100)
return 1;
if (_serialPort.IsOpen)
_serialPort.Close();
_serialPort.Open();
return 0;
}
catch
{
return -1;
}
}
/// <summary>
/// 成功返回0
/// </summary>
/// <param name="icdev"></param>
/// <param name="_Msec"></param>
/// <returns>成功返回0否则返回1</returns>
public static int rf_beep(int icdev, int _Msec)
{
if (icdev != 100)
return 1;
byte[] beepData = { 0x02, 0x0B, 0x0F };
byte[] buffer;
int bufLength = WriteAndRead(beepData, 0, beepData.Length, 2, out buffer);
if (bufLength == 0)
return 1;
if (buffer.Length == 2)
{
if (buffer[1] == 0x00)
return 0;
}
return 1;
}
/// <summary>
/// 返回卡号
/// </summary>
/// <param name="icdev"></param>
/// <param name="_Mode"></param>
/// <param name="_Snr"></param>
/// <returns></returns>
public static int rf_card(int icdev, byte _Mode, out uint _Snr)
{
int cardType = 0;
_Snr = 0;
if (rf_request(icdev, 0, out cardType) != 0)
return 1;
rf_anticoll(icdev, 0, out _Snr);
return 1;
}
/// <summary>
/// 终止卡(卡休眠)
/// </summary>
/// <param name="icdev"></param>
/// <returns></returns>
public static int rf_halt(int icdev)
{
if (icdev != 100)
return 1;
byte[] haltData = { 0x01, 0x01 };
byte[] buffer;
int bufLength = WriteAndRead(haltData, 0, haltData.Length, 2, out buffer);
if (bufLength == 0)
return 1;
if (buffer.Length == 2)
{
if (buffer[1] == 0x00)
return 0;
}
return 1;
}
/// <summary>
/// 寻卡
/// </summary>
/// <param name="icdev"></param>
/// <param name="_Mode"></param>
/// <param name="TagType"></param>
/// <returns></returns>
public static int rf_request(int icdev, byte _Mode, out int TagType)
{
TagType = 0;
if (icdev != 100)
return 1;
byte[] requestData = { 0x02, 0x02, 0x52 };
byte[] buffer = new byte[4];
int bufLength = WriteAndRead(requestData, 0, requestData.Length, 4, out buffer);
if (bufLength == 0)
return 1;
if (buffer.Length == 4)
{
TagType = (int)buffer[2];
if (buffer[1] == 0x00)
return 0;
}
return 1;
}
/// <summary>
/// 卡防冲突
/// </summary>
/// <param name="icdev"></param>
/// <param name="_Bcnt">0</param>
/// <param name="_Snr"></param>
/// <returns></returns>
public static int rf_anticoll(int icdev, byte _Bcnt, out uint _Snr)
{
_Snr = 0;
if (icdev != 100)
return 1;
byte[] anticollData = { 0x01, 0x03 };
byte[] buffer;
int bufLength = WriteAndRead(anticollData, 0, anticollData.Length, 6, out buffer);
if (bufLength == 0)
return 1;
if (buffer.Length == 6)
{
if (buffer[1] == 0x00)
{
List<byte> tempSnr = new List<byte>();
for (int i = 5; i > 1; i--)//低位在前高位在后
{
tempSnr.Add(buffer[i]);
}
if (tempSnr.Count == 4)
_Snr = BitConverter.ToUInt32(tempSnr.ToArray(), 0);
return 0;
}
}
return 1;
}
/// <summary>
/// 选择卡
/// </summary>
/// <param name="icdev"></param>
/// <param name="_Snr"></param>
/// <param name="_Size"></param>
/// <returns></returns>
public static int rf_select(int icdev, uint _Snr, out byte _Size)
{
_Size = 8;
if (icdev != 100)
return 1;
byte[] selectData = { 0x01, 0x04 };
byte[] buffer;
int bufLength = WriteAndRead(selectData, 0, selectData.Length, 4, out buffer);
if (bufLength == 0)
return 1;
if (buffer.Length == 4)
{
if (buffer[1] == 0x00)
return 0;
}
return 1;
}
/// <summary>
/// 加载密码
/// </summary>
/// <param name="icdev">设备号</param>
/// <param name="_Mode">60为密码A(61为密码B)</param>
/// <param name="_SecNr">扇区号</param>
/// <param name="key">密码</param>
/// <returns></returns>
public static int rf_load_key(int icdev, byte _Mode, byte _SecNr, byte[] key)
{
if (icdev != 100)
return 1;
byte[] keycmd = { 0x09, 0x06, _Mode, _SecNr };
List<byte> keyData = new List<byte>();
if (key.Length != 6)//6字节的密码
{
return 1;
}
else
{
keyData.AddRange(keycmd);
keyData.AddRange(key);
}
byte[] buffer;
int bufLength = WriteAndRead(keyData.ToArray(), 0, keyData.Count, 2, out buffer);
if (bufLength == 0)
return 1;
if (buffer.Length == 2)
{
if (buffer[1] == 0x00)
return 0;
}
return 1;
}
/// <summary>
/// 验证密码
/// </summary>
/// <param name="icdev"></param>
/// <param name="_Mode">密码组60为A组,61为B组</param>
/// <param name="_SecNr"></param>
/// <returns></returns>
public static int rf_authentication(int icdev, byte _Mode, byte _SecNr)
{
byte RegFIFOLength = (byte)(((int)_SecNr) * 4);
if (icdev != 100)
return 1;
byte[] authData = { 0x04, 0x05, _Mode, _SecNr, RegFIFOLength };
byte[] buffer;
int bufLength = WriteAndRead(authData, 0, authData.Length, 2, out buffer);
if (bufLength == 0)
return 1;
if (buffer.Length == 2)
{
if (buffer[1] == 0x00)
return 0;
}
return 1;
}
/// <summary>
/// 从某个扇区读取数据
/// </summary>
/// <param name="icdev">设备号</param>
/// <param name="_Adr">块号</param>
/// <param name="_Data">读取到的数据</param>
/// <returns></returns>
public static int rf_read(int icdev, byte _Adr, ref byte[] _Data)
{
if (icdev != 100)
return 1;
byte[] readCmdData = { 0x02, 0x08, _Adr };
byte[] buffer;
int bufLength = WriteAndRead(readCmdData, 0, readCmdData.Length, 18, out buffer);
if (bufLength == 0)
return 1;
if (buffer.Length == 18)
{
if (buffer[1] == 0x00)
{
List<byte> tempData = new List<byte>();
for (int i = 2; i < 18; i++)
{
tempData.Add(buffer[i]);
}
_Data = tempData.ToArray();
return 0;
}
}
return 1;
}
/// <summary>
/// 向某个扇区写入数据
/// </summary>
/// <param name="icdev"></param>
/// <param name="_Adr">块号</param>
/// <param name="_Data">要写的数据</param>
/// <returns></returns>
public static int rf_write(int icdev, byte _Adr, byte[] _Data)
{
if (icdev != 100)
return 1;
byte[] writeCmdData = { 0x12, 0x09, _Adr };
List<byte> cmdData = new List<byte>();
if (_Data.Length != 16)//16字节的数据
{
return 1;
}
else
{
cmdData.AddRange(writeCmdData);
cmdData.AddRange(_Data);
}
byte[] buffer;
int bufLength = WriteAndRead(cmdData.ToArray(), 0, cmdData.Count, 2, out buffer);
if (bufLength == 0)
return 1;
if (buffer.Length == 2)
{
if (buffer[1] == 0x00)
{
return 0;
}
}
return 1;
}
/// <summary>
/// 关闭串口
/// </summary>
/// <param name="icdev"></param>
/// <returns></returns>
public static int rf_exit(int icdev)
{
if (_serialPort != null)
{
if (_serialPort.IsOpen)
_serialPort.Close();
return 0;
}
return 1;
}
#endregion
#region 私有方法
/// <summary>
/// 向串口写数据并阻塞等待数据返回
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="count"></param>
/// <param name="ReadRoom"></param>
/// <param name="readBuf"></param>
/// <param name="ByteTime"></param>
/// <returns></returns>
static int WriteAndRead(byte[] buffer, int offset, int count, int ReadRoom, out byte[] readBuf)
{
_serialPort.Write(buffer, offset, count);
int val = ReadBlock(ReadRoom, out readBuf, 500);
return val;
}
static string GetPortName(int port)
{
string portName = string.Empty;
switch (port)
{
case 0:
portName = "COM1";
break;
case 1:
portName = "COM2";
break;
case 2:
portName = "COM3";
break;
case 3:
portName = "COM4";
break;
case 4:
portName = "COM5";
break;
default:
portName = "COM1";
break;
}
return portName;
}
/// <summary>
/// 串口同步读(阻塞方式读串口,直到串口缓冲区中没有数据,靠字符间间隔超时确定没有数据)
/// </summary>
/// <param name="ReadRoom">串口数据缓冲空间大小</param>
/// <param name="ByteTime">字节间隔最大时间</param>
/// <returns>从串口实际读入的字节</returns>
private static int ReadBlock(int ReadRoom, out byte[] readBuf, int ByteTime)
{
readBuf = new Byte[ReadRoom];
Array.Clear(readBuf, 0, readBuf.Length);
sbyte nBytelen;
if (_serialPort.IsOpen == false)
return 0;
nBytelen = 0;
_serialPort.ReadTimeout = ByteTime;
while (nBytelen < ReadRoom)
{
try
{
readBuf[nBytelen] = (byte)_serialPort.ReadByte();
nBytelen++;
}
catch
{
return 0;
}
}
return nBytelen;
}
#endregion
}
}