射频识别(RFID)技术

实验箱中的 125KHz 读卡器操作

作者:陈广 日期:2019-1-16


便携式读卡器的使用已经完成,接下来我们讲讲我们系使用的实验箱中的 125KHz 读卡器的使用。之所以还要讲它,主要是它传送数据的模式与之前讲的便携式的并不相同。先上图:

图 1:实验箱

够简陋,所有读卡器都裸露在外。125K 读卡器的位置我已经用红框标了出来,另一红框是在使用此读卡器时串口所要插的位置。

读卡器通讯模式

本实验箱的 125K 读卡模块完全支持 EM、 TK 及其兼容卡片的操作。它采用 UART 接口与其他计算机通信, UART 接口一帧的数据格式为 1 个起始位,8 个数据位,无奇偶校验位, 1 个停止位,波特率为 19200bps,其向外输出的数据格式参考下表:

卡号 校验
4 字节 1 字节

无论是否有卡,读卡模块均以固定间隔发送上表所示格式的数据包。如果没有卡,则以每隔半秒的时间间隔发送0000000000。如果有卡,则以很快的速度重复发送该卡卡号。

这种模式的缺点在于要时时对串口中的数据进行处理,占用 CPU 资源。优点在于可以在极短时间内感知 ID 卡离开了读卡器。这是上一款便携式读卡器所无法做到的。具体使用哪种模式,需要根据 ID 卡的应用场景来做决定。

编程操作 ID 卡

以这种通讯模式读卡,当然会麻烦一些。这一次我们使用 Windows 应用程序来读卡。当 ID 卡进入读卡模块的射频区域时,显示卡号;当 ID 卡离开读卡模块的射频区域时显示无卡。

需要注意的是,SerialPortDataReceived事件方法实际是会被安排在线程中执行,要访问 UI,需要做特殊处理。所幸的是这并非难事,处理方法可以参考《C#多线程编程》这一系列文章。

界面设计

界面非常简单,新建一个 Windows 窗体应用程序,在窗体中添加一个Label控制,命名为lblCardNo。如下图所示:

图 2:界面设计

代码

双击窗体生成窗体的 Load 事件,然后输入代码,全部代码如下:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO.Ports;

namespace ReadIDCard
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        SerialPort sp = new SerialPort();
        byte[] cardNum = new byte[5];
        int index = 0;

        private void MainForm_Load(object sender, EventArgs e)
        {
            sp.PortName = "COM4";
            sp.BaudRate = 19200;
            try
            {
                sp.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            sp.DataReceived += DataReceived;
        }

        private void DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (!sp.IsOpen) return;

            byte[] buff = new byte[sp.BytesToRead];
            sp.Read(buff, 0, buff.Length);
            bool same = true;
            foreach (var num in buff)
            {
                if (num != cardNum[index])
                {
                    cardNum[index] = num;
                    same = false;
                }
                index++;
                if (index == 5) 
                {
                    index = 0;
                    if (!same)
                    {
                        PrintCardNo();
                        same = true;
                    }
                }
            }
        }

        private void PrintCardNo()
        {
            int sum = 0; //用于判断是否接收到的5个字节全为0
            foreach (var b in cardNum)
            {
                sum += b;
            }
            if (sum == 0) //全为0表示无卡
            {
                this.Invoke(new Action(() =>
                {
                    lblCardNo.BackColor = Color.Silver;
                    lblCardNo.Text = "无卡";
                }));
            }
            else
            {
                //提取10位卡号码
                UInt32 cardNo = BitConverter.ToUInt32(cardNum, 0);
                string str1 = ReverseBytes(cardNo).ToString().PadLeft(10, '0') + "  ";
                //提取8位卡号码的第一部分
                string str2 = cardNum[1].ToString().PadLeft(3, '0') + ",";
                //提取8位卡号码的第二部分
                UInt16 part2 = BitConverter.ToUInt16(cardNum, 2);
                string str3 = ReverseBytes(part2).ToString().PadLeft(5, '0');
                this.Invoke(new Action(() =>
                {
                    lblCardNo.BackColor = Color.DarkOrange;
                    lblCardNo.Text = str1 + str2 + str3; //打印卡号
                }));
            }
        }
        //翻转32位整数字节序
        private UInt32 ReverseBytes(UInt32 value)
        {
            return (value & 0x000000FFu) << 24 | (value & 0x0000FF00u) << 8 |
                (value & 0x00FF0000u) >> 8 | (value & 0xFF000000u) >> 24;
        }
        //翻转16位整数字节序
        private UInt16 ReverseBytes(UInt16 value)
        {
            return (UInt16)((value & 0x00FFu) << 8 | (value & 0xFF00u) >> 8);
        }
    }
}

运行程序,刷卡,当有卡进入读卡模块的射频区域时,显示卡号;当卡离开读卡模块的射频区域时显示无卡。效果如下图所示:

图 3:程序运行效果
;

© 2018 - IOT小分队文章发布系统 v0.3