作者:陈广 日期:2019-1-16
便携式读卡器的使用已经完成,接下来我们讲讲我们系使用的实验箱中的 125KHz 读卡器的使用。之所以还要讲它,主要是它传送数据的模式与之前讲的便携式的并不相同。先上图:
够简陋,所有读卡器都裸露在外。125K 读卡器的位置我已经用红框标了出来,另一红框是在使用此读卡器时串口所要插的位置。
本实验箱的 125K 读卡模块完全支持 EM、 TK 及其兼容卡片的操作。它采用 UART 接口与其他计算机通信, UART 接口一帧的数据格式为 1 个起始位,8 个数据位,无奇偶校验位, 1 个停止位,波特率为 19200bps,其向外输出的数据格式参考下表:
卡号 | 校验 |
---|---|
4 字节 | 1 字节 |
无论是否有卡,读卡模块均以固定间隔发送上表所示格式的数据包。如果没有卡,则以每隔半秒的时间间隔发送0000000000
。如果有卡,则以很快的速度重复发送该卡卡号。
这种模式的缺点在于要时时对串口中的数据进行处理,占用 CPU 资源。优点在于可以在极短时间内感知 ID 卡离开了读卡器。这是上一款便携式读卡器所无法做到的。具体使用哪种模式,需要根据 ID 卡的应用场景来做决定。
以这种通讯模式读卡,当然会麻烦一些。这一次我们使用 Windows 应用程序来读卡。当 ID 卡进入读卡模块的射频区域时,显示卡号;当 ID 卡离开读卡模块的射频区域时显示无卡。
需要注意的是,SerialPort
的DataReceived
事件方法实际是会被安排在线程中执行,要访问 UI,需要做特殊处理。所幸的是这并非难事,处理方法可以参考《C#多线程编程》这一系列文章。
界面非常简单,新建一个 Windows 窗体应用程序,在窗体中添加一个Label
控制,命名为lblCardNo
。如下图所示:
双击窗体生成窗体的 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);
}
}
}
运行程序,刷卡,当有卡进入读卡模块的射频区域时,显示卡号;当卡离开读卡模块的射频区域时显示无卡。效果如下图所示: