EM4305/EM4205卡是采用瑞士EM微电子公司工作频率为125kHz,具有读、写功能的非接触式RFID射频芯片,它具有功耗低、可提供多种数据传输速率和数据编码方法等特点,适合射频芯片ISO 11784/11785规范,该芯片被广泛应用于动物识别和跟踪、智能门锁、通道门禁等一卡通管理系统。
EM4305/EM4205卡的EEPROM储存空间为512位,分为16个块,每个块32位,块1为UID块,块2为密码块,块4为配置块,块14、15为锁定标志块。可在-40℃~85℃在温度下工作。
EM4305/EM4205卡可以修改其配置信息将其模拟成兼容EM4100的ID卡,可用于ID卡的复制。
本示例使用的发卡器:EM4305 EM4469 ISO11784/85协议125K低频FXD-B动物标签读写发卡器-淘宝网 (taobao.com)
函数声明
//------------------------------------------------------------------------------------------------------------------------------------------------------
//外部函数声明:让设备发出声响
[DllImport("OUR_IDR.dll", EntryPoint = "idr_beep", CallingConvention = CallingConvention.StdCall)]
static extern byte idr_beep(UInt32 xms);//xms单位为毫秒
//------------------------------------------------------------------------------------------------------------------------------------------------------
//读取设备编号,可做为软件加密狗用,也可以根据此编号在公司网站上查询保修期限
[DllImport("OUR_IDR.dll", EntryPoint = "pcdgetdevicenumber", CallingConvention = CallingConvention.StdCall)]
static extern byte pcdgetdevicenumber(byte[] devicenumber);//devicenumber用于返回编号
//------------------------------------------------------------------------------------------------------------------------------------------------------
//只读EM4100卡号
[DllImport("OUR_IDR.dll", EntryPoint = "idr_read", CallingConvention = CallingConvention.StdCall)]
public static extern byte idr_read(byte[] serial);//serial返回卡号
//------------------------------------------------------------------------------------------------------------------------------------------------------
//只读卡号,只读一次EM4100卡,必须拿开卡才能再读到
[DllImport("OUR_IDR.dll", EntryPoint = "idr_read_once", CallingConvention = CallingConvention.StdCall)]
public static extern byte idr_read_once(byte[] serial);//serial返回卡号
//------------------------------------------------------------------------------------------------------------------------------------------------------
//EM4205/4305卡写配置块
[DllImport("OUR_IDR.dll", EntryPoint = "em4305_init", CallingConvention = CallingConvention.StdCall)]
public static extern byte em4305_init(byte ctrlword, byte[] seria, byte[] key, byte[] configdata);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//EM4469/4569卡写配置块
[DllImport("OUR_IDR.dll", EntryPoint = "em4469_init", CallingConvention = CallingConvention.StdCall)]
public static extern byte em4469_init(byte ctrlword, byte[] seria, byte[] key, byte[] configdata);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//EM4205/4305/4469/4569卡读卡
[DllImport("OUR_IDR.dll", EntryPoint = "em4305_read", CallingConvention = CallingConvention.StdCall)]
public static extern byte em4305_read(byte ctrlword, byte[] seria, byte[] key, byte[] blockflag, byte[] readdata);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//EM4205/4305/4469/4569卡写卡
[DllImport("OUR_IDR.dll", EntryPoint = "em4305_write", CallingConvention = CallingConvention.StdCall)]
public static extern byte em4305_write(byte ctrlword, byte[] seria, byte[] key, byte[] blockflag, byte[] writedata);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//EM4205/4305/4469/4569卡锁定块
[DllImport("OUR_IDR.dll", EntryPoint = "em4305_lock", CallingConvention = CallingConvention.StdCall)]
public static extern byte em4305_lock(byte ctrlword, byte[] seria, byte[] key, byte[] blockflag);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//EM4205/4305/4469/4569卡改密码
[DllImport("OUR_IDR.dll", EntryPoint = "em4305_changekey", CallingConvention = CallingConvention.StdCall)]
public static extern byte em4305_changekey(byte ctrlword, byte[] seria, byte[] oldkey, byte[] newkey);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//用EM4205/4305卡制作ID卡(也就是EM4100及兼容卡)
[DllImport("OUR_IDR.dll", EntryPoint = "em4305_to4100", CallingConvention = CallingConvention.StdCall)]
public static extern byte em4305_to4100(byte ctrlword, byte[] seria, byte[] oldkey, byte[] newkey, byte[] myuidbuf);
//-----------------------------------------------------------------------------------------------------------------------------------------------------
//只读HID卡号
[DllImport("OUR_IDR.dll", EntryPoint = "hid_read", CallingConvention = CallingConvention.StdCall)]
public static extern byte hid_read(byte[] serial);//serial返回卡号
读取EM4205/4305/4469/4569芯片内数据
byte status; //存放返回值
int j;
byte myctrlword = 0x00; //控制字
byte[] oldpicckey = new byte[4]; //密码
byte[] mypiccserial = new byte[4]; //卡序列号
byte[] mypiccdata = new byte[70]; //读卡数据缓冲:卡无线转输分频比、卡内容长度(字节数),及最多返回12个块的数据
byte[] mypiccblockflag = new byte[2]; //指定读哪一块
string PasswStr = textBox14.Text.Trim();
string seriaStr = textBox15.Text.Trim();
if (checkBox1.Checked) //本次操作需要密码验证
{
myctrlword = (byte)(myctrlword + NEEDKEY);
try
{
oldpicckey[0] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(0, 2), 16));
oldpicckey[1] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(2, 2), 16));
oldpicckey[2] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(4, 2), 16));
oldpicckey[3] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(6, 2), 16));
}
catch
{
MessageBox.Show("认证密码输入错误!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox14.Select();
return;
}
}
if (checkBox2.Checked) //仅操作指定卡号的卡
{
try
{
myctrlword = (byte)(myctrlword + NEEDSERIAL);
mypiccserial[0] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(0, 2), 16));
mypiccserial[1] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(2, 2), 16));
mypiccserial[2] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(4, 2), 16));
mypiccserial[3] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(6, 2), 16));
}
catch
{
MessageBox.Show("卡号输入错误!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox15.Select();
return;
}
}
j = 0;
mypiccblockflag[0] = 0x00;
for (int i = 0; i < 8; i++)
{
if (myCheckBox[i].Checked)
{
mypiccblockflag[0] = (byte)(mypiccblockflag[0] + Math.Pow(2, i));
j++;
}
}
mypiccblockflag[1] = 0x00;
for (int i = 0; i < 8; i++)
{
if (myCheckBox[8 + i].Checked)
{
mypiccblockflag[1] = (byte)(mypiccblockflag[1] + Math.Pow(2, i));
j++;
}
}
status = em4305_read(myctrlword, mypiccserial, oldpicckey, mypiccblockflag, mypiccdata);
if (status == 0)
{
string blockdata = "";
for (int i = 0; i < mypiccdata[1]; i++)
{
blockdata = blockdata + mypiccdata[2 + i].ToString("X2");
}
textBox4.Text = blockdata;
j = 0;
for (int i = 0; i < 16; i++)
{
if (myCheckBox[i].Checked)
{
myTextBox[i].Text = blockdata.Substring(j, 8);
j = j + 8;
}
}
seriaStr = "";
for (int i = 0; i < 4; i++)
{
seriaStr = seriaStr + mypiccserial[i].ToString("X2");
}
idr_beep(30);
MessageBox.Show("读卡成功,卡无线转输分频比:" + mypiccdata[0].ToString("D") + ",卡号:" + seriaStr, "提示:", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else { MessageErrInf(status); }
写数据到EM4205/4305/4469/4569芯片内
byte status; //存放返回值
int j;
byte myctrlword = 0x00; //控制字
byte[] oldpicckey = new byte[4]; //密码
byte[] mypiccserial = new byte[4]; //卡序列号
byte[] mypiccdata = new byte[50]; //写卡数据缓冲
byte[] mypiccblockflag = new byte[2]; //指定读哪一块
string PasswStr = textBox14.Text.Trim();
string seriaStr = textBox15.Text.Trim();
int chtxt = Form1_chedkdata();
if (chtxt != 100)
{
MessageBox.Show("写卡数据输入错误,请重新输入!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
myTextBox[chtxt].Select();
return;
}
if (checkBox1.Checked) //本次操作需要密码验证
{
myctrlword = (byte)(myctrlword + NEEDKEY);
try
{
oldpicckey[0] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(0, 2), 16));
oldpicckey[1] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(2, 2), 16));
oldpicckey[2] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(4, 2), 16));
oldpicckey[3] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(6, 2), 16));
}
catch
{
MessageBox.Show("认证密码输入错误!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox14.Select();
return;
}
}
if (checkBox2.Checked) //仅操作指定卡号的卡
{
try
{
myctrlword = (byte)(myctrlword + NEEDSERIAL);
mypiccserial[0] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(0, 2), 16));
mypiccserial[1] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(2, 2), 16));
mypiccserial[2] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(4, 2), 16));
mypiccserial[3] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(6, 2), 16));
}
catch
{
MessageBox.Show("卡号输入错误!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox15.Select();
return;
}
}
if (myCheckBox[1].Checked)
{
MessageBox.Show("块1为uid块,只能读取不可写入!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (myCheckBox[4].Checked)
{
MessageBox.Show("块4为配置区,只能在初始化函数中操作!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (myCheckBox[14].Checked)
{
MessageBox.Show("块14为锁定标志块,只能在初始化函数中操作!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (myCheckBox[15].Checked)
{
MessageBox.Show("块15为配置区,只能在初始化函数中操作!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
j = 0;
string WriteStr = "";
mypiccblockflag[0] = 0x00;
for (int i = 0; i < 8; i++)
{
if (myCheckBox[i].Checked)
{
mypiccblockflag[0] = (byte)(mypiccblockflag[0] + Math.Pow(2, i));
WriteStr = WriteStr + myTextBox[i].Text;
j++;
}
}
mypiccblockflag[1] = 0x00;
for (int i = 0; i < 8; i++)
{
if (myCheckBox[8 + i].Checked)
{
mypiccblockflag[1] = (byte)(mypiccblockflag[1] + Math.Pow(2, i));
WriteStr = WriteStr + myTextBox[8 + i].Text;
j++;
}
}
for (int i = 0; i < WriteStr.Length / 2; i++)
{
mypiccdata[i] = Convert.ToByte(Convert.ToInt32(WriteStr.Substring(i * 2, 2), 16));
}
status = em4305_write(myctrlword, mypiccserial, oldpicckey, mypiccblockflag, mypiccdata);
if (status == 0)
{
seriaStr = "";
for (int i = 0; i < 4; i++)
{
seriaStr = seriaStr + mypiccserial[i].ToString("X2");
}
idr_beep(30);
MessageBox.Show("卡号:" + seriaStr + "写卡成功!", "提示:", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else { MessageErrInf(status); }
将EM4205/4305卡制做成ID门禁卡
byte status; //存放返回值
byte myctrlword = 0x00; //控制字
byte[] oldpicckey = new byte[4]; //密码
byte[] newpicckey = new byte[4]; //新密码
byte[] mypiccserial = new byte[6]; //卡序列号
byte[] mypiccblockflag = new byte[2]; //指定读哪一块
string PasswStr = textBox14.Text.Trim();
string seriaStr = textBox15.Text.Trim();
string newpassw = textBox20.Text.Trim();
string newuidstr = textBox3.Text.Trim();
if (checkBox1.Checked) //本次操作需要密码验证
{
myctrlword = (byte)(myctrlword + NEEDKEY);
try
{
oldpicckey[0] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(0, 2), 16));
oldpicckey[1] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(2, 2), 16));
oldpicckey[2] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(4, 2), 16));
oldpicckey[3] = Convert.ToByte(Convert.ToInt32(PasswStr.Substring(6, 2), 16));
}
catch
{
MessageBox.Show("认证密码输入错误!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox14.Select();
return;
}
}
if (checkBox2.Checked) //仅操作指定卡号的卡
{
try
{
myctrlword = (byte)(myctrlword + NEEDSERIAL);
mypiccserial[0] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(0, 2), 16));
mypiccserial[1] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(2, 2), 16));
mypiccserial[2] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(4, 2), 16));
mypiccserial[3] = Convert.ToByte(Convert.ToInt32(seriaStr.Substring(6, 2), 16));
}
catch
{
MessageBox.Show("卡号输入错误!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox15.Select();
return;
}
}
if (checkBox11.Checked) //修改卡片密码
{
try
{
myctrlword = (byte)(myctrlword + KEYENABLE);
newpicckey[0] = Convert.ToByte(Convert.ToInt32(newpassw.Substring(0, 2), 16));
newpicckey[1] = Convert.ToByte(Convert.ToInt32(newpassw.Substring(2, 2), 16));
newpicckey[2] = Convert.ToByte(Convert.ToInt32(newpassw.Substring(4, 2), 16));
newpicckey[3] = Convert.ToByte(Convert.ToInt32(newpassw.Substring(6, 2), 16));
}
catch
{
MessageBox.Show("密码输入错误!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox20.Select();
return;
}
}
if (radioButton3.Checked)
{
if (newuidstr.Length != 10 || !checkhex(newuidstr)) { MessageBox.Show("要写入的10位16进制卡号错误,请输入正确的10位16进制卡号!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error); return; }
byte[] myuid = new byte[5]; //写入的新卡号
try
{
myuid[0] = Convert.ToByte(Convert.ToInt32(newuidstr.Substring(0, 2), 16));
myuid[1] = Convert.ToByte(Convert.ToInt32(newuidstr.Substring(2, 2), 16));
myuid[2] = Convert.ToByte(Convert.ToInt32(newuidstr.Substring(4, 2), 16));
myuid[3] = Convert.ToByte(Convert.ToInt32(newuidstr.Substring(6, 2), 16));
myuid[4] = Convert.ToByte(Convert.ToInt32(newuidstr.Substring(8, 2), 16));
}
catch
{
MessageBox.Show("新卡号输入错误,请输入8位16进制数的卡号!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBoxuid1.Select();
return;
}
status = em4305_to4100(myctrlword, mypiccserial, oldpicckey, newpicckey, myuid);
if (status == 0)
{
idr_beep(50);
MessageBox.Show("卡号:" + System.Convert.ToString(myuid[1] * 256 * 256 * 256 + myuid[2] * 256 * 256 + myuid[3] * 256 + myuid[4]) + " 写卡成功变成ID卡!", "提示:", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else { MessageErrInf(status); }
}
else
{
MessageBox.Show("此功能暂未开发!", "提示:", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
将EM4205/4305/4469/4569卡写入FDX-B动物标签
Int16 conutryid = Convert.ToInt16(textBox1.Text.Trim());
Int64 SerialnumberId = Convert.ToInt64(textBox2.Text.Trim());
Int16 Reserved = Convert.ToInt16(textBox3.Text.Trim());
Int32 Extension = Convert.ToInt32(textBox4.Text.Trim());
if (conutryid > 1023){
MessageBox.Show("Country国家代码的取值范围是:0~1023!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox1.SelectionStart = 0;
textBox1.SelectionLength = textBox1.Text.Trim().Length;
textBox1.Select();
return;
}
if (SerialnumberId > 274877906943)
{
MessageBox.Show("SerialNumber序列号的取值范围是:0~274877906943!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox2.SelectionStart = 0;
textBox2.SelectionLength = textBox2.Text.Trim().Length;
textBox2.Select();
return;
}
if (Reserved > 16383)
{
MessageBox.Show("Reserved保留信息的取值范围是:0~16383!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox3.SelectionStart = 0;
textBox3.SelectionLength = textBox3.Text.Trim().Length;
textBox3.Select();
return;
}
if (Extension > 16777215)
{
MessageBox.Show("Extension扩展信息的取值范围是:0~16777215!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox4.SelectionStart = 0;
textBox4.SelectionLength = textBox4.Text.Trim().Length;
textBox4.Select();
return;
}
string Extensionbit = "000000000000000000000000"+Convert.ToString(Extension, 2);
Extensionbit = Extensionbit.Substring(Extensionbit.Length - 24);
string Reservedbit = "00000000000000" + Convert.ToString(Reserved, 2);
Reservedbit = Reservedbit.Substring(Reservedbit.Length - 14);
if (checkBox4.Checked) { Reservedbit = "1" + Reservedbit; } else { Reservedbit = "0" + Reservedbit; }
if (checkBox3.Checked) { Reservedbit = Reservedbit+"1"; } else { Reservedbit = Reservedbit+"0"; }
string conutrybit = "0000000000" + Convert.ToString(conutryid, 2);
conutrybit = conutrybit.Substring(conutrybit.Length - 10);
string Serialnumberbit = "00000000000000000000000000000000000000" + Convert.ToString(SerialnumberId, 2);
Serialnumberbit = Serialnumberbit.Substring(Serialnumberbit.Length - 38);
string fdxbitstr = "10000000000"; //前导码
fdxbitstr = "1" + Serialnumberbit.Substring(30, 8) + fdxbitstr;
fdxbitstr = "1" + Serialnumberbit.Substring(22, 8) + fdxbitstr;
fdxbitstr = "1" + Serialnumberbit.Substring(14, 8) + fdxbitstr;
fdxbitstr = "1" + Serialnumberbit.Substring(6, 8) + fdxbitstr;
fdxbitstr = "1" + conutrybit.Substring(8, 2) + Serialnumberbit.Substring(0, 6) + fdxbitstr;
fdxbitstr = "1" + conutrybit.Substring(0, 8) + fdxbitstr;
fdxbitstr = "1" + Reservedbit.Substring(8, 8) + fdxbitstr;
fdxbitstr = "1" + Reservedbit.Substring(0, 8) + fdxbitstr;
byte[] info = new byte[8];
for (int i = 0; i < 8; i++)
{
string thisbit = fdxbitstr.Substring((i + 1) * 9 - 9, 9);
if (thisbit.Substring(0, 1) == "1")
{
info[7 - i] = (byte)Convert.ToInt16(thisbit.Substring(1, 8), 2);
}
}
ushort crc16ccitt = GetCrc16Ccitt(info);
string crcbit = "0000000000000000" + Convert.ToString(crc16ccitt, 2);
crcbit = crcbit.Substring(crcbit.Length - 16);
fdxbitstr = "1" + crcbit.Substring(8, 8) + fdxbitstr;
fdxbitstr = "1" + crcbit.Substring(0, 8) + fdxbitstr;
fdxbitstr = "1" + Extensionbit.Substring(16, 8) + fdxbitstr;
fdxbitstr = "1" + Extensionbit.Substring(8, 8) + fdxbitstr;
fdxbitstr = "1" + Extensionbit.Substring(0, 8) + fdxbitstr;
byte[] writebuf = new byte[16];
for (int i = 0; i < 16; i++)
{
string thisbit = fdxbitstr.Substring((i + 1) * 8 - 8, 8);
writebuf[15 - i] = (byte)Convert.ToInt16(thisbit, 2);
}
byte status; //存放返回值
byte myctrlword = 0x00; //控制字
byte[] oldpicckey = new byte[4]; //密码
byte[] mypiccserial = new byte[4]; //卡序列号
byte[] mypiccblockflag = new byte[2]; //指定读哪一块
mypiccblockflag[0] = 224;
mypiccblockflag[1] = 1;
status = em4305_write(myctrlword, mypiccserial, oldpicckey, mypiccblockflag, writebuf);
if (status == 0)
{
if (checkBox2.Checked){
em4305_lock(myctrlword, mypiccserial, oldpicckey, mypiccblockflag);
}
string seriaStr = "";
for (int i = 0; i < 4; i++)
{
seriaStr = seriaStr + mypiccserial[i].ToString("X2");
}
idr_beep(30);
MessageBox.Show("卡号:" + seriaStr + "写FDX-B协议标签成功!", "提示:", MessageBoxButtons.OK , MessageBoxIcon.Information );
if (checkBox1.Checked)
{
SerialnumberId = SerialnumberId + 1;
textBox2.Text = SerialnumberId.ToString("D12");
}
}
else { MessageErrInf(status); }