目录
一、引言
在信息化和智能化的浪潮中,RFID技术作为物联网的关键技术之一,以其非接触、自动识别、高效传输等特性,在门禁系统中发挥着重要作用。RFID门禁系统主要由RFID标签、读写器、门禁控制器、门锁、电源等设备组成。系统通过RFID读写器读取人员携带的RFID标签信息,与数据库中的授权信息进行比对,实现人员的身份识别和进出控制。同时,系统还支持远程管理和监控,方便管理员进行实时管理和数据统计。
二、RFID简介
1.介绍
RFID(Radio Frequency Identification)技术是一种通过无线射频信号自动识别目标对象并获取相关数据的无线通信技术。它由RFID标签、读写器和天线组成,通过电磁场或电磁波进行非接触式的数据传输。RFID技术通过无线电波不接触快速信息交换和存储技术,实现电子标签的读写与通信。在识别系统中,读写器发射一定频率的无线电波能量形成电磁场,电子标签进入该区域后被触发,发送存储在其中的数据,或根据读写器的指令修改存储在其中的数据,并能通过接口与计算机网络进行通信。
2.技术特点
RFID技术具有以下特点:
- 非接触式识别:无需人工干预,可工作于各种恶劣环境。
- 快速识别:可同时识别多个标签,操作快捷方便。
- 信息量大:可以细分单品,实现信息的精细化管理。
- 芯片存储:可多次读取,数据安全性高。
- 可与其他传感器共同使用:实现更丰富的应用场景。
3. 应用领域
RFID技术广泛应用于以下领域:
- 物流:货物追踪、信息自动采集、仓储应用等。
- 零售:商品销售数据实时统计、补货、防盗等。
- 制造业:生产数据实时监控、质量追踪、自动化生产等。
- 医疗:医疗器械管理、病人身份识别、婴儿防盗等。
- 身份识别:电子护照、身份证、学生证等各种电子证件。
- 防伪:贵重物品的防伪、票证的防伪等。
- 资产管理:各类资产的管理,如贵重的或数量大相似性高的或危险品等。
三、 RFID射频识别门禁系统工作原理
1. RFID标签与阅读器的通信
- 标签(Tag):每个RFID标签包含一个唯一的电子编码,这个编码用来标识目标对象(如门禁卡)。标签通常由射频芯片和天线组成,射频芯片存储用户的身份信息。
- 阅读器(Reader):负责读取标签信息的设备。当标签进入阅读器的射频场内时,阅读器通过发送射频信号与标签进行通信。
2. 信息读取与传输
- 信息读取:标签接收阅读器发出的射频信号后,如果是无源标签(Passive Tag),则通过感应电流获得的能量发送出存储在芯片中的产品信息;如果是有源标签(Active Tag),则主动发送某一频率的信号。
- 信息传输:阅读器读取标签发送的信息并进行解码,然后将这些信息传输至门禁系统的中央控制器。
3. 身份验证与权限判断
- 身份验证:门禁系统的中央控制器将读取到的用户身份信息与数据库中存储的用户信息进行比对,验证用户身份的真实性。
- 权限判断:根据用户的身份信息,门禁系统判断用户是否具有进入特定区域的权限。
4. 门禁控制
- 门禁控制:如果用户的身份信息有效且具备相应权限,门禁系统的中央控制器将发送指令给门禁设备(如电磁锁),允许用户进入。
5.数据库管理
- 用户信息管理:门禁系统通常会配备一个数据库,用于存储用户信息和权限设置。数据库中的信息包括每个用户的身份信息与对应的RFID标签编号。
- 权限设置:管理员可以在数据库中设置用户的权限,如允许进入的区域、时间段等。
四、系统设计
1.硬件设计
- RFID标签:存储着人员的身份信息和授权信息,是门禁系统的关键识别元素。
- RFID读写器:负责读取RFID标签信息,并将其传输给门禁控制器。读写器应具有较远的读取距离和稳定的读取性能。
- 门禁控制器:接收RFID读写器传输的信息,并与数据库中的授权信息进行比对,控制门锁的开关。门禁控制器应具有高性能的处理器和足够的存储容量,以确保快速准确地处理大量数据。
2.软件设计
- 数据库设计:建立人员信息表、授权信息表等数据库表,用于存储人员的身份信息和授权信息。数据库应具有高效的数据查询和存储能力,以支持快速的人员身份识别和进出控制。
- 读写器控制:编写相应的驱动程序,控制RFID读写器的读取和传输功能。读写器控制模块应具有稳定的数据传输和错误处理能力,以确保数据的准确性和可靠性。
- 门禁控制:根据读写器读取的信息,与数据库中的授权信息进行比对,控制门锁的开关。门禁控制模块应具有高效的数据处理和判断能力,以确保门禁系统的安全性和稳定性。
3.环境搭建
3.1安装Visual Studio 2022
在官网按照自己的需求下载并安装需要的版本,链接如下: https://visualstudio.microsoft.com/zh-hans/vs/
在这里就不详细说明下载及安装步骤
3.2界面设计
RFID门禁系统的界面设计是一个综合性的任务,需要考虑多个方面的因素。通过合理的设计原则、功能设计、布局设计、设计要素和安全设计等方面的考虑,可以设计出符合用户需求、易于使用、安全可靠的RFID门禁系统界面。根据系统设计需求,设计适合的界面。
3.3代码编写
整体示例代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.IO;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace 门禁系统
{
public partial class Form1 : Form
{
public delegate void showReceiveDelegate(string text);
SerialPort com = new SerialPort("COM2", 115200, Parity.None, 8, StopBits.One);
int com_num = 0;
int timer_num, flag = 0;
string Da,Pa = "E:\\Information.txt";
Timer Timer1 = new Timer();
DateTime star = DateTime.Now;
DateTime stop = DateTime.Now;
/*-------------------------------------------------------------
* status_num状态字
* 1,表示读取信息
--------------------------------------------------------------*/
int status_num=0;//状态字
public Form1()
{
InitializeComponent();
}
//窗体加载
private void Form1_Load(object sender, EventArgs e)
{
//串口初始化
cmbPort.SelectedIndex = 2;
cmbBaudRate.SelectedIndex = 4;
cmbDataBits.SelectedIndex = 0;
cmbStopBits.SelectedIndex = 0;
cmbParity.SelectedIndex = 0;
//定时器初始化
System.Timers.Timer t = new System.Timers.Timer(50);
t.Elapsed += new System.Timers.ElapsedEventHandler(theout);
t.AutoReset = true; //设置是执行一次(false)还是一直执行(true);
t.Enabled = true; //是否执行System.Timers.Timer.Elapsed事件;
timer_num = 0;
}
//串口打开与关闭
private void btnOpen_Click(object sender, EventArgs e)
{
if (btnOpen.Text == "打开串口")
{
try
{
if (!com.IsOpen)
{
com.PortName = cmbPort.Text;
com.BaudRate = int.Parse(cmbBaudRate.Text);
com.DataBits = int.Parse(cmbDataBits.Text);
switch (cmbStopBits.SelectedIndex)
{
case 0:
com.StopBits = StopBits.One; break;
case 1:
com.StopBits = StopBits.Two; break;
case 2:
com.StopBits = StopBits.OnePointFive; break;
case 3:
com.StopBits = StopBits.None; break;
}
switch (cmbParity.SelectedIndex)
{
case 0: com.Parity = Parity.None; break;
case 1: com.Parity = Parity.Odd; break;
case 2: com.Parity = Parity.Even; break;
}
com.Open();//打开串口
}
btnOpen.Text = "关闭串口";
txtStatus.Text = "串口已打开!";
btnInformation.Enabled = true;
btnClear.Enabled = true;
button1.Enabled = true;
//button2.Enabled = true;
// 数据接收模式变化时,设置串口的数据接收侦听事件
try
{
com.DataReceived += new
SerialDataReceivedEventHandler(com_DataReceived); //加载接收事件
}
catch (Exception err)
{
txtStatus.Text = err.ToString();
}
}
catch
{ txtStatus.Text = "串口打开错误或串口不存在!"; }
}
else //关闭串口
try
{
if (com.IsOpen)
com.Close(); //关闭串口
btnOpen.Text = "打开串口";
txtStatus.Text = "串口已关闭!";
btnInformation.Enabled = false;
btnClear.Enabled = false;
button1.Enabled = false;
//button2.Enabled = false;
}
catch
{
txtStatus.Text = "串口关闭错误或串口不存在!";
}
}
//--------------------------------------------------------------------------------
//定时器相关设置
public void theout(object source, System.Timers.ElapsedEventArgs e)
{
timer_num++;
this.BeginInvoke(new TextOption(function1));//invok 委托实现跨线程的调用
}
delegate void TextOption();//定义一个委托
void function1()
{
if ((timer_num > 10)&&(com_num>5))
{
com_num = 0;
try
{
int count = com.BytesToRead;
byte[] readBuffer = new byte[count];
com.Read(readBuffer, 0, count);
String strReceive = getStringFromBytes(readBuffer); //转十六进制
this.Invoke(new showReceiveDelegate(doShowReceive), strReceive);
}
catch (Exception err)
{
txtStatus.Text = err.ToString();
}
}
}
//--------------------------------------------------------------------------------
// 响应模式时,串口接收数据事件
private void com_DataReceived(object sender,
System.IO.Ports.SerialDataReceivedEventArgs e)
{
com_num = com.BytesToRead;
timer_num = 0;
}
//异步线程处理接受的字符,显示在接收的文本框中
public void doShowReceive(string str)
{
txtReceive.Text = str;
if (status_num == 1)
{
status_num = 0;
int i;
for (i = 0; i < 10; i++)
{
if (str.Substring(i, 20).Equals("FF 55 00 00 81 01 08"))
{
txtInformation.Text = str.Substring(i + 21, 11);//截取4个字节
txtStatus.Text = "低频卡信息读取成功!";
break;
}
}
if (i >= 10)
{
txtStatus.Text = "无法获取低频卡信息!";
}
}
//-----------------------------------------------------
//----------1---------2---------3---------4---------
//01234567890123456789012345678901234567890123456789
//FF 55 00 00 82 04 01 00 EE 33
//低频卡初始化操作
if (status_num == 2)
{
status_num = 0;
int i;
for (i = 0; i < 5; i++)
{
if (str.Substring(i, 23).Equals("FF 55 00 00 82 04 01 00"))
{
txtStatus.Text = "低频卡初始化操作成功!";
break;
}
}
if (i >= 5)
{
txtStatus.Text = "无法进行低频卡初始化操作!";
}
}
//------------------------------------------------------------
//发送命令,常规写块操作
//----0---------1---------2---------3---------4---------5---------
//----012345678901234567890123456789012345678901234567890123456789
//发送FF 55 00 00 03 04 05 01 00 00 00 00 47 48
//接收FF 55 00 00 83 04 01 00 23 8E
if (status_num == 3)
{
status_num = 0;
int i;
for (i = 0; i < 5; i++)
{
if (str.Substring(i, 20).Equals("FF 55 00 00 83 03 04"))
{
txtStatus.Text = "常规读块操作操作成功!";
textBox2.Text = str.Substring(i + 21, 11);//截取4个字节
break;
}
}
if (i >= 5)
{
txtStatus.Text = "常规读块操作未成功!";
textBox1.Text = "";
}
}
if (status_num == 4)
{
status_num = 0;
int i;
for (i = 0; i < 5; i++)
{
if (str.Substring(i, 23).Equals("FF 55 00 00 83 04 01 00"))
{
txtStatus.Text = "常规写块操作成功!";
break;
}
}
if (i >= 5)
{
txtStatus.Text = "常规写块操作未成功!";
textBox1.Text = "";
}
}
if (status_num == 5)
{
status_num = 0;
FileStream F = new FileStream(Pa, FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None);
StreamWriter W = new StreamWriter(F);
W.WriteLine(textBox3.Text);
W.WriteLine(textBox4.Text);
W.Close();
}
if (status_num == 6)
{
status_num = 0;
string[] a = new string[2];
StreamReader sa = new StreamReader(Pa);
for (int k = 0; k < 2; k++)
{
a[k] = sa.ReadLine();
}
int i;
for (i = 0; i < 5; i++)
{
if (str.Substring(i, 20).Equals("FF 55 00 00 83 03 04"))
{
txtStatus.Text = "常规读块操作操作成功!";
Da = str.Substring(i + 21, 11);//截取4个字节
break;
}
}
if (i >= 5)
{
txtStatus.Text = "常规读块操作操作未成功!";
}
for (int j = 1; j >= 0; j--)
{
if (Da == a[j])
{
flag = 1;
break;
}
else
{
flag = 0;
}
}
if (flag == 1)
{
flag = 0;
textBox8.Text = a[0];
textBox7.Text = a[1];
textBox9.Text = "比对成功,正在开门";
}
else
{
textBox8.Text = "";
textBox7.Text = "";
textBox9.Text = "该卡无效!";
}
}
if (status_num == 7)
{
status_num = 0;
}
if (status_num == 8)
{
status_num = 0;
textBox11.Text = (stop-star).ToString();
}
}
// 把字节数组转换为十六进制格式的字符串。
// <param name="pByte">要转换的字节数组。</param>
// <returns>返回十六进制格式的字符串。</returns>
public static string getStringFromBytes(byte[] pByte)
{
string str = ""; //定义字符串类型临时变量。
//遍历字节数组,把每个字节转换成十六进制字符串,不足两位前面添“0”,以空格分隔
累加到字符串变量里。
for (int i = 0; i < pByte.Length; i++)
str += (pByte[i].ToString("X").PadLeft(2, '0') + " ");
str = str.TrimEnd(' '); //去掉字符串末尾的空格。
return str; //返回字符串临时变量。
}
//<summary>
//把十六进制格式的字符串转换成字节数组。
//</summary>
//<param name="pString">要转换的十六进制格式的字符串</param>
//<returns>返回字节数组。</returns>
public static byte[] getBytesFromString(string pString)
{
string[] str = pString.Split(' '); //把十六进制格式的字符串按空格转换为字符串数组。
byte[] bytes = new byte[str.Length]; //定义字节数组并初始化,长度为字符串数组的长度。
for (int i = 0; i < str.Length; i++) //遍历字符串数组,把每个字符串转换成字节类型赋值给每个字节变量。
{ bytes[i] = Convert.ToByte(Convert.ToInt32(str[i], 16)); }
return bytes; //返回字节数组。
}
//------------------------------------------------------------
//清空接收框内容
private void btnClear_Click(object sender, EventArgs e)
{
txtSend.Text = "";
txtReceive.Text = "";
txtInformation.Text = "";
txtStatus.Text = "数据已经清除!";
}
//------------------------------------------------------------
//发送命令,获取低频卡信息
//0---------1---------2---------3---------4---------5---------
//012345678901234567890123456789012345678901234567890123456789
//FF 55 00 00 01 01 00 50 74
private void btnInformation_Click(object sender, EventArgs e)
{
txtSend.Text = "";
txtReceive.Text = "";
status_num = 1;
String str1 = "FF 55 00 00 01 01 00 50 74";
byte[] data = getBytesFromString(str1);
com.Write(data, 0, data.Length);
txtSend.Text = str1;
}
//------------------------------------------------------------
//发送命令,低频卡初始化操作
//0---------1---------2---------3---------4---------5---------
//012345678901234567890123456789012345678901234567890123456789
//FF 55 00 00 02 04 00 00 87
//FF 55 00 00 82 04 01 00 EE 33
private void button1_Click(object sender, EventArgs e)
{
txtSend.Text = "";
txtReceive.Text = "";
status_num = 2;
String str1 = "FF 55 00 00 02 04 00 00 87";
byte[] data = getBytesFromString(str1);
com.Write(data, 0, data.Length);
txtSend.Text = str1;
}
public static byte[] crc16(byte[] data, int len)
{
byte[] temdata = new byte[2];
int xda, xdapoly;
byte i, j, xdabit;
xda = 0xFFFF;
xdapoly = 0xA001;
for (i = 0; i < data.Length; i++)
{
xda ^= data[i];
for (j = 0; j < 8; j++)
{
xdabit = (byte)(xda & 0x01);
xda >>= 1;
if (xdabit == 1)
xda ^= xdapoly;
}
}
temdata[0] = (byte)(xda & 0xFF);
temdata[1] = (byte)(xda >> 8);
return temdata;
}
private void button2_Click(object sender, EventArgs e)
{
txtSend.Text = "";
txtReceive.Text = "";
status_num = 3;
//-------------0---------1---------2---------3--
//-------------012345678901234567890123456789
String str1 = "FF 55 00 00 03 03 01 00 CF F1";
str1 = str1.Remove(21, 2);
str1 = str1.Insert(21, comboBox1.Text); //获取块地址
byte[] temdata1 = getBytesFromString(str1);//转换字节,准备CRC16校验
//-----------------------------------------------
int num_len1 = 6;//前2个字节FF 55不加入CRC校验
int num_len2 = 6;//后2个字节50 74是校验位
String str2 = str1.Substring(num_len1, str1.Length - num_len1 - num_len2);
byte[] temdata2 = getBytesFromString(str2);
byte[] temdata3 = crc16(temdata2, temdata2.Length);//CRC校验
temdata1[temdata1.Length - 2] = temdata3[1];//填充校验位
temdata1[temdata1.Length - 1] = temdata3[0];
//-----------------------------------------------
com.Write(temdata1, 0, temdata1.Length); //发到串口
txtSend.Text = getStringFromBytes(temdata1);//显示
}
private void button3_Click(object sender, EventArgs e)
{
txtSend.Text = "";
txtReceive.Text = "";
status_num = 4;
String str1 = "FF 55 00 00 03 04 05 01 00 00 00 00 47 48";
str1 = str1.Remove(21, 2);
str1 = str1.Insert(21, comboBox2.Text); //获取块地址
str1 = str1.Remove(24, 11);
str1 = str1.Insert(24, textBox1.Text); //获取写入的数据内容
byte[] temdata1 = getBytesFromString(str1);//转换字节,准备CRC16校验
//-----------------------------------------------
int num_len1 = 6;//前2个字节FF 55不加入CRC校验
int num_len2 = 6;//后2个字节50 74是校验位
String str2 = str1.Substring(num_len1, str1.Length - num_len1 - num_len2);
byte[] temdata2 = getBytesFromString(str2);
byte[] temdata3 = crc16(temdata2, temdata2.Length);//CRC校验
temdata1[temdata1.Length - 2] = temdata3[1];//填充校验位
temdata1[temdata1.Length - 1] = temdata3[0];
//-----------------------------------------------
com.Write(temdata1, 0, temdata1.Length); //发到串口
txtSend.Text = getStringFromBytes(temdata1);//显示
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
}
private void button4_Click(object sender, EventArgs e)
{
txtSend.Text = "";
txtReceive.Text = "";
status_num = 5;
String str1 = "FF 55 00 00 03 04 05 01 00 00 00 00 47 48";
str1 = str1.Remove(21, 2);
str1 = str1.Insert(21, comboBox3.Text); //获取块地址
str1 = str1.Remove(24, 11);
str1 = str1.Insert(24, textBox4.Text); //获取写入的数据内容
byte[] temdata1 = getBytesFromString(str1);//转换字节,准备CRC16校验
//-----------------------------------------------
int num_len1 = 6;//前2个字节FF 55不加入CRC校验
int num_len2 = 6;//后2个字节50 74是校验位
String str2 = str1.Substring(num_len1, str1.Length - num_len1 - num_len2);
byte[] temdata2 = getBytesFromString(str2);
byte[] temdata3 = crc16(temdata2, temdata2.Length);//CRC校验
temdata1[temdata1.Length - 2] = temdata3[1];//填充校验位
temdata1[temdata1.Length - 1] = temdata3[0];
//-----------------------------------------------
com.Write(temdata1, 0, temdata1.Length); //发到串口
txtSend.Text = getStringFromBytes(temdata1);//显示
}
private void button5_Click(object sender, EventArgs e)
{
txtSend.Text = "";
txtReceive.Text = "";
status_num = 2;
String str1 = "FF 55 00 00 02 04 00 00 87";
byte[] data = getBytesFromString(str1);
com.Write(data, 0, data.Length);
txtSend.Text = str1;
textBox5.Text = str1;
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
}
private void txtReceive_TextChanged(object sender, EventArgs e)
{
}
private void tabPage3_Click(object sender, EventArgs e)
{
}
private void button7_Click(object sender, EventArgs e)
{
status_num = 7;
timer1.Start();
textBox10.Text = star.ToString();
String str1 = "FF 55 00 00 02 04 00 00 87";
byte[] data = getBytesFromString(str1);
com.Write(data, 0, data.Length);
textBox14.Text = str1;
}
private void button8_Click(object sender, EventArgs e)
{
status_num = 8;
timer1.Stop();
textBox12.Text = stop.ToString();
}
private void button7_Click_1(object sender, EventArgs e)
{
}
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void button6_Click(object sender, EventArgs e)
{
txtSend.Text = "";
txtReceive.Text = "";
status_num = 6;
//-------------0---------1---------2---------3--
//-------------012345678901234567890123456789
String str1 = "FF 55 00 00 03 03 01 00 CF F1";
str1 = str1.Remove(21, 2);
str1 = str1.Insert(21, comboBox1.Text); //获取块地址
byte[] temdata1 = getBytesFromString(str1);//转换字节,准备CRC16校验
//-----------------------------------------------
int num_len1 = 6;//前2个字节FF 55不加入CRC校验
int num_len2 = 6;//后2个字节50 74是校验位
String str2 = str1.Substring(num_len1, str1.Length - num_len1 - num_len2);
byte[] temdata2 = getBytesFromString(str2);
byte[] temdata3 = crc16(temdata2, temdata2.Length);//CRC校验
temdata1[temdata1.Length - 2] = temdata3[1];//填充校验位
temdata1[temdata1.Length - 1] = temdata3[0];
//-----------------------------------------------
com.Write(temdata1, 0, temdata1.Length); //发到串口
txtSend.Text = getStringFromBytes(temdata1);//显示
}
}
}
五、调试与测试结果
测试结果如下:
用非注册卡比对
六、应用效果与未来展望
基于RFID技术的门禁系统在实际应用中取得了良好的效果。系统能够实现对人员的身份识别和进出控制,有效防止了非法入侵和破坏。同时,系统还具有方便快捷的特点,减少了人员的等待时间,提高了工作效率。随着物联网技术的不断发展,RFID门禁系统也将不断升级和完善,如提高识别精度和速度、引入生物识别技术、加强系统集成和智能化等。
七、总结
基于RFID技术的门禁系统以其高效、安全、便捷的特点,在门禁领域得到了广泛应用。通过合理的硬件设计和软件设计,以及系统集成与调试,可以实现门禁系统的高效运行和稳定性能。随着技术的不断发展,RFID门禁系统将在未来发挥更大的作用。