用C# 根据 JSC100 V5.0读写器通讯协议 编写读卡器API
2010-01-05 15:30
357 查看
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
代码
一、通讯格式
波特率: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
代码
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 } }
相关文章推荐
- 算法:编写程序,根据输入的学生成绩,给出相应的等级,90~100为A
- C#借助API实现黑盒自动化测试工具的编写
- c# P2P穿透 已知NAT类型,NAT类型可根据Stun协议获取
- VS2005中使用C#编写MDI窗口根据子窗口个数控制菜单项的enabled属性
- C#编写QQ接口软件--QQ协议篇
- 编写一个存储过程,根据学号、课程号 按成绩的20%进行加分,如果增加后的分数大于100,则取消加分。同时在存储过程中返回增加后的成绩。
- C# 读写XML文档 通讯 算法 自定义协议
- C# 实现Socket5代理协议通讯
- 根据C#类的注释,生成API文档
- 在C++ Builder中用socket api来写网络通讯程序(同时支持TCP和UDP协议)(二)
- Java and C#基于AES对xml通讯协议加密
- 用c#编写socks代理服务器,大白话细述协议的最重要部分。
- 如何用Java来编写可根据SMTP和POP3协议来收发E-mail的Java Applet。
- 一个C#编写QQ接口软件--QQ协议(转)
- 一个C#编写QQ接口软件--QQ协议
- 用c#编写socks代理服务器,大白话细述协议的最重要部分。
- 我学xingo golang服务器之-Unity3d c# 协议api分解
- 用c#编写socks代理服务器,大白话细述协议的最重要部分。
- 在C#中利用API编写关机程序
- 如何用Java来编写可根据SMTP和POP3协议来收发E-mail的Java Applet。