zoukankan      html  css  js  c++  java
  • 用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

    代码
    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

    }
    }
  • 相关阅读:
    4:4 自定义拦截器
    DDD学习笔记一
    Winform/WPF国际化处理
    NPOI 操作Excel
    将输入的字符串进行大写格式化
    将输入的字符串分2个字符添加空格并大写格式化
    VS使用技巧
    NotifyIcon用法
    C#Winfrom系统打印机调用/设置默认打印机
    TextBox(只允许输入字母或者数字)——重写控件
  • 原文地址:https://www.cnblogs.com/shineqiujuan/p/1639684.html
Copyright © 2011-2022 走看看